dolibarr 25.0.0-alpha
html.form.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
7 * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9 * Copyright (C) 2006 Marc Barilley/Ocebo <marc@ocebo.com>
10 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerker@telenet.be>
11 * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
12 * Copyright (C) 2010 Juanjo Menent <jmenent@2byte.es>
13 * Copyright (C) 2010-2021 Philippe Grand <philippe.grand@atoo-net.com>
14 * Copyright (C) 2011 Herve Prot <herve.prot@symeos.com>
15 * Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
16 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
17 * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
18 * Copyright (C) 2014-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
19 * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
20 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
21 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
22 * Copyright (C) 2018 Christophe Battarel <christophe@altairis.fr>
23 * Copyright (C) 2018 Josep Lluis Amador <joseplluis@lliuretic.cat>
24 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
25 * Copyright (C) 2023 Nick Fragoulis
26 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
27 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
28 * Copyright (C) 2026 Lenin Rivas <lenin.rivas777@gmail.com>
29 * Copyright (C) 2026 Open-Dsi <support@open-dsi.fr>
30 * Copyright (C) 2026 Jose MARTINEZ <jose.martinez@pichinov.com>
31 *
32 * This program is free software; you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation; either version 3 of the License, or
35 * (at your option) any later version.
36 *
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 *
42 * You should have received a copy of the GNU General Public License
43 * along with this program. If not, see <https://www.gnu.org/licenses/>.
44 */
45
59class Form
60{
64 public $db;
65
69 public $error = '';
70
74 public $errors = array();
75
76 // Some properties used to return data by some methods
78 public $result;
79
81 public $num;
82
83 // Cache arrays
85 public $cache_types_paiements = array();
87 public $cache_conditions_paiements = array();
89 public $cache_transport_mode = array();
91 public $cache_availability = array();
93 public $cache_demand_reason = array();
95 public $cache_types_fees = array();
97 public $cache_vatrates = array();
99 public $cache_invoice_subtype = array();
101 public $cache_rule_for_lines_dates = array();
102
106 private $phoneInputSharedJsLoaded = false;
107
108
114 public function __construct($db)
115 {
116 $this->db = $db;
117 }
118
127 public function getDurationTypes(Translate $langs, $plurial = true, $reverse = false)
128 {
129 if ($plurial) {
130 $arrayoftypes = [
131 'y' => $langs->trans('Years'),
132 'm' => $langs->trans('Month'),
133 'w' => $langs->trans('Weeks'),
134 'd' => $langs->trans('Days'),
135 'h' => $langs->trans('Hours'),
136 'i' => $langs->trans('Minutes'),
137 's' => $langs->trans('Seconds'),
138 ];
139 } else {
140 $arrayoftypes = [
141 "y" => $langs->trans("Year"),
142 "m" => $langs->trans("Month"),
143 "w" => $langs->trans("Week"),
144 "d" => $langs->trans("Day"),
145 "h" => $langs->trans("Hour"),
146 "i" => $langs->trans("Minute"),
147 's' => $langs->trans('Second'),
148 ];
149 }
150 if ($reverse) {
151 return array_reverse($arrayoftypes);
152 } else {
153 return $arrayoftypes;
154 }
155 }
156
173 public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
174 {
175 global $langs;
176
177 $ret = '';
178
179 // TODO change for compatibility
180 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;/', $typeofdata)) {
181 if ($perm) {
182 $tmp = explode(':', $typeofdata);
183 $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
184 if ($fieldrequired) {
185 $ret .= '<span class="fieldrequired">';
186 }
187 if ($help) {
188 $ret .= $this->textwithpicto($langs->trans($text), $help);
189 } else {
190 $ret .= $langs->trans($text);
191 }
192 if ($fieldrequired) {
193 $ret .= '</span>';
194 }
195 $ret .= '</div>' . "\n";
196 } else {
197 if ($fieldrequired) {
198 $ret .= '<span class="fieldrequired">';
199 }
200 if ($help) {
201 $ret .= $this->textwithpicto($langs->trans($text), $help);
202 } else {
203 $ret .= $langs->trans($text);
204 }
205 if ($fieldrequired) {
206 $ret .= '</span>';
207 }
208 }
209 } else {
210 if (empty($notabletag) && $perm) {
211 $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
212 }
213 if ($fieldrequired) {
214 $ret .= '<span class="fieldrequired">';
215 }
216 if ($help) {
217 $ret .= $this->textwithpicto($langs->trans($text), $help);
218 } else {
219 $ret .= $langs->trans($text);
220 }
221 if ($fieldrequired) {
222 $ret .= '</span>';
223 }
224 if (!empty($notabletag)) {
225 $ret .= ' ';
226 }
227 if (empty($notabletag) && $perm) {
228 $ret .= '</td>';
229 }
230 if (empty($notabletag) && $perm) {
231 $ret .= '<td class="right">';
232 }
233 if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm && is_object($object)) {
234 $ret .= '<a class="editfielda reposition" href="' . dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'edit' . $htmlname, $paramid => $object->id], true) . $moreparam . '">';
235 $ret .= img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1));
236 $ret .= '</a>';
237 }
238 if (!empty($notabletag) && $notabletag == 1) {
239 if ($text) {
240 $ret .= ' : ';
241 } else {
242 $ret .= ' ';
243 }
244 }
245 if (!empty($notabletag) && $notabletag == 3) {
246 $ret .= ' ';
247 }
248 if (empty($notabletag) && $perm) {
249 $ret .= '</td>';
250 }
251 if (empty($notabletag) && $perm) {
252 $ret .= '</tr></table>';
253 }
254 }
255
256 return $ret;
257 }
258
282 public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '')
283 {
284 global $conf, $langs;
285
286 $ret = '';
287
288 // Check parameters
289 if (empty($typeofdata)) {
290 return 'ErrorBadParameter typeofdata is empty';
291 }
292 // Clean parameter $typeofdata
293 if ($typeofdata == 'datetime') {
294 $typeofdata = 'dayhour';
295 }
296 $reg = array();
297 if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
298 if ($reg[1] == 'varchar') {
299 $typeofdata = 'string';
300 } elseif ($reg[1] == 'int') {
301 $typeofdata = 'numeric';
302 } else {
303 return 'ErrorBadParameter ' . $typeofdata;
304 }
305 }
306
307 // When option to edit inline is activated
308 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
309 $ret .= $this->editInPlace($object, $value, $htmlname, ($perm ? 1 : 0), $typeofdata, $editvalue, $extObject, $custommsg);
310 } else {
311 if ($editaction == '') {
312 $editaction = GETPOST('action', 'aZ09');
313 }
314 $editmode = ($editaction == 'edit' . $htmlname);
315 if ($editmode) { // edit mode
316 $ret .= "<!-- formeditfieldval -->\n";
317 $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
318 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
319 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
320 $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
321 if (empty($notabletag)) {
322 $ret .= '<table class="nobordernopadding centpercent">';
323 }
324 if (empty($notabletag)) {
325 $ret .= '<tr><td>';
326 }
327 if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
328 $tmp = explode(':', $typeofdata);
329 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus spellcheck="false">';
330 } elseif (preg_match('/^(integer)/', $typeofdata)) {
331 $tmp = explode(':', $typeofdata);
332 $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
333 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
334 } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
335 $tmp = explode(':', $typeofdata);
336 $valuetoshow = price2num($editvalue ? $editvalue : $value);
337 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
338 } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
339 $tmp = explode(':', $typeofdata);
340 $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
341 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
342 $tmp = explode(':', $typeofdata);
343 $cols = (empty($tmp[2]) ? '' : $tmp[2]);
344 $morealt = '';
345 if (preg_match('/%/', $cols)) {
346 $morealt = ' style="width: ' . $cols . '"';
347 $cols = '';
348 }
349 $valuetoshow = ($editvalue ? $editvalue : $value);
350 $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
351 // textarea convert automatically entities chars into simple chars.
352 // 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.
353 $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
354 $ret .= dol_htmlwithnojs(dol_string_neverthesehtmltags($valuetoshow, array('textarea')));
355 $ret .= '</textarea><div class="clearboth"></div>';
356 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
357 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
358 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
359 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
360 $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
361 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
362 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
363 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
364 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
365 $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
366 } elseif (preg_match('/^select;/', $typeofdata)) {
367 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
368 $arraylist = array();
369 foreach ($arraydata as $val) {
370 $tmp = explode(':', $val);
371 $tmpkey = str_replace('|', ':', $tmp[0]);
372 $arraylist[$tmpkey] = $tmp[1];
373 }
374 $ret .= $this->selectarray($htmlname, $arraylist, $value);
375 } elseif (preg_match('/^link/', $typeofdata)) {
376 // TODO Not yet implemented. See code for extrafields
377 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
378 $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
379 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
380 $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]));
381 $ret .= $doleditor->Create(1);
382 } elseif ($typeofdata == 'asis') {
383 $ret .= ($editvalue ? $editvalue : $value);
384 }
385 if (empty($notabletag)) {
386 $ret .= '</td>';
387 }
388
389 // Button save-cancel
390 if (empty($notabletag)) {
391 $ret .= '<td>';
392 }
393 //else $ret.='<div class="clearboth"></div>';
394 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Save") . '">';
395 if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
396 $ret .= '<br>' . "\n";
397 }
398 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
399 if (empty($notabletag)) {
400 $ret .= '</td>';
401 }
402
403 if (empty($notabletag)) {
404 $ret .= '</tr></table>' . "\n";
405 }
406 $ret .= '</form>' . "\n";
407 } else { // view mode
408 if (preg_match('/^email/', $typeofdata)) {
409 $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
410 } elseif (preg_match('/^phone/', $typeofdata)) {
411 $ret .= dol_print_phone($value, '_blank', 32, 1);
412 } elseif (preg_match('/^url/', $typeofdata)) {
413 $ret .= dol_print_url($value, '_blank', 32, 1);
414 } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
415 $ret .= ($value != '' ? price($value, 0, $langs, 0, -1, -1, $conf->currency) : '');
416 } elseif (preg_match('/^checkbox/', $typeofdata)) {
417 $tmp = explode(':', $typeofdata);
418 $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
419 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
421 } elseif (preg_match('/^(safehtmlstring|restricthtml)/', $typeofdata)) { // 'restricthtml' is not an allowed type for editfieldval. Value is 'safehtmlstring'
423 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
424 $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
425 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
426 $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
427 } elseif (preg_match('/^select;/', $typeofdata)) {
428 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
429 $arraylist = array();
430 foreach ($arraydata as $val) {
431 $tmp = explode(':', $val);
432 $arraylist[$tmp[0]] = $tmp[1];
433 }
434 $ret .= $arraylist[$value];
435 if ($htmlname == 'fk_product_type') {
436 if ($value == 0) {
437 $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
438 } else {
439 $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
440 }
441 }
442 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
443 $tmpcontent = dol_htmlentitiesbr($value);
444 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
445 $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
446 $firstline = preg_replace('/[\n\r].*/', '', $firstline);
447 $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
448 }
449 // We don't use dol_escape_htmltag to get the html formatting active, but this need we must also
450 // clean data from some dangerous html
452 } else {
453 if (empty($moreoptions['valuealreadyhtmlescaped'])) {
454 $ret .= dol_escape_htmltag($value);
455 } else {
456 $ret .= $value; // $value must be already html escaped.
457 }
458 }
459
460 // Custom format if parameter $formatfunc has been provided
461 if ($formatfunc && method_exists($object, $formatfunc)) {
462 $ret = $object->$formatfunc($ret);
463 }
464 }
465 }
466 return $ret;
467 }
468
480 public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
481 {
482 global $conf, $langs, $extralanguages;
483
484 $result = '';
485
486 // List of extra languages
487 $arrayoflangcode = array();
488 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
489 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
490 }
491
492 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
493 if (!is_object($extralanguages)) {
494 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
495 $extralanguages = new ExtraLanguages($this->db);
496 }
497 $extralanguages->fetch_name_extralanguages('societe');
498
499 // ExtraLanguages::fetch_name_extralanguages() leaves $this->attributes empty
500 // when MAIN_USE_ALTERNATE_TRANSLATION_FOR is not configured, so PHP 8 raises
501 // 'Undefined array key' on the read below if we do not guard it (issue #34596).
502 if (empty($extralanguages->attributes[$object->element]) || !is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
503 return ''; // No extralang field to show
504 }
505
506 $result .= '<!-- Widget for translation -->' . "\n";
507 $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
508 $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', 0, 0, 0, '', 'fa-15 editfieldlang');
509 $result .= $s;
510 $result .= '</div>';
511
512 $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
513
514 $resultforextrlang = '';
515 foreach ($arrayoflangcode as $langcode) {
516 $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
517 if (empty($valuetoshow)) {
518 $object->fetchValuesForExtraLanguages();
519 //var_dump($object->array_languages);
520 $valuetoshow = $object->array_languages[$fieldname][$langcode];
521 }
522
523 $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
524 $resultforextrlang .= $s;
525
526 // TODO Use the showInputField() method of ExtraLanguages object
527 if ($typeofdata == 'textarea') {
528 $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
529 $resultforextrlang .= $valuetoshow;
530 $resultforextrlang .= '</textarea>';
531 } else {
532 $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
533 }
534 }
535 $result .= $resultforextrlang;
536
537 $result .= '</div>';
538 $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
539 }
540
541 return $result;
542 }
543
557 protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
558 {
559 $out = '';
560
561 // Check parameters
562 if (preg_match('/^text/', $inputType)) {
563 $value = dol_nl2br($value);
564 } elseif (preg_match('/^numeric/', $inputType)) {
565 $value = price($value);
566 } elseif ($inputType == 'day' || $inputType == 'datepicker') {
567 $value = dol_print_date($value, 'day');
568 }
569
570 if ($condition) {
571 $element = false;
572 $table_element = false;
573 $fk_element = false;
574 $loadmethod = false;
575 $savemethod = false;
576 $ext_element = false;
577 $button_only = false;
578 $inputOption = '';
579 $rows = '';
580 $cols = '';
581
582 if (is_object($object)) {
583 $element = $object->element;
584 $table_element = $object->table_element;
585 $fk_element = $object->id;
586 }
587
588 if (is_object($extObject)) {
589 $ext_element = $extObject->element;
590 }
591
592 if (preg_match('/^(string|email|numeric)/', $inputType)) {
593 $tmp = explode(':', $inputType);
594 $inputType = $tmp[0];
595 if (!empty($tmp[1])) {
596 $inputOption = $tmp[1];
597 }
598 if (!empty($tmp[2])) {
599 $savemethod = $tmp[2];
600 }
601 $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
602 } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
603 $tmp = explode(':', $inputType);
604 $inputType = $tmp[0];
605 if (!empty($tmp[1])) {
606 $inputOption = $tmp[1];
607 }
608 if (!empty($tmp[2])) {
609 $savemethod = $tmp[2];
610 }
611
612 $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
613 } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
614 $tmp = explode(':', $inputType);
615 $inputType = $tmp[0];
616 $loadmethod = $tmp[1];
617 if (!empty($tmp[2])) {
618 $savemethod = $tmp[2];
619 }
620 if (!empty($tmp[3])) {
621 $button_only = true;
622 }
623 } elseif (preg_match('/^textarea/', $inputType)) {
624 $tmp = explode(':', $inputType);
625 $inputType = $tmp[0];
626 $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
627 $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
628 } elseif (preg_match('/^ckeditor/', $inputType)) {
629 $tmp = explode(':', $inputType);
630 $inputType = $tmp[0];
631 $toolbar = $tmp[1];
632 if (!empty($tmp[2])) {
633 $width = $tmp[2];
634 }
635 if (!empty($tmp[3])) {
636 $height = $tmp[3];
637 }
638 if (!empty($tmp[4])) {
639 $savemethod = $tmp[4];
640 }
641
642 if (isModEnabled('fckeditor')) {
643 $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
644 } else {
645 $inputType = 'textarea';
646 }
647 }
648
649 $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
650 $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
651 $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
652 $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
653 if (!empty($savemethod)) {
654 $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
655 }
656 if (!empty($ext_element)) {
657 $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
658 }
659 if (!empty($custommsg)) {
660 if (is_array($custommsg)) {
661 if (!empty($custommsg['success'])) {
662 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
663 }
664 if (!empty($custommsg['error'])) {
665 $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
666 }
667 } else {
668 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
669 }
670 }
671 if ($inputType == 'textarea') {
672 $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
673 $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
674 }
675 $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
676 $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
677 } else {
678 $out = $value;
679 }
680
681 return $out;
682 }
683
702 public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
703 {
704 if ($incbefore) {
705 $text = $incbefore . $text;
706 }
707 if (!$htmltext) {
708 return $text;
709 }
710 $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
711
712 $tag = 'td';
713 if ($notabs == 2) {
714 $tag = 'div';
715 }
716 if ($notabs == 3) {
717 $tag = 'span';
718 }
719 // Sanitize tooltip
720 $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
721
722 $extrastyle = '';
723 if ($direction < 0) {
724 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
725 $extrastyle = 'padding: 0px; padding-left: 2px;';
726 }
727 if ($direction > 0) {
728 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
729 $extrastyle = 'padding: 0px; padding-right: 2px;';
730 }
731
732 $classfortooltip = 'classfortooltip';
733
734 $s = '';
735 $textfordialog = '';
736
737 if ($tooltiptrigger == '') {
738 $htmltext = str_replace('"', '&quot;', $htmltext);
739 } else {
740 $classfortooltip = 'classfortooltiponclick';
741 $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext"';
742 // Set default title of dialog
743 global $langs;
744 if ($langs instanceof Translate) {
745 $textfordialog .= ' title="'.$langs->trans("Note").'"';
746 }
747 $textfordialog .= '>' . $htmltext . '</div>';
748 }
749 if ($tooltipon == 2 || $tooltipon == 3) {
750 $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
751 if ($tooltiptrigger == '') {
752 $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on img tag to store tooltip
753 } else {
754 $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
755 }
756 } else {
757 $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
758 }
759 if ($tooltipon == 1 || $tooltipon == 3) {
760 $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ($tag != 'td' ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
761 if ($tooltiptrigger == '') {
762 $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on td tag to store tooltip
763 } else {
764 $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
765 }
766 } else {
767 $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
768 }
769 if (empty($notabs)) {
770 $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
771 } elseif ($notabs == 2) {
772 $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
773 }
774 // Define value if value is before
775 if ($direction < 0) {
776 $s .= '<' . $tag . $paramfortooltipimg;
777 if ($tag == 'td') {
778 $s .= ' class="valigntop" width="14"';
779 }
780 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
781 }
782 // Use another method to help avoid having a space in value in order to use this value with jquery
783 // Define label
784 if ((string) $text != '') {
785 $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
786 }
787 // Define value if value is after
788 if ($direction > 0) {
789 $s .= '<' . $tag . $paramfortooltipimg;
790 if ($tag == 'td') {
791 $s .= ' class="valignmiddle" width="14"';
792 }
793 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
794 }
795 if (empty($notabs)) {
796 $s .= '</tr></table>';
797 } elseif ($notabs == 2) {
798 $s .= '</div>';
799 }
800
801 return $s;
802 }
803
818 public function textwithpicto($text, $htmltooltip, $direction = 1, $type = 'help', $extracss = 'valignmiddle', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
819 {
820 global $conf, $langs;
821
822 //For backwards compatibility
823 if ($type == '0') {
824 $type = 'info';
825 } elseif ($type == '1') {
826 $type = 'help';
827 }
828 // Clean parameters
829 $tooltiptrigger = preg_replace('/[^a-z0-9]/i', '', $tooltiptrigger);
830
831 if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
832 $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
833 }
834 $alt = '';
835 if ($tooltiptrigger) {
836 $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
837 }
838
839 // If info or help with no javascript, show only text
840 if (empty($conf->use_javascript_ajax)) {
841 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
842 return $text;
843 } else {
844 $alt = $htmltooltip;
845 $htmltooltip = '';
846 }
847 }
848
849 // If info or help with smartphone, show only text (tooltip hover can't works)
850 if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
851 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
852 return $text;
853 }
854 }
855 // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
856 //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
857 //{
858 //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
859 //}
860
861 $img = '';
862 if ($type == 'info') {
863 $img = img_help(($tooltiptrigger != '' ? 2 : 0), $alt);
864 } elseif ($type == 'help') {
865 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
866 } elseif ($type == 'helpclickable') {
867 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
868 } elseif ($type == 'warning') {
869 $img = img_warning($alt);
870 } elseif ($type != 'none') {
871 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
872 $img = img_picto($alt, $type); // $type can be an image path
873 }
874
875 $tooltipon = ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2);
876
877 return $this->textwithtooltip($text, $htmltooltip, $tooltipon, $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
878 }
879
890 public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
891 {
892 global $conf, $langs, $hookmanager;
893
894 $disabled = 0;
895 $ret = '<div class="centpercent center">';
896 $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
897
898 // 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.
899 $parameters = array();
900 $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
901 // check if there is a mass action
902
903 if (is_array($arrayofaction) && count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
904 return;
905 }
906 if (empty($reshook)) {
907 $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
908 if (is_array($arrayofaction)) {
909 foreach ($arrayofaction as $code => $label) {
910 $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
911 }
912 }
913 }
914 $ret .= $hookmanager->resPrint;
915
916 $ret .= '</select>';
917
918 if (empty($conf->dol_optimize_smallscreen)) {
919 $ret .= ajax_combobox('.' . $name . 'select');
920 }
921
922 // 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
923 $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.
924 $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")) . '">';
925 $ret .= '</div>';
926
927 if (!empty($conf->use_javascript_ajax)) {
928 $ret .= '<!-- JS CODE TO ENABLE mass action select -->
929 <script nonce="' . getNonce() . '">
930 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 */
931 atleastoneselected=0;
932 jQuery("."+cssclass).each(function( index ) {
933 /* console.log( index + ": " + $( this ).text() ); */
934 if ($(this).is(\':checked\')) atleastoneselected++;
935 });
936
937 console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
938
939 if (atleastoneselected || ' . ((int) $alwaysvisible) . ') {
940 jQuery("."+name).show();
941 ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
942 ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
943 } else {
944 jQuery("."+name).hide();
945 jQuery("."+name+"other").hide();
946 }
947 }
948
949 jQuery(document).ready(function () {
950 initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
951 jQuery(".' . $cssclass . '").change(function() {
952 console.log("A change was done on .' . $cssclass . '");
953 initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
954 });
955 jQuery(".' . $name . 'select").change(function() {
956 var massaction = $( this ).val();
957 var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
958 if (massaction == "builddoc") {
959 urlform = urlform + "#show_files";
960 }
961 $( this ).closest("form").attr("action", urlform);
962 console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
963 /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
964 if ($(this).val() != \'0\') {
965 jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
966 jQuery(".' . $name . 'other").hide(); /* To disable if another div was open */
967 jQuery(".' . $name . '"+massaction).show();
968 } else {
969 jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
970 jQuery(".' . $name . 'other").hide(); /* To disable any div open */
971 }
972 });
973 });
974 </script>
975 ';
976 }
977
978 return $ret;
979 }
980
981 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
982
1000 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)
1001 {
1002 // phpcs:enable
1003 global $langs, $mysoc;
1004
1005 $langs->load("dict");
1006
1007 $selected = (string) $selected;
1008
1009 $out = '';
1011 $countryArray = array();
1012 $favorite = array();
1013 $label = array();
1014 $atleastonefavorite = 0;
1015
1016 $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
1017 $sql .= " FROM " . $this->db->prefix() . "c_country";
1018 $sql .= " WHERE active > 0";
1019 //$sql.= " ORDER BY code ASC";
1020
1021 dol_syslog(get_class($this) . "::select_country", LOG_DEBUG);
1022
1023 $resql = $this->db->query($sql);
1024 if ($resql) {
1025 $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
1026 $num = $this->db->num_rows($resql);
1027 $i = 0;
1028 if ($num) {
1029 while ($i < $num) {
1030 $obj = $this->db->fetch_object($resql);
1031
1032 $countryArray[$i]
1033 = array(
1034 'rowid' => (int) $obj->rowid,
1035 'code_iso' => (string) $obj->code_iso,
1036 'code_iso3' => (string) $obj->code_iso3,
1037 '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 : '')),
1038 'favorite' => (string) $obj->favorite,
1039 'eec' => (string) $obj->eec,
1040 );
1041 $favorite[$i] = $obj->favorite;
1042 $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
1043 $i++;
1044 }
1045
1046 if (empty($disablefavorites)) {
1047 $array1_sort_order = SORT_DESC;
1048 $array2_sort_order = SORT_ASC;
1049 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
1050 } else {
1051 $countryArray = dol_sort_array($countryArray, 'label');
1052 }
1053
1054 if ($showempty) {
1055 if (is_numeric($showempty)) {
1056 $out .= '<option value="">&nbsp;</option>' . "\n";
1057 } else {
1058 $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
1059 }
1060 }
1061
1062 if ($addspecialentries) { // Add dedicated entries for groups of countries
1063 //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
1064 $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1065 $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
1066 if ($mysoc->isInEEC()) {
1067 $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1068 }
1069 $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
1070 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1071 }
1072
1073 foreach ($countryArray as $row) {
1074 //if (empty($showempty) && empty($row['rowid'])) continue;
1075 if (empty($row['rowid'])) {
1076 continue;
1077 }
1078 if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1079 continue; // exclude some countries
1080 }
1081
1082 if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1083 $atleastonefavorite++;
1084 }
1085 if (empty($row['favorite']) && $atleastonefavorite) {
1086 $atleastonefavorite = 0;
1087 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1088 }
1089
1090 $labeltoshow = '';
1091 if ($row['label']) {
1092 $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1093 } else {
1094 $labeltoshow .= '&nbsp;';
1095 }
1096 if ($row['code_iso']) {
1097 $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1098 if (empty($hideflags)) {
1099 $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1100 $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1101 }
1102 }
1103
1104 if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1105 $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']) . '">';
1106 } else {
1107 $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']) . '">';
1108 }
1109 $out .= dol_string_nohtmltag($labeltoshow);
1110 $out .= '</option>' . "\n";
1111 }
1112 }
1113 $out .= '</select>';
1114 } else {
1115 dol_print_error($this->db);
1116 }
1117
1118 // Make select dynamic
1119 if (empty($forcecombo)) {
1120 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1121 $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1122 }
1123
1124 return $out;
1125 }
1126
1137 public function selectPhoneCode($selected = '', $htmlname = 'phone_code', $morecss = 'maxwidth150', $showempty = 0, $country_id_hint = 0)
1138 {
1139 global $langs;
1140
1141 $langs->load("dict");
1142
1143 $out = '';
1144 $codeArray = array();
1145 $favorite = array();
1146 $label = array();
1147 $atleastonefavorite = 0;
1148
1149 $sql = "SELECT rowid, code, label, phone_code, favorite, trunk_prefix";
1150 $sql .= " FROM ".$this->db->prefix()."c_country";
1151 $sql .= " WHERE active > 0 AND phone_code IS NOT NULL AND phone_code != ''";
1152
1153 dol_syslog(get_class($this)."::selectPhoneCode", LOG_DEBUG);
1154 $resql = $this->db->query($sql);
1155 if ($resql) {
1156 $num = $this->db->num_rows($resql);
1157 $i = 0;
1158 while ($i < $num) {
1159 $obj = $this->db->fetch_object($resql);
1160
1161 $translabel = ($obj->code && $langs->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langs->transnoentitiesnoconv("Country".$obj->code) : $obj->label;
1162
1163 $codeArray[$i]['rowid'] = $obj->rowid;
1164 $codeArray[$i]['code'] = $obj->code;
1165 $codeArray[$i]['label'] = $translabel;
1166 $codeArray[$i]['phone_code'] = '+'.$obj->phone_code;
1167 $codeArray[$i]['favorite'] = $obj->favorite;
1168 $codeArray[$i]['trunk_prefix'] = $obj->trunk_prefix;
1169 $favorite[$i] = $obj->favorite;
1170 $label[$i] = dol_string_unaccent($translabel);
1171 $i++;
1172 }
1173
1174 $array1_sort_order = SORT_DESC;
1175 $array2_sort_order = SORT_ASC;
1176 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $codeArray);
1177
1178 $out .= '<select id="select'.$htmlname.'" class="flat selectphonecode'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1179
1180 if ($showempty) {
1181 $out .= '<option value="">&nbsp;</option>'."\n";
1182 }
1183
1184 // Determine which row index to select: prefer country_id_hint match, fallback to first phone_code match
1185 $selectedIdx = -1;
1186 $firstMatchIdx = -1;
1187 if ($selected !== '') {
1188 foreach ($codeArray as $idx => $row) {
1189 if ($row['phone_code'] == $selected) {
1190 if ($firstMatchIdx < 0) {
1191 $firstMatchIdx = $idx;
1192 }
1193 if ($country_id_hint > 0 && $row['rowid'] == $country_id_hint) {
1194 $selectedIdx = $idx;
1195 break;
1196 }
1197 }
1198 }
1199 if ($selectedIdx < 0 && $firstMatchIdx >= 0) {
1200 $selectedIdx = $firstMatchIdx;
1201 }
1202 }
1203
1204 foreach ($codeArray as $idx => $row) {
1205 if (empty($row['code'])) {
1206 continue;
1207 }
1208
1209 if ($row['favorite']) {
1210 $atleastonefavorite++;
1211 }
1212 if (empty($row['favorite']) && $atleastonefavorite) {
1213 $atleastonefavorite = 0;
1214 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1215 }
1216
1217 $tmpflag = picto_from_langcode($row['code'], 'class="saturatemedium paddingrightonly"', 1);
1218
1219 // Short label for selected display: flag + country code
1220 $selectlabel = ($tmpflag ? $tmpflag.' ' : '').$row['code'];
1221
1222 // Detailed label for dropdown list: flag + country name + phone code
1223 $labeltoshow = ($tmpflag ? $tmpflag.' ' : '').$row['label'].' '.$row['phone_code'];
1224
1225 $out .= '<option value="'.dol_escape_htmltag($row['phone_code']).'"';
1226 if ($idx === $selectedIdx) {
1227 $out .= ' selected';
1228 }
1229 $out .= ' data-html="'.dol_escape_htmltag($labeltoshow).'"';
1230 $out .= ' data-select-html="'.dol_escape_htmltag($selectlabel).'"';
1231 $out .= ' data-country-id="'.((int) $row['rowid']).'"';
1232 $out .= ' data-trunk-prefix="'.dol_escape_htmltag((string) $row['trunk_prefix']).'"';
1233 $out .= '>';
1234 $out .= dol_string_nohtmltag($labeltoshow);
1235 $out .= '</option>'."\n";
1236 }
1237 $out .= '</select>';
1238 } else {
1239 dol_print_error($this->db);
1240 }
1241
1242 // Make select dynamic
1243 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1244 $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1245
1246 return $out;
1247 }
1248
1265 public function showPhoneInput($phoneValue, $htmlname, $country_id_hint = 0, $picto = 'object_phoning', $morecss = 'maxwidth150', $maxlength = 0, $countrySelectorId = 'selectcountry_id')
1266 {
1267 global $mysoc;
1268
1269 include_once DOL_DOCUMENT_ROOT.'/core/lib/phone.lib.php';
1270
1271 $codename = $htmlname.'_code';
1272
1273 // Fallback country_id: use caller hint, else main company country
1274 if (empty($country_id_hint) && !empty($mysoc->country_id)) {
1275 $country_id_hint = $mysoc->country_id;
1276 }
1277
1278 // On POST re-display, read the hidden field (which contains the full phone string)
1279 if (GETPOSTISSET($htmlname)) {
1280 $fullPhone = (string) GETPOST($htmlname);
1281 } else {
1282 $fullPhone = (string) $phoneValue;
1283 }
1284
1285 // Split into code + number
1286 $parsed = dol_parse_phone($fullPhone);
1287
1288 // Resolve default phone code: parsed code if set, else from country hint
1289 $phonecode = !empty($parsed['code']) ? $parsed['code'] : dol_get_phone_code_from_country($this->db, $country_id_hint);
1290
1291 $selectedCode = $phonecode;
1292 $numberValue = $parsed['number'];
1293
1294 // Add back trunk prefix for display (e.g. "644986885" → "0644986885" for France)
1295 if ($numberValue !== '' && $selectedCode !== '') {
1296 $trunkPrefix = dol_get_trunk_prefix($this->db, $selectedCode);
1297 if ($trunkPrefix !== '' && strpos($numberValue, $trunkPrefix) !== 0) {
1298 $numberValue = $trunkPrefix.$numberValue;
1299 }
1300 }
1301
1302 // Build output: hidden field (POSTed value)
1303 $out = '<input type="hidden" name="'.dol_escape_htmltag($htmlname).'" id="'.dol_escape_htmltag($htmlname).'" value="'.dol_escape_htmltag($fullPhone).'">';
1304
1305 // Picto
1306 $out .= img_picto('', $picto, 'class="pictofixedwidth"');
1307
1308 // Phone code select (display-only name, not submitted as separate POST param)
1309 $out .= $this->selectPhoneCode($selectedCode, $codename, 'maxwidth75 phone_code_select', 0, $country_id_hint);
1310
1311 // Visible number input (no name — not POSTed)
1312 $out .= '<input type="tel" inputmode="numeric" pattern="[0-9]*" id="'.dol_escape_htmltag($htmlname).'_input" class="'.dol_escape_htmltag($morecss).'"';
1313 if ($maxlength > 0) {
1314 $out .= ' maxlength="'.$maxlength.'"';
1315 }
1316 $out .= ' value="'.dol_escape_htmltag($numberValue).'">';
1317
1318 // Per-field JS to sync hidden field
1319 $out .= $this->getPhoneInputFieldJs($htmlname, $codename);
1320
1321 // Shared JS for country-sync (output once per page)
1322 $out .= $this->getPhoneInputSharedJs($countrySelectorId);
1323
1324 return $out;
1325 }
1326
1337 private function getPhoneInputFieldJs($htmlname, $codename)
1338 {
1339 $hiddenId = dol_escape_js($htmlname);
1340 $inputId = dol_escape_js($htmlname).'_input';
1341 $selectId = 'select'.dol_escape_js($codename);
1342
1343 $out = "\n".'<script type="text/javascript">'."\n";
1344 $out .= 'jQuery(document).ready(function() {'."\n";
1345 $out .= ' function syncPhoneField_'.$hiddenId.'() {'."\n";
1346 $out .= ' var selectEl = jQuery("#'.$selectId.'");'."\n";
1347 $out .= ' var code = selectEl.val() || "";'."\n";
1348 $out .= ' var number = (jQuery("#'.$inputId.'").val() || "").replace(/[^0-9]/g, "");'."\n";
1349 $out .= ' if (code && number) {'."\n";
1350 $out .= ' var selOpt = selectEl[0] && selectEl[0].selectedOptions && selectEl[0].selectedOptions[0];'."\n";
1351 $out .= ' var trunkPrefix = selOpt ? (selOpt.getAttribute("data-trunk-prefix") || "") : "";'."\n";
1352 $out .= ' if (trunkPrefix !== "" && number.indexOf(trunkPrefix) === 0) {'."\n";
1353 $out .= ' number = number.substring(trunkPrefix.length);'."\n";
1354 $out .= ' }'."\n";
1355 $out .= ' jQuery("#'.$hiddenId.'").val(code + " " + number);'."\n";
1356 $out .= ' } else if (number) {'."\n";
1357 $out .= ' jQuery("#'.$hiddenId.'").val(number);'."\n";
1358 $out .= ' } else {'."\n";
1359 $out .= ' jQuery("#'.$hiddenId.'").val("");'."\n";
1360 $out .= ' }'."\n";
1361 $out .= ' }'."\n";
1362 $out .= ' jQuery("#'.$selectId.'").on("change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1363 $out .= ' jQuery("#'.$inputId.'").on("input change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1364 $out .= '});'."\n";
1365 $out .= '</script>'."\n";
1366
1367 return $out;
1368 }
1369
1379 private function getPhoneInputSharedJs($countrySelectorId)
1380 {
1381 if ($this->phoneInputSharedJsLoaded) {
1382 return '';
1383 }
1384 $this->phoneInputSharedJsLoaded = true;
1385
1386 $out = "\n".'<script type="text/javascript">'."\n";
1387 $out .= 'jQuery(document).ready(function() {'."\n";
1388 $out .= ' jQuery("#'.dol_escape_js($countrySelectorId).'").on("change", function() {'."\n";
1389 $out .= ' var country_id = jQuery(this).val();'."\n";
1390 $out .= ' if (country_id) {'."\n";
1391 $out .= ' jQuery.getJSON("'.DOL_URL_ROOT.'/core/ajax/getphonecode.php", {country_id: country_id, token: "'.currentToken().'"}, function(data) {'."\n";
1392 $out .= ' if (data.phone_code) {'."\n";
1393 $out .= ' jQuery(".phone_code_select").each(function() {'."\n";
1394 $out .= ' jQuery(this).val(data.phone_code).trigger("change");'."\n";
1395 $out .= ' });'."\n";
1396 $out .= ' }'."\n";
1397 $out .= ' });'."\n";
1398 $out .= ' }'."\n";
1399 $out .= ' });'."\n";
1400 $out .= '});'."\n";
1401 $out .= '</script>'."\n";
1402
1403 return $out;
1404 }
1405
1419 private function makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
1420 {
1421 dol_syslog(__METHOD__, LOG_DEBUG);
1422 global $langs, $form;
1423 if (empty($form)) {
1424 $form = new Form($this->db);
1425 }
1426 $htmltoenteralink = '';
1427 $i = 0;
1428
1429 // headers
1430 $htmltoenteralink .= '<tr class="liste_titre">';
1431 $htmltoenteralink .= '<td class="nowrap"></td>';
1432 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1433 $htmltoenteralink .= '<td>' . $langs->trans("RefCustomer") . '</td>';
1434 $htmltoenteralink .= '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
1435 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1436 $htmltoenteralink .= '</tr>';
1437
1438 // rows with data
1439 while ($i < $num) {
1440 $objp = $this->db->fetch_object($resqllist);
1441 $alreadylinked = false;
1442 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1443 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1444 $alreadylinked = true;
1445 }
1446 }
1447 $htmltoenteralink .= '<tr class="oddeven">';
1448 $htmltoenteralink .= '<td>';
1449 if ($alreadylinked) {
1450 $htmltoenteralink .= img_picto('', 'link');
1451 } else {
1452 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1453 }
1454 $htmltoenteralink .= '</td>';
1455 $htmltoenteralink .= '<td>';
1456 if (!$alreadylinked) {
1457 $htmltoenteralink .= '<label for="' . $key . '_' . $objp->rowid . '">';
1458 }
1459 $htmltoenteralink .= $objp->ref;
1460 if (!$alreadylinked) {
1461 $htmltoenteralink .= '</label>';
1462 }
1463 $htmltoenteralink .= '</td>';
1464 $htmltoenteralink .= '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
1465 $htmltoenteralink .= '<td class="right">';
1466 if ($possiblelink['label'] == 'LinkToContract') {
1467 $htmltoenteralink .= $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
1468 }
1469 $htmltoenteralink .= '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
1470 $htmltoenteralink .= '</td>';
1471 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1472 $htmltoenteralink .= '</tr>';
1473 $i++;
1474 }
1475
1476 return $htmltoenteralink;
1477 }
1478
1493 private function makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
1494 {
1495 dol_syslog(__METHOD__, LOG_DEBUG);
1496 global $langs, $form;
1497 require_once DOL_DOCUMENT_ROOT . '/eventorganization/class/conferenceorboothattendee.class.php';
1498 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1499 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
1500 $attendeestatic = new ConferenceOrBoothAttendee($this->db);
1501 $companystatic = new Societe($this->db);
1502 $projectstatic = new Project($this->db);
1503 if (empty($form)) {
1504 $form = new Form($this->db);
1505 }
1506 $htmltoenteralink = '';
1507 $i = 0;
1508
1509 // headers
1510 $htmltoenteralink .= '<tr class="liste_titre">';
1511 $htmltoenteralink .= '<td class="nowrap"></td>';
1512 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1513 $htmltoenteralink .= '<td>' . $langs->trans("Name") . '</td>';
1514 $htmltoenteralink .= '<td>' . $langs->trans("Email") . '</td>';
1515 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1516 $htmltoenteralink .= '<td>' . $langs->trans("Project") . '</td>';
1517 $htmltoenteralink .= '<td>' . $langs->trans("DateOfRegistration") . '</td>';
1518 $htmltoenteralink .= '</tr>';
1519
1520 // rows with data
1521 while ($i < $num) {
1522 $objp = $this->db->fetch_object($resqllist);
1523 $alreadylinked = false;
1524 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1525 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1526 $alreadylinked = true;
1527 }
1528 }
1529 $htmltoenteralink .= '<tr class="oddeven">';
1530 $htmltoenteralink .= '<td>';
1531 if ($alreadylinked) {
1532 $htmltoenteralink .= img_picto('', 'link');
1533 } else {
1534 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1535 }
1536 $htmltoenteralink .= '</td>';
1537 $fetchattendee = $attendeestatic->fetch($objp->rowid);
1538 if ($fetchattendee) {
1539 $htmltoenteralink .= '<td>' . $attendeestatic->getNomUrl(0). '</td>';
1540 } else {
1541 $htmltoenteralink .= '<td><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
1542 }
1543 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1544 $htmltoenteralink .= '<td>' . $objp->email . '</td>';
1545 $fetchcompany = $companystatic->fetch($objp->socid);
1546 if ($fetchcompany) {
1547 $htmltoenteralink .= '<td>' . $companystatic->getNomUrl(0). '</td>';
1548 } else {
1549 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1550 }
1551 $fetchcproject = $projectstatic->fetch($objp->fk_project);
1552 if ($fetchcproject) {
1553 $htmltoenteralink .= '<td>' . $projectstatic->getNomUrl(0). '</td>';
1554 } else {
1555 $htmltoenteralink .= '<td>' . $objp->fk_project . '</td>';
1556 }
1557 $htmltoenteralink .= '<td>' . $objp->date_subscription . '</td>';
1558 $htmltoenteralink .= '</tr>';
1559 $i++;
1560 }
1561
1562 return $htmltoenteralink;
1563 }
1564
1565 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1566
1580 public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1581 {
1582 // phpcs:enable
1583 global $conf, $langs;
1584
1585 $langs->load("dict");
1586
1587 $out = '';
1588 //$moreattrib = '';
1589 $incotermArray = array();
1590
1591 $sql = "SELECT rowid, code";
1592 $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1593 $sql .= " WHERE active > 0";
1594 $sql .= " ORDER BY code ASC";
1595
1596 dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1597 $resql = $this->db->query($sql);
1598 if ($resql) {
1599 if ($conf->use_javascript_ajax && !$forcecombo) {
1600 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1601 $out .= ajax_combobox($htmlname, $events);
1602 }
1603
1604 if (!empty($page)) {
1605 $out .= '<form method="post" action="' . $page . '">';
1606 $out .= '<input type="hidden" name="action" value="set_incoterms">';
1607 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1608 }
1609
1610 $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1611 $out .= '<option value="0">&nbsp;</option>';
1612 $num = $this->db->num_rows($resql);
1613 $i = 0;
1614 if ($num) {
1615 while ($i < $num) {
1616 $obj = $this->db->fetch_object($resql);
1617 $incotermArray[$i]['rowid'] = $obj->rowid;
1618 $incotermArray[$i]['code'] = $obj->code;
1619 $i++;
1620 }
1621
1622 foreach ($incotermArray as $row) {
1623 if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1624 $out .= '<option value="' . $row['rowid'] . '" selected>';
1625 } else {
1626 $out .= '<option value="' . $row['rowid'] . '">';
1627 }
1628
1629 if ($row['code']) {
1630 $out .= $row['code'];
1631 }
1632
1633 $out .= '</option>';
1634 }
1635 }
1636 $out .= '</select>';
1637 $out .= ajax_combobox($htmlname);
1638
1639 if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1640 $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1641 //$moreattrib .= ' autocomplete="off"';
1642 }
1643 $out .= '<input id="location_incoterms" class="maxwidthonsmartphone heightofcombo" type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1644
1645 if (!empty($page)) {
1646 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1647 }
1648 } else {
1649 dol_print_error($this->db);
1650 }
1651
1652 return $out;
1653 }
1654
1655 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1656
1670 public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
1671 {
1672 // phpcs:enable
1673 global $langs;
1674
1675 // If product & services are enabled or both disabled.
1676 if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1677 || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1678 if (empty($hidetext)) {
1679 print $langs->trans("Type").'...';
1680 }
1681
1682 print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1683 if ($showempty) {
1684 print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
1685 if ($selected == -1) {
1686 print ' selected';
1687 }
1688 print '>';
1689 if (is_numeric($showempty)) {
1690 print '&nbsp;';
1691 } else {
1692 print $showempty;
1693 }
1694 print '</option>';
1695 }
1696
1697 print '<option value="0"';
1698 if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1699 print ' selected';
1700 }
1701 print '>' . $langs->trans("Product");
1702 print '</option>';
1703
1704 print '<option value="1"';
1705 if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1706 print ' selected';
1707 }
1708 print '>' . $langs->trans("Service");
1709 print '</option>';
1710
1711 print '</select>';
1712
1713 if ($useajaxcombo) {
1714 print ajax_combobox('select_' . $htmlname);
1715 }
1716 //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1717 }
1718 if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1719 print $langs->trans("Service");
1720 print '<input type="hidden" name="' . $htmlname . '" value="1">';
1721 }
1722 if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1723 print $langs->trans("Product");
1724 print '<input type="hidden" name="' . $htmlname . '" value="0">';
1725 }
1726 if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1727 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
1728 }
1729 }
1730
1731 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1732
1738 public function load_cache_types_fees()
1739 {
1740 // phpcs:enable
1741 global $langs;
1742
1743 $num = count($this->cache_types_fees);
1744 if ($num > 0) {
1745 return 0; // Cache already loaded
1746 }
1747
1748 dol_syslog(__METHOD__, LOG_DEBUG);
1749
1750 $langs->load("trips");
1751
1752 $sql = "SELECT c.code, c.label";
1753 $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1754 $sql .= " WHERE active > 0";
1755
1756 $resql = $this->db->query($sql);
1757 if ($resql) {
1758 $num = $this->db->num_rows($resql);
1759 $i = 0;
1760
1761 while ($i < $num) {
1762 $obj = $this->db->fetch_object($resql);
1763
1764 // If a translation exists, we use is, otherwise, we take the label by default
1765 $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1766 $this->cache_types_fees[$obj->code] = $label;
1767 $i++;
1768 }
1769
1770 asort($this->cache_types_fees);
1771
1772 return $num;
1773 } else {
1774 dol_print_error($this->db);
1775 return -1;
1776 }
1777 }
1778
1779 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1780
1789 public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1790 {
1791 // phpcs:enable
1792 global $user, $langs;
1793
1794 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1795
1796 $this->load_cache_types_fees();
1797
1798 print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1799 if ($showempty) {
1800 print '<option value="-1"';
1801 if ($selected == -1) {
1802 print ' selected';
1803 }
1804 print '>&nbsp;</option>';
1805 }
1806
1807 foreach ($this->cache_types_fees as $key => $value) {
1808 print '<option value="' . $key . '"';
1809 if ($key == $selected) {
1810 print ' selected';
1811 }
1812 print '>';
1813 print $value;
1814 print '</option>';
1815 }
1816
1817 print '</select>';
1818 if ($user->admin) {
1819 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1820 }
1821 }
1822
1823
1824 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1825
1848 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)
1849 {
1850 // phpcs:enable
1851 global $conf, $langs;
1852
1853 $out = '';
1854
1855 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1856 if (is_null($ajaxoptions)) {
1857 $ajaxoptions = array();
1858 }
1859
1860 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1861
1862 // No immediate load of all database
1863 $placeholder = '';
1864 if ($selected && empty($selected_input_value)) {
1865 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1866 $societetmp = new Societe($this->db);
1867 $societetmp->fetch($selected);
1868 $selected_input_value = $societetmp->name;
1869 unset($societetmp);
1870 }
1871
1872 // mode 1
1873 $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 : '');
1874
1875 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1876 if (empty($hidelabel)) {
1877 $out .= $langs->trans("RefOrLabel") . ' : ';
1878 } elseif ($hidelabel == 1 && !is_numeric($showempty)) {
1879 $placeholder = $langs->trans($showempty);
1880 } elseif ($hidelabel > 1) {
1881 $placeholder = $langs->trans("RefOrLabel");
1882 if ($hidelabel == 2) {
1883 $out .= img_picto($langs->trans("Search"), 'search');
1884 }
1885 }
1886 $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" />';
1887 if ($hidelabel == 3) {
1888 $out .= img_picto($langs->trans("Search"), 'search');
1889 }
1890
1891 $out .= ajax_event($htmlname, $events);
1892
1893 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, getDolGlobalInt('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1894 } else {
1895 // Immediate load of all database
1896 $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1897 }
1898
1899 return $out;
1900 }
1901
1902
1903 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1904
1930 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 = '')
1931 {
1932 // phpcs:enable
1933
1934 global $conf, $langs;
1935
1936 $out = '';
1937
1938 $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1939 if ($nokeyifsocid && $socid > 0) {
1940 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1941 }
1942
1943 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1944 $ajaxoptions = array();
1945
1946 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1947
1948 // No immediate load of all database
1949 $placeholder = '';
1950 if ($selected && empty($selected_input_value)) {
1951 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1952 $contacttmp = new Contact($this->db);
1953 $contacttmp->fetch($selected);
1954 $selected_input_value = $contacttmp->getFullName($langs);
1955 unset($contacttmp);
1956 }
1957 if (!is_numeric($showempty)) {
1958 $placeholder = $showempty;
1959 }
1960
1961 // mode 1
1962 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1963
1964 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1965
1966 $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" />';
1967
1968 $out .= ajax_event($htmlname, $events);
1969
1970 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/contact/ajax/contact.php', $urloption, getDolGlobalInt('CONTACT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1971 } else {
1972 // Immediate load of all database
1973 $multiple = false;
1974 $disableifempty = 0;
1975 $options_only = 0;
1976 $limitto = '';
1977
1978 $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty);
1979 }
1980
1981 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1982
1983 return $out;
1984 }
1985
1986
1987 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1988
2012 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)
2013 {
2014 // phpcs:enable
2015 global $user, $langs;
2016 global $hookmanager;
2017
2018 $langs->loadLangs(array("companies", "suppliers"));
2019
2020 $out = '';
2021 $num = 0;
2022 $outarray = array();
2023
2024 if ($selected === '') {
2025 $selected = array();
2026 } elseif (!is_array($selected)) {
2027 $selected = array($selected);
2028 }
2029
2030 // Clean $filter that may contains sql conditions so sql code
2031 if (function_exists('testSqlAndScriptInject')) {
2032 if (testSqlAndScriptInject($filter, 3) > 0) {
2033 $filter = '';
2034 return 'SQLInjectionTryDetected';
2035 }
2036 }
2037
2038 if ($filter != '') { // If a filter was provided
2039 $errormsg = '';
2040 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2041
2042 // Redo clean $filter that may contains sql conditions so sql code
2043 if (function_exists('testSqlAndScriptInject')) {
2044 if (testSqlAndScriptInject($filter, 3) > 0) {
2045 $filter = '';
2046 return 'SQLInjectionTryDetected';
2047 }
2048 }
2049 }
2050
2051 // We search companies
2052 $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
2053 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2054 $sql .= ", s.address, s.zip, s.town";
2055 $sql .= ", dictp.code as country_code";
2056 }
2057 $sql .= " FROM " . $this->db->prefix() . "societe as s";
2058 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2059 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
2060 }
2061 if (!$user->hasRight('societe', 'client', 'voir')) {
2062 $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
2063 }
2064 $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
2065 if (!empty($user->socid)) {
2066 $sql .= " AND s.rowid = " . ((int) $user->socid);
2067 }
2068 if ($filter) {
2069 // $filter is safe because, it has been tested by testSqlAndScriptInject() and sanitized by forgeSQLFromUniversalSearchCriteria()
2070 $sqlwhere = $filter; // @phan-suppress-current-line SqlInjection
2071 $sql .= " AND (" . $sqlwhere . ")";
2072 }
2073 if (!$user->hasRight('societe', 'client', 'voir')) {
2074 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
2075 }
2076 if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
2077 $sql .= " AND s.status <> 0";
2078 }
2079 if (!empty($excludeids)) {
2080 $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
2081 }
2082 // Add where from hooks
2083 $parameters = array();
2084 $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
2085 $sql .= $hookmanager->resPrint;
2086 // Add criteria
2087 if ($filterkey && $filterkey != '') {
2088 $sql .= " AND (";
2089 $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
2090 // For natural search
2091 $search_crit = explode(' ', $filterkey);
2092 $i = 0;
2093 if (count($search_crit) > 1) {
2094 $sql .= "(";
2095 }
2096 foreach ($search_crit as $crit) {
2097 if ($i > 0) {
2098 $sql .= " AND ";
2099 }
2100 $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
2101 $i++;
2102 }
2103 if (count($search_crit) > 1) {
2104 $sql .= ")";
2105 }
2106 if (isModEnabled('barcode')) {
2107 $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2108 }
2109 $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2110 $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2111 $sql .= ")";
2112 }
2113 $sql .= $this->db->order("nom", "ASC");
2114 $sql .= $this->db->plimit($limit, 0);
2115
2116 // Build output string
2117 dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
2118 $resql = $this->db->query($sql);
2119 if ($resql) {
2120 // Construct $out and $outarray
2121 $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple' : '') . '>' . "\n";
2122
2123 $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
2124 if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
2125 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2126 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2127 if ($showempty && !is_numeric($showempty)) {
2128 $textifempty = $langs->trans($showempty);
2129 } else {
2130 $textifempty .= $langs->trans("All");
2131 }
2132 }
2133 if ($showempty) {
2134 $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
2135 }
2136
2137 $companytemp = new Societe($this->db);
2138
2139 $num = $this->db->num_rows($resql);
2140 $i = 0;
2141 if ($num) {
2142 while ($i < $num) {
2143 $obj = $this->db->fetch_object($resql);
2144 $label = '';
2145 if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
2146 if (($obj->client) && (!empty($obj->code_client))) {
2147 $label = $obj->code_client . ' - ';
2148 }
2149 if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
2150 $label .= $obj->code_fournisseur . ' - ';
2151 }
2152 $label .= ' ' . $obj->name;
2153 } else {
2154 $label = $obj->name;
2155 }
2156
2157 if (!empty($obj->name_alias)) {
2158 $label .= ' (' . $obj->name_alias . ')';
2159 }
2160
2161 if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
2162 $label .= ' - '.$obj->tva_intra;
2163 }
2164
2165 $labelhtml = $label;
2166
2167 if ($showtype) {
2168 $companytemp->id = $obj->rowid;
2169 $companytemp->client = $obj->client;
2170 $companytemp->fournisseur = $obj->fournisseur;
2171 $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
2172 if ($tmptype) {
2173 $labelhtml .= ' ' . $tmptype;
2174 }
2175
2176 if ($obj->client || $obj->fournisseur) {
2177 $label .= ' (';
2178 }
2179 if ($obj->client == 1 || $obj->client == 3) {
2180 $label .= $langs->trans("Customer");
2181 }
2182 if ($obj->client == 2 || $obj->client == 3) {
2183 $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
2184 }
2185 if ($obj->fournisseur) {
2186 $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
2187 }
2188 if ($obj->client || $obj->fournisseur) {
2189 $label .= ')';
2190 }
2191 }
2192
2193 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2194 $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
2195 if (!empty($obj->country_code)) {
2196 $s .= ', ' . $langs->trans('Country' . $obj->country_code);
2197 }
2198 $label .= $s;
2199 $labelhtml .= $s;
2200 }
2201
2202 if (empty($outputmode)) {
2203 if (in_array($obj->rowid, $selected)) {
2204 $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>';
2205 } else {
2206 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2207 }
2208 } else {
2209 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
2210 }
2211
2212 $i++;
2213 if (($i % 10) == 0) {
2214 $out .= "\n";
2215 }
2216 }
2217 }
2218 $out .= '</select>' . "\n";
2219 if (!$forcecombo) {
2220 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2221 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
2222 }
2223 } else {
2224 dol_print_error($this->db);
2225 }
2226
2227 $this->result = array('nbofthirdparties' => $num);
2228
2229 if ($outputmode) {
2230 return $outarray;
2231 }
2232 return $out;
2233 }
2234
2235
2261 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 = '')
2262 {
2263 global $conf, $user, $langs, $hookmanager, $action;
2264
2265 $langs->load('companies');
2266
2267 if (empty($htmlid)) {
2268 $htmlid = $htmlname;
2269 }
2270 $num = 0;
2271 $out = '';
2272 $outarray = array();
2273
2274 if ($selected === '') {
2275 $selected = array();
2276 } elseif (!is_array($selected)) {
2277 $selected = array((int) $selected);
2278 }
2279
2280 // Clean $filter that may contains sql conditions so sql code
2281 if (function_exists('testSqlAndScriptInject')) {
2282 if (testSqlAndScriptInject($filter, 3) > 0) {
2283 $filter = '';
2284 return 'SQLInjectionTryDetected';
2285 }
2286 }
2287
2288 if ($filter != '') { // If a filter was provided
2289 if (preg_match('/[\‍(\‍)]/', $filter)) {
2290 // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
2291 $errormsg = '';
2292 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2293
2294 // Redo clean $filter that may contains sql conditions so sql code
2295 if (function_exists('testSqlAndScriptInject')) {
2296 if (testSqlAndScriptInject($filter, 3) > 0) {
2297 $filter = '';
2298 return 'SQLInjectionTryDetected';
2299 }
2300 }
2301 } else {
2302 // If not, we do nothing. We already know that there is no parenthesis
2303 // TODO Disallow this case in a future by returning an error here.
2304 dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Filter Syntax.", LOG_WARNING);
2305 }
2306 }
2307
2308 if (!is_object($hookmanager)) {
2309 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2310 $hookmanager = new HookManager($this->db);
2311 }
2312
2313 // We search third parties
2314 $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";
2315 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2316 $sql .= ", s.nom as company, s.town AS company_town";
2317 }
2318 $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
2319 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2320 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = sp.fk_soc";
2321 }
2322 $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
2323 $sql .= " AND ((sp.fk_user_creat = ".((int) $user->id)." AND sp.priv = 1) OR sp.priv = 0)"; // check if this is a private contact
2324 if ($socid > 0 || $socid == -1) {
2325 $sql .= " AND sp.fk_soc = " . ((int) $socid);
2326 }
2327 if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
2328 $sql .= " AND sp.statut <> 0";
2329 }
2330 // filter user access
2331 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
2332 $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 .")";
2333 }
2334 if ($user->socid > 0) {
2335 $sql .= " AND sp.fk_soc = ".((int) $user->socid);
2336 }
2337 if ($filter) {
2338 // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
2339 // if not, by testSqlAndScriptInject() only.
2340 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2341 $sql .= " AND (" . $sanitizedfilter . ")";
2342 }
2343 // Add where from hooks
2344 $parameters = array();
2345 $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
2346 $sql .= $hookmanager->resPrint;
2347 $sql .= " ORDER BY sp.lastname ASC";
2348
2349 dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
2350 $resql = $this->db->query($sql);
2351 if ($resql) {
2352 $num = $this->db->num_rows($resql);
2353
2354 if ($htmlname != 'none' && !$options_only) {
2355 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
2356 }
2357
2358 if ($showempty && !is_numeric($showempty)) {
2359 $textforempty = $showempty;
2360 $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
2361 } else {
2362 if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
2363 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
2364 }
2365 if ($showempty == 2) {
2366 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
2367 }
2368 }
2369
2370 $i = 0;
2371 if ($num) {
2372 include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2373 $contactstatic = new Contact($this->db);
2374
2375 while ($i < $num) {
2376 $obj = $this->db->fetch_object($resql);
2377
2378 // Set email (or phones) and town extended infos
2379 $extendedInfos = '';
2380 if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2381 $extendedInfos = array();
2382 $email = trim($obj->email);
2383 if (!empty($email)) {
2384 $extendedInfos[] = $email;
2385 } else {
2386 $phone = trim($obj->phone);
2387 $phone_perso = trim($obj->phone_perso);
2388 $phone_mobile = trim($obj->phone_mobile);
2389 if (!empty($phone)) {
2390 $extendedInfos[] = $phone;
2391 }
2392 if (!empty($phone_perso)) {
2393 $extendedInfos[] = $phone_perso;
2394 }
2395 if (!empty($phone_mobile)) {
2396 $extendedInfos[] = $phone_mobile;
2397 }
2398 }
2399 $contact_town = trim($obj->contact_town);
2400 $company_town = trim($obj->company_town);
2401 if (!empty($contact_town)) {
2402 $extendedInfos[] = $contact_town;
2403 } elseif (!empty($company_town)) {
2404 $extendedInfos[] = $company_town;
2405 }
2406 $extendedInfos = implode(' - ', $extendedInfos);
2407 if (!empty($extendedInfos)) {
2408 $extendedInfos = ' - ' . $extendedInfos;
2409 }
2410 }
2411
2412 $contactstatic->id = $obj->rowid;
2413 $contactstatic->lastname = $obj->lastname;
2414 $contactstatic->firstname = $obj->firstname;
2415 if ($obj->statut == 1) {
2416 $tmplabel = '';
2417 if ($htmlname != 'none') {
2418 $disabled = 0;
2419 if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
2420 $disabled = 1;
2421 }
2422 if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
2423 $disabled = 1;
2424 }
2425 if (!empty($selected) && in_array($obj->rowid, $selected)) {
2426 $out .= '<option value="' . $obj->rowid . '"';
2427 if ($disabled) {
2428 $out .= ' disabled';
2429 }
2430 $out .= ' selected>';
2431
2432 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2433 if ($showfunction && $obj->poste) {
2434 $tmplabel .= ' (' . $obj->poste . ')';
2435 }
2436 if (($showsoc > 0) && $obj->company) {
2437 $tmplabel .= ' - (' . $obj->company . ')';
2438 }
2439
2440 $out .= $tmplabel;
2441 $out .= '</option>';
2442 } else {
2443 $out .= '<option value="' . $obj->rowid . '"';
2444 if ($disabled) {
2445 $out .= ' disabled';
2446 }
2447 $out .= '>';
2448
2449 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2450 if ($showfunction && $obj->poste) {
2451 $tmplabel .= ' (' . $obj->poste . ')';
2452 }
2453 if (($showsoc > 0) && $obj->company) {
2454 $tmplabel .= ' - (' . $obj->company . ')';
2455 }
2456
2457 $out .= $tmplabel;
2458 $out .= '</option>';
2459 }
2460 } else {
2461 if (in_array($obj->rowid, $selected)) {
2462 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2463 if ($showfunction && $obj->poste) {
2464 $tmplabel .= ' (' . $obj->poste . ')';
2465 }
2466 if (($showsoc > 0) && $obj->company) {
2467 $tmplabel .= ' - (' . $obj->company . ')';
2468 }
2469
2470 $out .= $tmplabel;
2471 }
2472 }
2473
2474 if ($tmplabel != '') {
2475 array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
2476 }
2477 }
2478 $i++;
2479 }
2480 } else {
2481 $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
2482 $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
2483 $out .= $labeltoshow;
2484 $out .= '</option>';
2485 }
2486
2487 $parameters = array(
2488 'socid' => $socid,
2489 'htmlname' => $htmlname,
2490 'resql' => $resql,
2491 'out' => &$out,
2492 'showfunction' => $showfunction,
2493 'showsoc' => $showsoc,
2494 );
2495
2496 $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2497
2498 if ($htmlname != 'none' && !$options_only) {
2499 $out .= '</select>';
2500 }
2501
2502 if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
2503 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2504 $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
2505 }
2506
2507 $this->num = $num;
2508
2509 if ($options_only === 2) {
2510 // Return array of options
2511 return $outarray;
2512 } else {
2513 return $out;
2514 }
2515 } else {
2516 dol_print_error($this->db);
2517 return -1;
2518 }
2519 }
2520
2521
2522 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2523
2534 public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2535 {
2536 // phpcs:enable
2537 global $langs, $conf;
2538
2539 // On recherche les remises
2540 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2541 $sql .= " re.description, re.fk_facture_source";
2542 $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2543 $sql .= " WHERE re.fk_soc = " . (int) $socid;
2544 $sql .= " AND re.entity = " . ((int) $conf->entity);
2545 if ($filter) {
2546 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2547 $sql .= " AND " . $sanitizedfilter;
2548 }
2549 $sql .= " ORDER BY re.description ASC";
2550
2551 dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
2552 $resql = $this->db->query($sql);
2553 if ($resql) {
2554 print '<select id="select_' . $htmlname . '" class="flat maxwidth200onsmartphone" name="' . $htmlname . '">';
2555 $num = $this->db->num_rows($resql);
2556
2557 $qualifiedlines = $num;
2558
2559 $i = 0;
2560 if ($num) {
2561 print '<option value="0">&nbsp;</option>';
2562 while ($i < $num) {
2563 $obj = $this->db->fetch_object($resql);
2564 $desc = dol_trunc($obj->description, 40);
2565 if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
2566 $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
2567 }
2568 if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
2569 $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
2570 }
2571 if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
2572 $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
2573 }
2574 if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
2575 $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
2576 }
2577
2578 $selectstring = '';
2579 if ($selected > 0 && $selected == $obj->rowid) {
2580 $selectstring = ' selected';
2581 }
2582
2583 $disabled = '';
2584 if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2585 $qualifiedlines--;
2586 $disabled = ' disabled';
2587 }
2588
2589 if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2590 $tmpfac = new Facture($this->db);
2591 if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2592 $desc = $desc . ' - ' . $tmpfac->ref;
2593 }
2594 }
2595
2596 print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2597 $i++;
2598 }
2599 }
2600 print '</select>';
2601 print ajax_combobox('select_' . $htmlname);
2602
2603 return $qualifiedlines;
2604 } else {
2605 dol_print_error($this->db);
2606 return -1;
2607 }
2608 }
2609
2610
2611 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2612
2628 public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2629 {
2630 // phpcs:enable
2631 print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2632 }
2633
2634 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2635
2660 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)
2661 {
2662 // phpcs:enable
2663 global $conf, $user, $langs, $hookmanager;
2664 global $action;
2665
2666 // Convert $selected into an int (in case it is an object)
2667 if (is_object($userselected)) {
2668 $selected = (int) $userselected->id;
2669 } elseif (is_numeric($userselected)) {
2670 $selected = (int) $userselected;
2671 } elseif (is_array($userselected)) {
2672 $selected = $userselected;
2673 } else {
2674 $selected = -1;
2675 }
2676
2677 // If no preselected user defined, we take current user
2678 if ((is_numeric($selected) && ((int) $selected < -4 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2679 $selected = $user->id;
2680 }
2681
2682 // Convert selected int into an array
2683 if (!is_array($selected)) {
2684 if ($selected === -1 || $selected === '') {
2685 $selected = array();
2686 } else {
2687 $selected = array($selected);
2688 }
2689 }
2690
2691 // Exclude some users in $excludeUsers string
2692 $excludeUsers = null;
2693 if (is_array($exclude)) {
2694 $excludeUsers = implode(",", $exclude);
2695 }
2696
2697 // Include some users in $includeUsers string
2698 $includeUsers = null;
2699 $includeUsersArray = array();
2700 if (is_array($include)) {
2701 $includeUsersArray = $include;
2702 } elseif ($include == 'hierarchy') {
2703 // Build list includeUsersArray to have only hierarchy
2704 $includeUsersArray = $user->getAllChildIds(0);
2705 } elseif ($include == 'hierarchyme') {
2706 // Build list includeUsersArray to have only hierarchy and current user
2707 $includeUsersArray = $user->getAllChildIds(1);
2708 }
2709 // Get list of allowed users
2710 /* 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
2711 * see all other users and element in other. For example in agenda, we can have permission to read all event of otherusers.
2712 * So we disable this.
2713 if (!$user->hasRight('user', 'user', 'lire')) {
2714 if (empty($includeUsersArray)) {
2715 $includeUsers = implode(",", $user->getAllChildIds(1));
2716 } else {
2717 $includeUsers = implode(",", array_intersect($includeUsersArray, $user->getAllChildIds(1)));
2718 }
2719 } else {
2720 $includeUsers = implode(",", $includeUsersArray);
2721 } */
2722 $includeUsers = implode(",", $includeUsersArray);
2723
2724 $num = 0;
2725
2726 $out = '';
2727 $outarray = array();
2728 $outarray2 = array();
2729
2730 // Do we want to show the label of entity into the combo list ?
2731 $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity) && !preg_match('/^search_/', $htmlname);
2732 $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2733
2734 // Forge request to select users
2735 $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";
2736 if ($showlabelofentity) {
2737 $sql .= ", e.label";
2738 }
2739 $sql .= " FROM " . $this->db->prefix() . "user as u";
2740 if ($showlabelofentity) {
2741 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2742 }
2743 // Condition here should be the same than into societe->getSalesRepresentatives().
2744 if ($userissuperadminentityone && $force_entity !== 'default') {
2745 if (!empty($force_entity)) {
2746 $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2747 } else {
2748 $sql .= " WHERE u.entity IS NOT NULL";
2749 }
2750 } else {
2751 if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2752 $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2753 } else {
2754 $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2755 }
2756 }
2757
2758 if (!empty($user->socid)) {
2759 $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2760 }
2761 if (is_array($exclude) && $excludeUsers) {
2762 $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2763 }
2764 if ($includeUsers) {
2765 $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2766 }
2767 if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2768 $sql .= " AND (u.statut <> 0";
2769 if (!empty($selected)) {
2770 $sql .= " OR rowid IN (".$this->db->sanitize(implode(',', $selected)).")"; // We must always keep the selected users to avoid to loose it/them when updating
2771 }
2772 $sql .= ")";
2773 }
2774 if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX')) {
2775 $sql .= " AND u.employee <> 0";
2776 }
2777 if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX')) {
2778 $sql .= " AND u.fk_soc IS NULL";
2779 }
2780 if (!empty($morefilter)) {
2781 $errormessage = '';
2782 $sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
2783 if ($errormessage) {
2784 $this->errors[] = $errormessage;
2785 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2786 if ($outputmode == 0) {
2787 return 'Error bad param $morefilter';
2788 } else {
2789 return array();
2790 }
2791 }
2792 }
2793
2794 //Add hook to filter on user (for example on usergroup define in custom modules)
2795 $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2796 if (!empty($reshook)) {
2797 $sql .= $hookmanager->resPrint;
2798 }
2799
2800 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2801 $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2802 } else {
2803 $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2804 }
2805
2806 dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2807
2808 $resql = $this->db->query($sql);
2809 if ($resql) {
2810 $num = $this->db->num_rows($resql);
2811 $i = 0;
2812 if ($num) {
2813 // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2814 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2815 if ($show_empty && !$multiple) {
2816 $textforempty = ' ';
2817 if (!empty($conf->use_javascript_ajax)) {
2818 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2819 }
2820 if (!is_numeric($show_empty)) {
2821 $textforempty = $show_empty;
2822 }
2823 $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2824
2825 $outarray[($show_empty < 0 ? $show_empty : -1)] = $textforempty;
2826 $outarray2[($show_empty < 0 ? $show_empty : -1)] = array(
2827 'id' => ($show_empty < 0 ? $show_empty : -1),
2828 'label' => $textforempty,
2829 'labelhtml' => $textforempty,
2830 'color' => '',
2831 'picto' => ''
2832 );
2833 }
2834 if ($showalso == 2 || $showalso == 3) {
2835 $out .= '<option value="-3"' . ((in_array(-3, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("MyTeam") . ' --</option>' . "\n";
2836
2837 $hasAtLeastOneSubordinate = (count($user->getAllChildIds(1)) > 1);
2838 if ($hasAtLeastOneSubordinate) {
2839 //$sql = "SELECT rowid FROM".MAIN_DB_PREFIX."user "
2840 $outarray[-3] = '-- ' . $langs->trans("MyTeam") . ' --';
2841 $outarray2[-3] = array(
2842 'id' => -3,
2843 'label' => '-- ' . $langs->trans("MyTeam") . ' --',
2844 'labelhtml' => '-- ' . $langs->trans("MyTeam") . ' --',
2845 'color' => '',
2846 'picto' => ''
2847 );
2848 }
2849 }
2850 if ($showalso == 1 || $showalso == 3) {
2851 $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2852
2853 $outarray[-2] = '-- ' . $langs->trans("Everybody") . ' --';
2854 $outarray2[-2] = array(
2855 'id' => -2,
2856 'label' => '-- ' . $langs->trans("Everybody") . ' --',
2857 'labelhtml' => '-- ' . $langs->trans("Everybody") . ' --',
2858 'color' => '',
2859 'picto' => ''
2860 );
2861 }
2862 if ($showalso == 4) {
2863 $out .= '<option value="-4"' . ((in_array(-4, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("AllProjectContacts") . ' --</option>' . "\n";
2864
2865 $outarray[-4] = '-- ' . $langs->trans("AllProjectContacts") . ' --';
2866 $outarray2[-4] = array(
2867 'id' => -4,
2868 'label' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2869 'labelhtml' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2870 'color' => '',
2871 'picto' => ''
2872 );
2873 }
2874
2875 $userstatic = new User($this->db);
2876
2877 while ($i < $num) {
2878 $obj = $this->db->fetch_object($resql);
2879
2880 $userstatic->id = $obj->rowid;
2881 $userstatic->lastname = $obj->lastname;
2882 $userstatic->firstname = $obj->firstname;
2883 $userstatic->photo = $obj->photo;
2884 $userstatic->status = $obj->status;
2885 $userstatic->entity = $obj->entity;
2886 $userstatic->admin = $obj->admin;
2887 $userstatic->gender = $obj->gender;
2888
2889 $disableline = '';
2890 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2891 $disableline = ($enableonlytext ? $enableonlytext : '1');
2892 }
2893
2894 $labeltoshow = '';
2895 $labeltoshowhtml = '';
2896
2897 // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2898 $fullNameMode = 0;
2899 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2900 $fullNameMode = 1; //Firstname+lastname
2901 }
2902 $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2903 $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2904 if (empty($obj->firstname) && empty($obj->lastname)) {
2905 $labeltoshow .= $obj->login;
2906 $labeltoshowhtml .= $obj->login;
2907 }
2908
2909 // Complete name with a more info string like: ' (info1 - info2 - ...)'
2910 $moreinfo = '';
2911 $moreinfohtml = '';
2912 if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2913 $moreinfo .= ($moreinfo ? ' - ' : ' (');
2914 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2915 $moreinfo .= $obj->login;
2916 $moreinfohtml .= $obj->login;
2917 }
2918 if ($showstatus >= 0) {
2919 if ($obj->status == 1 && $showstatus == 1) {
2920 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2921 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2922 }
2923 if ($obj->status == 0 && $showstatus == 1) {
2924 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2925 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2926 }
2927 }
2928 if ($showlabelofentity) {
2929 if (empty($obj->entity)) {
2930 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2931 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2932 } else {
2933 if ($obj->entity != $conf->entity) {
2934 $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2935 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2936 }
2937 }
2938 }
2939 $moreinfo .= (!empty($moreinfo) ? ')' : '');
2940 $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2941 if (!empty($disableline) && $disableline != '1') {
2942 // Add text from $enableonlytext parameter
2943 $moreinfo .= ' - ' . $disableline;
2944 $moreinfohtml .= ' - ' . $disableline;
2945 }
2946 $labeltoshow .= $moreinfo;
2947 $labeltoshowhtml .= $moreinfohtml;
2948
2949 $out .= '<option value="' . $obj->rowid . '"';
2950 if (!empty($disableline)) {
2951 $out .= ' disabled';
2952 }
2953 if (in_array($obj->rowid, $selected)) {
2954 $out .= ' selected';
2955 }
2956 $out .= ' data-html="';
2957
2958 $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2959 if ($showstatus >= 0 && $obj->status == 0) {
2960 $outhtml .= '<strike class="opacitymediumxxx">';
2961 }
2962 $outhtml .= $labeltoshowhtml;
2963 if ($showstatus >= 0 && $obj->status == 0) {
2964 $outhtml .= '</strike>';
2965 }
2966 $labeltoshowhtml = $outhtml;
2967
2968 $out .= dol_escape_htmltag($outhtml);
2969 $out .= '">';
2970 $out .= $labeltoshow;
2971 $out .= '</option>';
2972
2973 $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2974 $outarray2[$userstatic->id] = array(
2975 'id' => $userstatic->id,
2976 'label' => $labeltoshow,
2977 'labelhtml' => $labeltoshowhtml,
2978 'color' => '',
2979 'picto' => ''
2980 );
2981
2982 $i++;
2983 }
2984 } else {
2985 $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2986 $out .= '<option value="">' . $langs->trans("None") . '</option>';
2987 }
2988 $out .= '</select>';
2989
2990 if ($num && !$forcecombo) {
2991 // Enhance with select2
2992 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2993 $out .= ajax_combobox($htmlname);
2994 }
2995 } else {
2996 dol_print_error($this->db);
2997 }
2998
2999 $this->num = $num;
3000
3001 if ($outputmode == 2) {
3002 return $outarray2;
3003 } elseif ($outputmode) {
3004 return $outarray;
3005 }
3006
3007 return $out;
3008 }
3009
3010
3011 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3035 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)
3036 {
3037 // phpcs:enable
3038 global $langs, $user;
3039
3040 $userstatic = new User($this->db);
3041 $out = '';
3042
3043 if (!empty($_SESSION['assignedtouser'])) {
3044 $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
3045 if (!is_array($assignedtouser)) {
3046 $assignedtouser = array();
3047 }
3048 } else {
3049 $assignedtouser = array();
3050 }
3051 $nbassignetouser = count($assignedtouser);
3052
3053 //if ($nbassignetouser && $action != 'view') $out .= '<br>';
3054 if ($nbassignetouser) {
3055 $out .= '<ul class="attendees">';
3056 }
3057 $i = 0;
3058 $ownerid = 0;
3059 foreach ($assignedtouser as $key => $value) {
3060 if ($value['id'] == $ownerid) {
3061 continue;
3062 }
3063
3064 $out .= '<li>';
3065
3066 $userstatic->fetch($value['id']);
3067 $out .= $userstatic->getNomUrl(-4);
3068
3069 if ($i == 0) {
3070 $ownerid = $value['id'];
3071 $out .= ' (' . $langs->trans("Owner") . ')';
3072 }
3073 // Add picto to delete owner/assignee
3074 if ($nbassignetouser > 1 && $action != 'view') {
3075 $canremoveassignee = 1;
3076 if ($i == 0) {
3077 // We are on the owner of the event
3078 if (!$canremoveowner) {
3079 $canremoveassignee = 0;
3080 }
3081 if (!$user->hasRight('agenda', 'allactions', 'create')) {
3082 $canremoveassignee = 0; // Can't remove the owner
3083 }
3084 } else {
3085 // We are not on the owner of the event but on a secondary assignee
3086 }
3087 if ($canremoveassignee) {
3088 // If user has all permission, he should be ableto remove a assignee.
3089 // If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
3090 $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 . '">';
3091 }
3092 }
3093 // Show my availability
3094 if ($showproperties) {
3095 if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
3096 $out .= '<div class="myavailability inline-block">';
3097 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3098 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ':</span>';
3099 $out .= '</span>';
3100 $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>';
3101 $out .= '</div>';
3102 }
3103 }
3104 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3105 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3106
3107 $out .= '</li>';
3108 $i++;
3109 }
3110 if ($nbassignetouser) {
3111 $out .= '</ul>';
3112 }
3113
3114 // Method with no ajax
3115 if ($action != 'view') {
3116 // Section to add another user
3117 $out .= '<div class="divadduser'.$htmlname.'">';
3118 $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
3119 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3120 $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
3121 $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
3122 $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
3123 $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
3124 $out .= '});';
3125 $out .= '})</script>';
3126 $out .= img_picto('', 'user', 'class="pictofixedwidth"');
3127 $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter, 0, '', 'minwidth200');
3128 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3129 $out .= $langs->trans("Add").'</button>';
3130 $out .= '</div>';
3131 //$out .= '<br>';
3132 }
3133
3134 return $out;
3135 }
3136
3137 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3157 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())
3158 {
3159 // phpcs:enable
3160 global $langs;
3161
3162 require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php';
3163 require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php';
3164 $formresources = new FormResource($this->db);
3165 $resourcestatic = new Dolresource($this->db);
3166
3167 $out = '';
3168 if (!empty($_SESSION['assignedtoresource'])) {
3169 $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
3170 if (!is_array($assignedtoresource)) {
3171 $assignedtoresource = array();
3172 }
3173 } else {
3174 $assignedtoresource = array();
3175 }
3176 $nbassignetoresource = count($assignedtoresource);
3177
3178 //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
3179 if ($nbassignetoresource) {
3180 $out .= '<ul class="attendees">';
3181 }
3182 $i = 0;
3183
3184 foreach ($assignedtoresource as $key => $value) {
3185 $out .= '<li>';
3186 $resourcestatic->fetch($value['id']);
3187 $out .= $resourcestatic->getNomUrl(-1);
3188 if ($nbassignetoresource >= 1 && $action != 'view') {
3189 $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 . '">';
3190 }
3191 // Show my availability
3192 if ($showproperties) {
3193 if (is_array($listofresourceid) && count($listofresourceid)) {
3194 $out .= '<div class="myavailability inline-block">';
3195 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3196 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ': </span>';
3197 $out .= '</span>';
3198 $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>';
3199 $out .= '</div>';
3200 }
3201 }
3202 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3203 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3204
3205 $out .= '</li>';
3206 $i++;
3207 }
3208 if ($nbassignetoresource) {
3209 $out .= '</ul>';
3210 }
3211
3212 // Method with no ajax
3213 if ($action != 'view') {
3214 $out .= '<input type="hidden" class="removedassignedresourcehidden" name="removedassignedresource" value="">';
3215 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3216 $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
3217 $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
3218 $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
3219 $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
3220 $out .= '});';
3221 $out .= '})</script>';
3222
3223 $events = array();
3224 if ($nbassignetoresource) {
3225 //$out .= img_picto('', 'add', 'class="pictofixedwidth"');
3226 } else {
3227 $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
3228 }
3229 $out .= $formresources->select_resource_list(0, $htmlname, '', 1, 1, 0, $events, '', 2, 0, 'minwidth200');
3230 //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
3231 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3232 $out .= $langs->trans("Add");
3233 $out .= '</button>';
3234 $out .= '<br>';
3235 }
3236
3237 return $out;
3238 }
3239
3240 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3241
3271 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)
3272 {
3273 // phpcs:enable
3274 global $langs, $conf;
3275
3276 $out = '';
3277
3278 // check parameters
3279 $price_level = (!empty($price_level) ? $price_level : 0);
3280 if (is_null($ajaxoptions)) {
3281 $ajaxoptions = array();
3282 }
3283
3284 if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
3285 if (isModEnabled("product") && !isModEnabled('service')) {
3286 $filtertype = '0';
3287 } elseif (!isModEnabled('product') && isModEnabled("service")) {
3288 $filtertype = '1';
3289 }
3290 }
3291
3292 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3293 $placeholder = (is_numeric($showempty) ? '' : 'placeholder="'.dolPrintHTML($showempty).'"');
3294
3295 if ($selected && empty($selected_input_value)) {
3296 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3297 $producttmpselect = new Product($this->db);
3298 $producttmpselect->fetch($selected);
3299 $selected_input_value = $producttmpselect->ref;
3300 unset($producttmpselect);
3301 }
3302 // handle case where product or service module is disabled + no filter specified
3303 if ($filtertype == '') {
3304 if (!isModEnabled('product')) { // when product module is disabled, show services only
3305 $filtertype = 1;
3306 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3307 $filtertype = 0;
3308 }
3309 }
3310 // mode=1 means customers products
3311 $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;
3312 if ((int) $warehouseId > 0) {
3313 $urloption .= '&warehouseid=' . (int) $warehouseId;
3314 }
3315
3316 if (isModEnabled('variants') && is_array($selected_combinations)) {
3317 // Code to automatically insert with javascript the select of attributes under the select of product
3318 // when a parent of variant has been selected.
3319 // Note: Samecode than for product input using select
3320 $htmltag = 'input';
3321 $out .= '
3322 <!-- script to auto show attributes select tags if a variant was selected -->
3323 <script nonce="' . getNonce() . '">
3324 // auto show attributes fields
3325 selected = ' . json_encode($selected_combinations) . ';
3326 combvalues = {};
3327
3328 jQuery(document).ready(function () {
3329
3330 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3331 if (jQuery(this).val() == \'free\') {
3332 jQuery(\'div#attributes_box\').empty();
3333 }
3334 });
3335
3336 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3337
3338 if (!jQuery(this).val()) {
3339 jQuery(\'div#attributes_box\').empty();
3340 return;
3341 }
3342
3343 console.log("A change has started. We get variants fields to inject html select");
3344
3345 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3346 id: jQuery(this).val()
3347 }, function (data) {
3348 jQuery(\'div#attributes_box\').empty();
3349
3350 jQuery.each(data, function (key, val) {
3351
3352 combvalues[val.id] = val.values;
3353
3354 var span = jQuery(document.createElement(\'div\')).css({
3355 \'display\': \'table-row\'
3356 });
3357
3358 span.append(
3359 jQuery(document.createElement(\'div\')).text(val.label).css({
3360 \'font-weight\': \'bold\',
3361 \'display\': \'table-cell\'
3362 })
3363 );
3364
3365 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3366 \'margin-left\': \'15px\',
3367 \'white-space\': \'pre\'
3368 }).append(
3369 jQuery(document.createElement(\'option\')).val(\'\')
3370 );
3371
3372 jQuery.each(combvalues[val.id], function (key, val) {
3373 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3374
3375 if (selected[val.fk_product_attribute] == val.id) {
3376 tag.attr(\'selected\', \'selected\');
3377 }
3378
3379 html.append(tag);
3380 });
3381
3382 span.append(html);
3383 jQuery(\'div#attributes_box\').append(span);
3384 });
3385 })
3386 });
3387
3388 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3389 });
3390 </script>
3391 ';
3392 }
3393
3394 if (empty($hidelabel)) {
3395 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3396 } elseif ($hidelabel > 1) {
3397 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3398 if ($hidelabel == 2) {
3399 $out .= img_picto($langs->trans("Search"), 'search');
3400 }
3401 }
3402
3403 $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" />';
3404 if ($hidelabel == 3) {
3405 $out .= img_picto($langs->trans("Search"), 'search');
3406 }
3407
3408 $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);
3409 } else {
3410 $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase, $warehouseId);
3411
3412 if (isModEnabled('variants') && is_array($selected_combinations)) {
3413 // Code to automatically insert with javascript the select of attributes under the select of product
3414 // when a parent of variant has been selected.
3415 // Note: Samecode than for product input using Ajax
3416 $htmltag = 'select';
3417 $out .= '
3418 <!-- script to auto show attributes select tags if a variant was selected -->
3419 <script nonce="' . getNonce() . '">
3420 // auto show attributes fields
3421 selected = ' . json_encode($selected_combinations) . ';
3422 combvalues = {};
3423
3424 jQuery(document).ready(function () {
3425
3426 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3427 if (jQuery(this).val() == \'free\') {
3428 jQuery(\'div#attributes_box\').empty();
3429 }
3430 });
3431
3432 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3433
3434 if (!jQuery(this).val()) {
3435 jQuery(\'div#attributes_box\').empty();
3436 return;
3437 }
3438
3439 console.log("A change has started. We get variants fields to inject html select");
3440
3441 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3442 id: jQuery(this).val()
3443 }, function (data) {
3444 jQuery(\'div#attributes_box\').empty();
3445
3446 jQuery.each(data, function (key, val) {
3447
3448 combvalues[val.id] = val.values;
3449
3450 var span = jQuery(document.createElement(\'div\')).css({
3451 \'display\': \'table-row\'
3452 });
3453
3454 span.append(
3455 jQuery(document.createElement(\'div\')).text(val.label).css({
3456 \'font-weight\': \'bold\',
3457 \'display\': \'table-cell\'
3458 })
3459 );
3460
3461 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3462 \'margin-left\': \'15px\',
3463 \'white-space\': \'pre\'
3464 }).append(
3465 jQuery(document.createElement(\'option\')).val(\'\')
3466 );
3467
3468 jQuery.each(combvalues[val.id], function (key, val) {
3469 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3470
3471 if (selected[val.fk_product_attribute] == val.id) {
3472 tag.attr(\'selected\', \'selected\');
3473 }
3474
3475 html.append(tag);
3476 });
3477
3478 span.append(html);
3479 jQuery(\'div#attributes_box\').append(span);
3480 });
3481 })
3482 });
3483
3484 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3485 });
3486 </script>
3487 ';
3488 }
3489 }
3490
3491 if (empty($nooutput)) {
3492 print $out;
3493 } else {
3494 return $out;
3495 }
3496 }
3497
3498 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3499
3515 public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
3516 {
3517 // phpcs:enable
3518
3519 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3520
3521 $error = 0;
3522 $out = '';
3523
3524 if (!$forcecombo) {
3525 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3526 $events = array();
3527 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("BOM_USE_SEARCH_TO_SELECT"));
3528 }
3529
3530 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3531
3532 $sql = 'SELECT b.rowid, b.ref, b.label as bomLabel, p.label as productLabel';
3533 $sql .= ' FROM ' . $this->db->prefix() . 'bom_bom as b';
3534 $sql .= ' INNER JOIN ' . $this->db->prefix() . 'product as p ON b.fk_product = p.rowid';
3535 $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
3536 if (!empty($status)) {
3537 $sql .= ' AND status = ' . (int) $status;
3538 }
3539 if (!empty($type)) {
3540 $sql .= ' AND bomtype = ' . (int) $type;
3541 }
3542 if (!empty($TProducts)) {
3543 $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
3544 }
3545 if (!empty($limit)) {
3546 $sql .= ' LIMIT ' . (int) $limit;
3547 }
3548 $resql = $this->db->query($sql);
3549 if ($resql) {
3550 if ($showempty) {
3551 $out .= '<option value="-1"';
3552 if (empty($selected)) {
3553 $out .= ' selected';
3554 }
3555 $out .= '>&nbsp;</option>';
3556 }
3557 while ($obj = $this->db->fetch_object($resql)) {
3558 $out .= '<option value="' . $obj->rowid . '"';
3559 if ($obj->rowid == $selected) {
3560 $out .= 'selected';
3561 }
3562 $out .= '>' . $obj->ref . ' - ' . $obj->productLabel . ' - ' . $obj->bomLabel . '</option>';
3563 }
3564 } else {
3565 $error++;
3566 dol_print_error($this->db);
3567 }
3568 $out .= '</select>';
3569 if (empty($nooutput)) {
3570 print $out;
3571 } else {
3572 return $out;
3573 }
3574 }
3575
3576 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3577
3604 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)
3605 {
3606 // phpcs:enable
3607 global $langs;
3608 global $hookmanager;
3609
3610 $out = '';
3611 $outarray = array();
3612
3613 // Units
3614 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3615 $langs->load('other');
3616 }
3617
3618 $warehouseStatusArray = array();
3619 if (!empty($warehouseStatus)) {
3620 require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
3621 if (preg_match('/warehouseclosed/', $warehouseStatus)) {
3622 $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
3623 }
3624 if (preg_match('/warehouseopen/', $warehouseStatus)) {
3625 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
3626 }
3627 if (preg_match('/warehouseinternal/', $warehouseStatus)) {
3628 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
3629 }
3630 }
3631
3632 $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";
3633 if (count($warehouseStatusArray)) {
3634 $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
3635 } else {
3636 $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", '0', "p.stock") . " AS stock";
3637 }
3638
3639 $sql = "SELECT ";
3640
3641 // Add select from hooks
3642 $parameters = array();
3643 $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
3644 if (empty($reshook)) {
3645 $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
3646 } else {
3647 $sql .= $hookmanager->resPrint;
3648 }
3649
3650 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3651 // Take randomly the first category of product to allow a sort on it. Bugged feature !
3652 $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
3653 FROM " . $this->db->prefix() . "categorie_product
3654 WHERE " . $this->db->prefix() . "categorie_product.fk_product = p.rowid
3655 LIMIT 1
3656 ) AS categorie_product_id";
3657 }
3658
3659 // Price by customer
3660 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3661 $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
3662 $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';
3663 $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref, custdiscount_percent";
3664 }
3665 // Units
3666 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3667 $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";
3668 $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';
3669 }
3670
3671 // Multilang : we add translation
3672 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3673 $sql .= ", pl.label as label_translated";
3674 $sql .= ", pl.description as description_translated";
3675 $selectFields .= ", label_translated";
3676 $selectFields .= ", description_translated";
3677 }
3678 // Price by quantity
3679 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3680 $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
3681 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3682 $sql .= " AND price_level = " . ((int) $price_level);
3683 }
3684 $sql .= " ORDER BY date_price";
3685 $sql .= " DESC LIMIT 1) as price_rowid";
3686 $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
3687 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3688 $sql .= " AND price_level = " . ((int) $price_level);
3689 }
3690 $sql .= " ORDER BY date_price";
3691 $sql .= " DESC LIMIT 1) as price_by_qty";
3692 $selectFields .= ", price_rowid, price_by_qty";
3693 }
3694
3695 //$sqlfields = $sql; // $sql fields to remove for count total
3696
3697 $sql .= " FROM ".$this->db->prefix()."product as p";
3698
3699 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
3700 $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
3701 }
3702
3703 // Add from (left join) from hooks
3704 $parameters = array(
3705 'socid' => $socid,
3706 );
3707 $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
3708 $sql .= $hookmanager->resPrint;
3709
3710 if (count($warehouseStatusArray)) {
3711 // Return line if product is inside the selected stock. If not, e.* and p.* will be null so we will count 0.
3712 // Replace this with a AND EXISTS ? Not possible as we need the ps.reel field for the SUM or 0 if no link.
3713 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps ON ps.fk_product = p.rowid";
3714 $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e ON ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
3715 $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')';
3716 }
3717
3718 // Price by customer (Add field pcp for the older price for couple product/thirdparty.
3719 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3720 $now = dol_now();
3721 $sql .= " LEFT JOIN (";
3722 $sql .= " SELECT pcp1.*";
3723 $sql .= " FROM " . $this->db->prefix() . "product_customer_price AS pcp1";
3724 $sql .= " LEFT JOIN (";
3725 $sql .= " SELECT fk_soc, fk_product, MIN(date_begin) AS date_begin";
3726 $sql .= " FROM " . $this->db->prefix() . "product_customer_price";
3727 $sql .= " WHERE fk_soc = " . ((int) $socid);
3728 $sql .= " AND date_begin <= '" . $this->db->idate($now) . "'";
3729 $sql .= " AND (date_end IS NULL OR '" . $this->db->idate($now) . "' <= date_end)";
3730 $sql .= " GROUP BY fk_soc, fk_product";
3731 $sql .= " ) AS pcp2 ON pcp1.fk_soc = pcp2.fk_soc AND pcp1.fk_product = pcp2.fk_product AND pcp1.date_begin = pcp2.date_begin";
3732 $sql .= " WHERE pcp2.fk_soc IS NOT NULL";
3733 $sql .= " ) AS pcp ON pcp.fk_soc = " . ((int) $socid) . " AND pcp.fk_product = p.rowid";
3734 }
3735 // Units : we add unit properties with a link on the primary key of unit
3736 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3737 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units as u ON u.rowid = p.fk_unit";
3738 }
3739 // Multilang : we add translation fields with a link on unique key fk_product/lang.
3740 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3741 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid";
3742 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3743 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3744 $soc = new Societe($this->db);
3745 $result = $soc->fetch($socid);
3746 if ($result > 0 && !empty($soc->default_lang)) {
3747 $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3748 } else {
3749 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3750 }
3751 } else {
3752 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3753 }
3754 }
3755
3756 // Add WHERE conditions
3757 $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3758 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3759 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD_BUT_ALLOW_SEARCH_IN_EAN13')) {
3760 if (strlen($filterkey) != 13) {
3761 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3762 }
3763 } else {
3764 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3765 }
3766 }
3767 if ($finished == 0) {
3768 $sql .= " AND p.finished = " . ((int) $finished);
3769 } elseif ($finished == 1) {
3770 $sql .= " AND p.finished = ".((int) $finished);
3771 }
3772 if ($status >= 0) {
3773 $sql .= " AND p.tosell = ".((int) $status);
3774 }
3775 if ($status_purchase >= 0) {
3776 $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3777 }
3778 // Filter by product type
3779 if (strval($filtertype) != '') {
3780 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3781 } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3782 $sql .= " AND p.fk_product_type = 1";
3783 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3784 $sql .= " AND p.fk_product_type = 0";
3785 }
3786
3787 if ((int) $warehouseId > 0) {
3788 $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)";
3789 }
3790
3791 // Add where from hooks
3792 $parameters = array(
3793 'filterkey' => &$filterkey,
3794 'socid' => $socid,
3795 );
3796 $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3797 $sql .= $hookmanager->resPrint;
3798 // Add criteria on ref/label
3799 if ($filterkey != '') {
3800 $sqlSupplierSearch = '';
3801
3802 $sql .= ' AND (';
3803 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3804 // For natural search
3805 $search_crit = explode(' ', $filterkey);
3806 $i = 0;
3807 if (count($search_crit) > 1) {
3808 $sql .= "(";
3809 }
3810 foreach ($search_crit as $crit) {
3811 if ($i > 0) {
3812 $sql .= " AND ";
3813 }
3814 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3815 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3816 $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3817 }
3818 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3819 $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3820 }
3821 if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3822 $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3823 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3824 $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3825 }
3826 }
3827
3828 // include search in supplier ref
3829 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3830 $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' AND ' : '';
3831 $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3832 }
3833 $sql .= ")";
3834 $i++;
3835 }
3836 if (count($search_crit) > 1) {
3837 $sql .= ")";
3838 }
3839 if (isModEnabled('barcode')) {
3840 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3841 }
3842
3843 // include search in supplier ref
3844 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3845 $sql .= " OR EXISTS (SELECT pfp.fk_product FROM " . $this->db->prefix() . "product_fournisseur_price as pfp WHERE p.rowid = pfp.fk_product";
3846 $sql .= " AND (";
3847 $sql .= $sqlSupplierSearch;
3848 $sql .= "))";
3849 }
3850
3851 $sql .= ')';
3852 }
3853 if (count($warehouseStatusArray)) {
3854 $sql .= " GROUP BY " . $this->db->sanitize($selectFields, 0, 0, 1); // To have the SUM on ps.reel working in the select.
3855 }
3856
3857 // Sort by category
3858 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3859 $sql .= " ORDER BY categorie_product_id ".(getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1 ? "ASC" : "DESC");
3860 } else {
3861 $sql .= $this->db->order("p.ref");
3862 }
3863
3864 $limit = getDolGlobalInt('SEARCH_LIMIT_AJAX') ?: $limit; // SEARCH_LIMIT_AJAX is a hidden option that has priority on visible option PRODUIT_LIMIT_SIZE if set.
3865 $sql .= $this->db->plimit($limit, 0);
3866
3867 /* The fast and low memory method to get and count full list converts the sql into a sql count */
3868 /*
3869 $nbtotalofrecords = 0;
3870 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
3871 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
3872
3873 $resql = $this->db->query($sqlforcount);
3874 if ($resql) {
3875 $objforcount = $this->db->fetch_object($resql);
3876 $nbtotalofrecords = $objforcount->nbtotalofrecords;
3877 } else {
3878 dol_print_error($this->db);
3879 }
3880 */
3881
3882 // Build output string
3883 dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
3884
3885 // If we have no $limit parameter, this request may hang dur to high number of lines returned.
3886 // This should not happen because this method should not be called directly, iIt is called by select_produit() that always add a $limit parameter.
3887 $result = $this->db->query($sql);
3888
3889 if ($result) {
3890 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3891 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3892 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3893
3894 $num = $this->db->num_rows($result);
3895
3896 $events = array();
3897
3898 if (!$forcecombo) {
3899 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3900 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3901 }
3902
3903 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3904
3905 $textifempty = '';
3906 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3907 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3908 if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3909 if ($showempty && !is_numeric($showempty)) {
3910 $textifempty = $langs->trans($showempty);
3911 } else {
3912 $textifempty .= $langs->trans("All");
3913 }
3914 } else {
3915 if ($showempty && !is_numeric($showempty)) {
3916 $textifempty = $langs->trans($showempty);
3917 }
3918 }
3919 if ($showempty) {
3920 $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3921 }
3922
3923 $i = 0;
3924 while ($num && $i < $num) {
3925 $opt = '';
3926 $optJson = array();
3927 $objp = $this->db->fetch_object($result);
3928
3929 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
3930 $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3931 $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3932 $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3933 $sql .= " ORDER BY quantity ASC";
3934
3935 dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3936 $result2 = $this->db->query($sql);
3937 if ($result2) {
3938 $nb_prices = $this->db->num_rows($result2);
3939 $j = 0;
3940 while ($nb_prices && $j < $nb_prices) {
3941 $objp2 = $this->db->fetch_object($result2);
3942
3943 $objp->price_by_qty_rowid = $objp2->rowid;
3944 $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3945 $objp->price_by_qty_quantity = $objp2->quantity;
3946 $objp->price_by_qty_unitprice = $objp2->unitprice;
3947 $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3948 // For backward compatibility
3949 $objp->quantity = $objp2->quantity;
3950 $objp->price = $objp2->price;
3951 $objp->unitprice = $objp2->unitprice;
3952 $objp->remise_percent = $objp2->remise_percent;
3953
3954 //$objp->tva_tx is not overwritten by $objp2 value
3955 //$objp->default_vat_code is not overwritten by $objp2 value
3956
3957 $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3958 '@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';
3959 $j++;
3960
3961 // Add new entry
3962 // "key" value of json key array is used by jQuery automatically as selected value
3963 // "label" value of json key array is used by jQuery automatically as text for combo box
3964 $out .= $opt;
3965 array_push($outarray, $optJson);
3966 }
3967 }
3968 } else {
3969 if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3970 $price_product = new Product($this->db);
3971 $price_product->fetch($objp->rowid, '', '', '1');
3972
3973 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3974 $priceparser = new PriceParser($this->db);
3975 $price_result = $priceparser->parseProduct($price_product);
3976 if ($price_result >= 0) {
3977 $objp->price = $price_result;
3978 $objp->unitprice = $price_result;
3979 //Calculate the VAT
3980 $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3981 $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3982 }
3983 }
3984 if (getDolGlobalInt('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES') && !empty($objp->custprice)) {
3985 $price_level = '';
3986 }
3987 $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
3988 // Add new entry
3989 // "key" value of json key array is used by jQuery automatically as selected value
3990 // "label" value of json key array is used by jQuery automatically as text for combo box
3991 $out .= $opt;
3992 array_push($outarray, $optJson);
3993 }
3994
3995 $i++;
3996 }
3997
3998 $out .= '</select>';
3999
4000 $this->db->free($result);
4001
4002 if (empty($outputmode)) {
4003 return $out;
4004 }
4005
4006 return $outarray;
4007 } else {
4008 dol_print_error($this->db);
4009 }
4010
4011 return '';
4012 }
4013
4029 protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
4030 {
4031 global $langs, $conf, $user;
4032 global $hookmanager;
4033
4034 $outkey = '';
4035 $outval = '';
4036 $outref = '';
4037 $outlabel = '';
4038 $outlabel_translated = '';
4039 $outdesc = '';
4040 $outdesc_translated = '';
4041 $outbarcode = '';
4042 $outorigin = '';
4043 $outtype = '';
4044 $outprice_ht = '';
4045 $outprice_ttc = '';
4046 $outpricebasetype = '';
4047 $outtva_tx = '';
4048 $outdefault_vat_code = '';
4049 $outqty = 1;
4050 $outdiscount = '0';
4051
4052 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4053
4054 $productlabel = $objp->label;
4055 if (!empty($objp->label_translated)) {
4056 $productlabel = $objp->label_translated;
4057 }
4058 $label = $productlabel;
4059 if (!empty($filterkey) && $filterkey != '') {
4060 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4061 }
4062
4063 $outkey = $objp->rowid;
4064 $outref = $objp->ref;
4065 $outrefcust = empty($objp->custref) ? '' : $objp->custref;
4066 $outlabel = $objp->label;
4067 $outdesc = $objp->description;
4068 if (getDolGlobalInt('MAIN_MULTILANGS')) {
4069 $outlabel_translated = $objp->label_translated;
4070 $outdesc_translated = $objp->description_translated;
4071 }
4072 $outbarcode = $objp->barcode;
4073 $outorigin = $objp->fk_country;
4074 $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
4075
4076 $outtype = $objp->fk_product_type;
4077 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4078 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4079
4080 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4081 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
4082 }
4083
4084 // Units
4085 $outvalUnits = '';
4086 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4087 if (!empty($objp->unit_short)) {
4088 $outvalUnits .= ' - ' . $objp->unit_short;
4089 }
4090 }
4091 if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
4092 if (!empty($objp->weight) && $objp->weight_units !== null) {
4093 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4094 $outvalUnits .= ' - ' . $unitToShow;
4095 }
4096 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4097 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4098 $outvalUnits .= ' - ' . $unitToShow;
4099 }
4100 if (!empty($objp->surface) && $objp->surface_units !== null) {
4101 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4102 $outvalUnits .= ' - ' . $unitToShow;
4103 }
4104 if (!empty($objp->volume) && $objp->volume_units !== null) {
4105 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4106 $outvalUnits .= ' - ' . $unitToShow;
4107 }
4108 }
4109 if ($outdurationvalue && $outdurationunit) {
4110 $da = array(
4111 'h' => $langs->trans('Hour'),
4112 'd' => $langs->trans('Day'),
4113 'w' => $langs->trans('Week'),
4114 'm' => $langs->trans('Month'),
4115 'y' => $langs->trans('Year')
4116 );
4117 if (isset($da[$outdurationunit])) {
4118 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4119 }
4120 }
4121
4122 // Set stocktag (stock too low or not or unknown)
4123 $stocktag = 0;
4124 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4125 if ($user->hasRight('stock', 'lire')) {
4126 if ($objp->stock > 0) {
4127 $stocktag = 1;
4128 } elseif ($objp->stock <= 0) {
4129 $stocktag = -1;
4130 }
4131 }
4132 }
4133
4134 // Set full plain label for the native <option> text. Select2 uses this text
4135 // as its search corpus, while data-html below keeps the visible label short.
4136 $labeltosearch = '';
4137 $labeltosearch .= $objp->ref;
4138 if (!empty($objp->custref)) {
4139 $labeltosearch .= ' (' . $objp->custref . ')';
4140 }
4141 if ($outbarcode) {
4142 $labeltosearch .= ' (' . $outbarcode . ')';
4143 }
4144 $labeltosearch .= ' - ' . $productlabel;
4145 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4146 $labeltosearch .= ' (' . getCountry($outorigin, '1') . ')';
4147 }
4148
4149 // Set $labltoshowhtml
4150 $labeltoshowhtml = '';
4151 $labeltoshowhtml .= $objp->ref;
4152 if (!empty($objp->custref)) {
4153 $labeltoshowhtml .= ' (' . $objp->custref . ')';
4154 }
4155 if (!empty($filterkey) && $filterkey != '') {
4156 $labeltoshowhtml = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $labeltoshowhtml, 1);
4157 }
4158 if ($outbarcode) {
4159 $labeltoshowhtml .= ' (' . $outbarcode . ')';
4160 }
4161 $labeltoshowhtml .= ' - ' . dol_trunc($label, $maxlengtharticle);
4162 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4163 $labeltoshowhtml .= ' (' . getCountry($outorigin, '1') . ')';
4164 }
4165
4166 // Stock
4167 $labeltoshowstock = '';
4168 $labeltoshowhtmlstock = '';
4169 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4170 if ($user->hasRight('stock', 'lire')) {
4171 $labeltoshowstock .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4172
4173 if ($objp->stock > 0) {
4174 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_ok">';
4175 } elseif ($objp->stock <= 0) {
4176 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_too_low">';
4177 }
4178 $labeltoshowhtmlstock .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4179 $labeltoshowhtmlstock .= '</span>';
4180
4181 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4182 $langs->load("stocks");
4183
4184 $tmpproduct = new Product($this->db);
4185 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4186 $tmpproduct->load_virtual_stock();
4187 $virtualstock = $tmpproduct->stock_theorique;
4188
4189 $labeltoshowstock .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4190
4191 $labeltoshowhtmlstock .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4192 if ($virtualstock > 0) {
4193 $labeltoshowhtmlstock .= '<span class="product_line_stock_ok">';
4194 } elseif ($virtualstock <= 0) {
4195 $labeltoshowhtmlstock .= '<span class="product_line_stock_too_low">';
4196 }
4197 $labeltoshowhtmlstock .= $virtualstock;
4198 $labeltoshowhtmlstock .= '</span>';
4199
4200 unset($tmpproduct);
4201 }
4202 }
4203 }
4204
4205 // Price
4206 $found = 0;
4207 $labeltoshowprice = '';
4208 $labeltoshowhtmlprice = '';
4209 // If we need a particular price level (from 1 to n)
4210 if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4211 $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
4212 $sql .= " FROM " . $this->db->prefix() . "product_price";
4213 $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
4214 $sql .= " AND entity IN (" . getEntity('productprice') . ")";
4215 $sql .= " AND price_level = " . ((int) $price_level);
4216 $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
4217 $sql .= " LIMIT 1";
4218
4219 dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
4220 $result2 = $this->db->query($sql);
4221 if ($result2) {
4222 $objp2 = $this->db->fetch_object($result2);
4223 if ($objp2) {
4224 $found = 1;
4225 if ($objp2->price_base_type == 'HT') {
4226 $labeltoshowprice .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4227 $labeltoshowhtmlprice .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4228 } else {
4229 $labeltoshowprice .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4230 $labeltoshowhtmlprice .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4231 }
4232 $outprice_ht = price($objp2->price);
4233 $outprice_ttc = price($objp2->price_ttc);
4234 $outpricebasetype = $objp2->price_base_type;
4235 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
4236 $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
4237 $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
4238 } else {
4239 $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
4240 $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
4241 }
4242 }
4243 } else {
4244 dol_print_error($this->db);
4245 }
4246 }
4247
4248 // Price by quantity
4249 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
4250 $found = 1;
4251 $outqty = $objp->quantity;
4252 $outdiscount = $objp->remise_percent;
4253 if ($objp->quantity == 1) {
4254 $labeltoshowprice .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
4255 $labeltoshowhtmlprice .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
4256 $labeltoshowprice .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4257 $labeltoshowhtmlprice .= $langs->transnoentities("Unit");
4258 } else {
4259 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4260 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4261 $labeltoshowprice .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4262 $labeltoshowhtmlprice .= $langs->transnoentities("Units");
4263 }
4264
4265 $outprice_ht = price($objp->unitprice);
4266 $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
4267 $outpricebasetype = $objp->price_base_type;
4268 $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
4269 $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
4270 }
4271 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
4272 $labeltoshowprice .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4273 $labeltoshowhtmlprice .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4274 }
4275 if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
4276 $labeltoshowprice .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4277 $labeltoshowhtmlprice .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4278 }
4279
4280 // Price by customer
4281 if (empty($hidepriceinlabel) && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4282 if (!empty($objp->idprodcustprice)) {
4283 $found = 1;
4284
4285 if ($objp->custprice_base_type == 'HT') {
4286 $labeltoshowprice .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4287 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4288 } else {
4289 $labeltoshowprice .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4290 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4291 }
4292
4293 $outprice_ht = price($objp->custprice);
4294 $outprice_ttc = price($objp->custprice_ttc);
4295 $outpricebasetype = $objp->custprice_base_type;
4296 $outtva_tx = $objp->custtva_tx;
4297 $outdefault_vat_code = $objp->custdefault_vat_code;
4298 $outdiscount = $objp->custdiscount_percent;
4299 }
4300 }
4301
4302 // If level no defined or multiprice not found, we used the default price
4303 if (empty($hidepriceinlabel) && !$found) {
4304 if ($objp->price_base_type == 'HT') {
4305 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4306 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4307 } else {
4308 $labeltoshowprice .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4309 $labeltoshowhtmlprice .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4310 }
4311 $outprice_ht = price($objp->price);
4312 $outprice_ttc = price($objp->price_ttc);
4313 $outpricebasetype = $objp->price_base_type;
4314 $outtva_tx = $objp->tva_tx;
4315 $outdefault_vat_code = $objp->default_vat_code;
4316 }
4317
4318 $optiontext = $labeltosearch.$outvalUnits.$labeltoshowprice.$labeltoshowstock;
4319 $optionhtml = $labeltoshowhtml.$outvalUnits.$labeltoshowhtmlprice.$labeltoshowhtmlstock;
4320 $optionhtmlforattribute = dol_escape_htmltag($optionhtml, 0, 0, '', 0, 1);
4321
4322 // Build options
4323 $opt = '<option value="' . $objp->rowid . '"';
4324 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
4325 if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
4326 $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 . '"';
4327 }
4328 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
4329 $opt .= ' data-labeltrans="' . dol_escape_htmltag($outlabel_translated, 0, 0, '', 0, 1) . '"';
4330 $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
4331 }
4332
4333 if ($stocktag == 1) {
4334 $opt .= ' class="product_line_stock_ok" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4335 //$opt .= ' class="product_line_stock_ok"';
4336 }
4337 if ($stocktag == -1) {
4338 $opt .= ' class="product_line_stock_too_low" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4339 //$opt .= ' class="product_line_stock_too_low"';
4340 }
4341 $opt .= ' data-html="'.$optionhtmlforattribute.'" data-select-html="'.$optionhtmlforattribute.'"';
4342
4343 $opt .= '>';
4344
4345 // Ref, barcode, country
4346 $opt .= dol_escape_htmltag($optiontext, 0, 0, '', 0, 1);
4347 $outval .= $labeltoshowhtml;
4348
4349 // Units
4350 $outval .= $outvalUnits;
4351
4352 // Price
4353 $outval .= $labeltoshowhtmlprice;
4354
4355 // Stock
4356 $outval .= $labeltoshowhtmlstock;
4357
4358
4359 $parameters = array('objp' => $objp);
4360 $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
4361 if (empty($reshook)) {
4362 $opt .= $hookmanager->resPrint;
4363 } else {
4364 $opt = $hookmanager->resPrint;
4365 }
4366
4367 $opt .= "</option>\n";
4368 $optJson = array(
4369 'key' => $outkey,
4370 'value' => $outref,
4371 'label' => $outval,
4372 'label2' => $outlabel,
4373 'desc' => $outdesc,
4374 'type' => $outtype,
4375 'price_ht' => price2num($outprice_ht),
4376 'price_ttc' => price2num($outprice_ttc),
4377 'price_ht_locale' => price(price2num($outprice_ht)),
4378 'price_ttc_locale' => price(price2num($outprice_ttc)),
4379 'pricebasetype' => $outpricebasetype,
4380 'tva_tx' => $outtva_tx,
4381 'default_vat_code' => $outdefault_vat_code,
4382 'qty' => $outqty,
4383 'discount' => $outdiscount,
4384 'duration_value' => $outdurationvalue,
4385 'duration_unit' => $outdurationunit,
4386 'pbq' => $outpbq,
4387 'labeltrans' => $outlabel_translated,
4388 'desctrans' => $outdesc_translated,
4389 'ref_customer' => $outrefcust
4390 );
4391 }
4392
4393 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4394
4411 public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $notused = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '', $nooutput = 0)
4412 {
4413 // phpcs:enable
4414 global $langs, $conf;
4415 global $price_level, $status, $finished;
4416
4417 if (!isset($status)) {
4418 $status = 1;
4419 }
4420
4421 $selected_input_value = '';
4422 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
4423 if ((int) $selected > 0) {
4424 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4425 $producttmpselect = new Product($this->db);
4426 $producttmpselect->fetch((int) $selected);
4427 $selected_input_value = $producttmpselect->ref;
4428 unset($producttmpselect);
4429 } elseif (preg_match('/^idprod_([0-9]+)$/', (string) $selected, $regtmpsel)) {
4430 // Preselect when a product without supplier price was just created ('idprod_ID' value, used by backtopage of creation popup)
4431 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4432 $producttmpselect = new Product($this->db);
4433 $producttmpselect->fetch((int) $regtmpsel[1]);
4434 $selected_input_value = $producttmpselect->ref;
4435 unset($producttmpselect);
4436 }
4437
4438 // mode=2 means suppliers products
4439 $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
4440
4441 $s = ($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="'.$morecss.'" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
4442
4443 $s .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalInt('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
4444 } else {
4445 $s = $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $notused, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, getDolGlobalInt('SUPPLIER_SHOW_STOCK_IN_PRODUCTS_COMBO'), $placeholder);
4446 }
4447
4448 if ($nooutput) {
4449 return $s;
4450 } else {
4451 print $s;
4452 }
4453 }
4454
4455 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4456
4475 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 = '')
4476 {
4477 // phpcs:enable
4478 global $langs, $conf, $user;
4479 global $hookmanager;
4480
4481 $out = '';
4482 $outarray = array();
4483
4484 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4485
4486 $langs->load('stocks');
4487 // Units
4488 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4489 $langs->load('other');
4490 }
4491
4492 $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,";
4493 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
4494 $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
4495 $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
4496 $sql .= ", pfp.supplier_reputation";
4497 // if we use supplier description of the products
4498 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4499 $sql .= ", pfp.desc_fourn as description";
4500 } else {
4501 $sql .= ", p.description";
4502 }
4503 // Units
4504 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4505 $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";
4506 }
4507
4508 // Add select from hooks
4509 $parameters = [];
4510 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
4511 $sql .= $hookmanager->resPrint;
4512
4513 $sql .= " FROM " . $this->db->prefix() . "product as p";
4514
4515 // Add join from hooks
4516 $parameters = [];
4517 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
4518 $sql .= $hookmanager->resPrint;
4519
4520 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
4521 if ($socid > 0) {
4522 $sql .= " AND pfp.fk_soc = " . ((int) $socid);
4523 }
4524 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4525 // Units
4526 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4527 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
4528 }
4529 $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
4530 if ($statut != -1) {
4531 $sql .= " AND p.tobuy = " . ((int) $statut);
4532 }
4533 if (strval($filtertype) != '') {
4534 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
4535 }
4536
4537 // Add where from hooks
4538 $parameters = array();
4539 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
4540 $sql .= $hookmanager->resPrint;
4541 // Add criteria on ref/label
4542 if ($filterkey != '') {
4543 $sql .= ' AND (';
4544 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
4545 // For natural search
4546 $search_crit = explode(' ', $filterkey);
4547 $i = 0;
4548 if (count($search_crit) > 1) {
4549 $sql .= "(";
4550 }
4551 foreach ($search_crit as $crit) {
4552 if ($i > 0) {
4553 $sql .= " AND ";
4554 }
4555 $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) . "%'";
4556 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4557 $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
4558 }
4559 $sql .= ")";
4560 $i++;
4561 }
4562 if (count($search_crit) > 1) {
4563 $sql .= ")";
4564 }
4565 if (isModEnabled('barcode')) {
4566 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4567 $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4568 }
4569 $sql .= ')';
4570 }
4571 $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
4572 $sql .= $this->db->plimit($limit, 0);
4573
4574 // Build output string
4575
4576 dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
4577 $result = $this->db->query($sql);
4578 if ($result) {
4579 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4580 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
4581
4582 $num = $this->db->num_rows($result);
4583
4584 //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
4585 $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
4586 if (!$selected) {
4587 $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4588 } else {
4589 $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4590 }
4591
4592 $i = 0;
4593 while ($i < $num) {
4594 $objp = $this->db->fetch_object($result);
4595
4596 if (is_null($objp->idprodfournprice)) {
4597 // There is no supplier price found, we will use the vat rate for sale
4598 $objp->tva_tx = $objp->tva_tx_sale;
4599 $objp->default_vat_code = $objp->default_vat_code_sale;
4600 }
4601
4602 $outkey = $objp->idprodfournprice; // id in table of price
4603 if (!$outkey && $alsoproductwithnosupplierprice) {
4604 $outkey = 'idprod_' . $objp->rowid; // id of product
4605 }
4606
4607 $outref = $objp->ref;
4608 $outbarcode = $objp->barcode;
4609 $outqty = 1;
4610 $outdiscount = 0;
4611 $outtype = $objp->fk_product_type;
4612 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4613 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4614
4615 // Units
4616 $outvalUnits = '';
4617 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4618 if (!empty($objp->unit_short)) {
4619 $outvalUnits .= ' - ' . $objp->unit_short;
4620 }
4621 if (!empty($objp->weight) && $objp->weight_units !== null) {
4622 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4623 $outvalUnits .= ' - ' . $unitToShow;
4624 }
4625 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4626 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4627 $outvalUnits .= ' - ' . $unitToShow;
4628 }
4629 if (!empty($objp->surface) && $objp->surface_units !== null) {
4630 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4631 $outvalUnits .= ' - ' . $unitToShow;
4632 }
4633 if (!empty($objp->volume) && $objp->volume_units !== null) {
4634 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4635 $outvalUnits .= ' - ' . $unitToShow;
4636 }
4637 if ($outdurationvalue && $outdurationunit) {
4638 $da = array(
4639 'h' => $langs->trans('Hour'),
4640 'd' => $langs->trans('Day'),
4641 'w' => $langs->trans('Week'),
4642 'm' => $langs->trans('Month'),
4643 'y' => $langs->trans('Year')
4644 );
4645 if (isset($da[$outdurationunit])) {
4646 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4647 }
4648 }
4649 }
4650
4651 $objRef = $objp->ref;
4652 if ($filterkey && $filterkey != '') {
4653 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
4654 }
4655 $objRefFourn = $objp->ref_fourn;
4656 if ($filterkey && $filterkey != '') {
4657 $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
4658 }
4659 $label = $objp->label;
4660 if ($filterkey && $filterkey != '') {
4661 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4662 }
4663
4664 switch ($objp->fk_product_type) {
4666 $picto = 'product';
4667 break;
4669 $picto = 'service';
4670 break;
4671 default:
4672 $picto = '';
4673 break;
4674 }
4675
4676 if (empty($picto)) {
4677 $optlabel = '';
4678 } else {
4679 $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
4680 }
4681
4682 $optlabel .= $objp->ref;
4683 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4684 $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
4685 }
4686 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4687 $optlabel .= ' (' . $outbarcode . ')';
4688 }
4689 $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4690
4691 $outvallabel = $objRef;
4692 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4693 $outvallabel .= ' (' . $objRefFourn . ')';
4694 }
4695 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4696 $outvallabel .= ' (' . $outbarcode . ')';
4697 }
4698 $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4699
4700 $outsearchlabel = implode(' ', array_filter(array(
4701 (string) $objp->ref,
4702 (string) $objp->ref_fourn,
4703 (string) $objp->barcode,
4704 (string) $objp->label,
4705 dol_string_nohtmltag((string) $objp->description)
4706 ), function (string $value): bool {
4707 return $value !== '';
4708 }));
4709
4710 // Units
4711 $optlabel .= $outvalUnits;
4712 $outvallabel .= $outvalUnits;
4713
4714 if (!empty($objp->idprodfournprice)) {
4715 $outqty = $objp->quantity;
4716 $outdiscount = $objp->remise_percent;
4717 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4718 $prod_supplier = new ProductFournisseur($this->db);
4719 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4720 $prod_supplier->id = $objp->fk_product;
4721 $prod_supplier->fourn_qty = $objp->quantity;
4722 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4723 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4724
4725 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4726 $priceparser = new PriceParser($this->db);
4727 $price_result = $priceparser->parseProductSupplier($prod_supplier);
4728 if ($price_result >= 0) {
4729 $objp->fprice = $price_result;
4730 if ($objp->quantity >= 1) {
4731 $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
4732 }
4733 }
4734 }
4735 if ($objp->quantity == 1) {
4736 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4737 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
4738 $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4739 $outvallabel .= $langs->transnoentities("Unit");
4740 } else {
4741 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4742 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4743 $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4744 $outvallabel .= ' ' . $langs->transnoentities("Units");
4745 }
4746
4747 if ($objp->quantity != 1) {
4748 $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
4749 $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
4750 }
4751 if ($objp->remise_percent >= 1) {
4752 $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4753 $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4754 }
4755 if ($objp->duration) {
4756 $optlabel .= " - " . $objp->duration;
4757 $outvallabel .= " - " . $objp->duration;
4758 }
4759 if (!$socid) {
4760 $optlabel .= " - " . dol_trunc($objp->name, 8);
4761 $outvallabel .= " - " . dol_trunc($objp->name, 8);
4762 }
4763 if ($objp->supplier_reputation) {
4764 //TODO dictionary
4765 $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
4766
4767 $optlabel .= " - " . $reputations[$objp->supplier_reputation];
4768 $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
4769 }
4770 } else {
4771 $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
4772 $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
4773 }
4774
4775 if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4776 $novirtualstock = ($showstockinlist == 2);
4777
4778 if ($user->hasRight('stock', 'lire')) {
4779 $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4780
4781 if ($objp->stock > 0) {
4782 $optlabel .= ' - <span class="product_line_stock_ok">';
4783 } elseif ($objp->stock <= 0) {
4784 $optlabel .= ' - <span class="product_line_stock_too_low">';
4785 }
4786 $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
4787 $optlabel .= '</span>';
4788 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4789 $langs->load("stocks");
4790
4791 $tmpproduct = new Product($this->db);
4792 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4793 $tmpproduct->load_virtual_stock();
4794 $virtualstock = $tmpproduct->stock_theorique;
4795
4796 $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4797
4798 $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4799 if ($virtualstock > 0) {
4800 $optlabel .= '<span class="product_line_stock_ok">';
4801 } elseif ($virtualstock <= 0) {
4802 $optlabel .= '<span class="product_line_stock_too_low">';
4803 }
4804 $optlabel .= $virtualstock;
4805 $optlabel .= '</span>';
4806
4807 unset($tmpproduct);
4808 }
4809 }
4810 }
4811
4812 $optstart = '<option value="' . $outkey . '"';
4813 if ($selected && preg_match('/^idprod_/', (string) $selected) && (string) $selected == 'idprod_'.$objp->rowid) {
4814 $optstart .= ' selected';
4815 } elseif ($selected && (string) $selected == (string) $objp->idprodfournprice) {
4816 $optstart .= ' selected';
4817 }
4818
4819 if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
4820 $optstart .= ' disabled';
4821 }
4822
4823 if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
4824 $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
4825 $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
4826 $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
4827 $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"'; // the price with numeric international format
4828 $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"'; // the price formatted in user language
4829 $optstart .= ' data-discount="' . dol_escape_htmltag((string) $outdiscount) . '"';
4830 $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"'; // the rate with numeric international format
4831 $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"'; // the rate formatted in user language
4832 $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
4833 $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
4834 if (isModEnabled('multicurrency')) {
4835 $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
4836 $optstart .= ' data-multicurrency-unitprice="' . dol_escape_htmltag(price2num($objp->multicurrency_unitprice)) . '"'; // the price with numeric international format
4837 }
4838 }
4839 $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
4840 $optstart .= ' data-search="' . dol_escape_htmltag($outsearchlabel) . '"';
4841
4842 // set $parameters to call hook
4843 $outarrayentry = array(
4844 'key' => $outkey,
4845 'value' => $outref,
4846 'label' => $outvallabel,
4847 'labelhtml' => $optlabel,
4848 'qty' => $outqty,
4849 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4850 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4851 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4852 'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
4853 'tva_tx' => price2num($objp->tva_tx),
4854 'default_vat_code' => $objp->default_vat_code,
4855 'supplier_ref' => $objp->ref_fourn,
4856 'discount' => $outdiscount,
4857 'type' => $outtype,
4858 'duration_value' => $outdurationvalue,
4859 'duration_unit' => $outdurationunit,
4860 'disabled' => empty($objp->idprodfournprice),
4861 'description' => $objp->description
4862 );
4863 if (isModEnabled('multicurrency')) {
4864 $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4865 $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4866 }
4867 $parameters = array(
4868 'objp' => &$objp,
4869 'optstart' => &$optstart,
4870 'optlabel' => &$optlabel,
4871 'outvallabel' => &$outvallabel,
4872 'outarrayentry' => &$outarrayentry,
4873 'fk_soc' => $socid
4874 );
4875 $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4876
4877
4878 // Add new entry
4879 // "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
4880 // "label" value of json key array is used by jQuery automatically as text for combo box
4881 $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4882 $outarraypush = array(
4883 'key' => $outkey,
4884 'value' => $outref,
4885 'label' => $outvallabel,
4886 'labelhtml' => $optlabel,
4887 'qty' => $outqty,
4888 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4889 'price_qty_ht_locale' => price($objp->fprice),
4890 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4891 'price_unit_ht_locale' => price($objp->unitprice),
4892 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4893 'tva_tx_formated' => price($objp->tva_tx),
4894 'tva_tx' => price2num($objp->tva_tx),
4895 'default_vat_code' => $objp->default_vat_code,
4896 'supplier_ref' => $objp->ref_fourn,
4897 'discount' => $outdiscount,
4898 'type' => $outtype,
4899 'duration_value' => $outdurationvalue,
4900 'duration_unit' => $outdurationunit,
4901 'disabled' => empty($objp->idprodfournprice),
4902 'description' => $objp->description
4903 );
4904 if (isModEnabled('multicurrency')) {
4905 $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4906 $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4907 }
4908 array_push($outarray, $outarraypush);
4909
4910 // Example of var_dump $outarray
4911 // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4912 // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/unit (20,00 Euros/unit)"
4913 // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4914 //}
4915 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4916 //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unit (20,00 Euros/unit)');
4917 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4918
4919 $i++;
4920 }
4921 $out .= '</select>';
4922
4923 $this->db->free($result);
4924
4925 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4926 $out .= ajax_combobox($htmlname);
4927 } else {
4928 dol_print_error($this->db);
4929 }
4930
4931 if (empty($outputmode)) {
4932 return $out;
4933 }
4934 return $outarray;
4935 }
4936
4937 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4938
4947 public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4948 {
4949 // phpcs:enable
4950 global $langs, $conf;
4951
4952 $langs->load('stocks');
4953
4954 $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4955 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4956 $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4957 $sql .= " FROM " . $this->db->prefix() . "product as p";
4958 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4959 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4960 $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4961 $sql .= " AND p.tobuy = 1";
4962 $sql .= " AND s.fournisseur = 1";
4963 $sql .= " AND p.rowid = " . ((int) $productid);
4964 if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4965 $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4966 } else {
4967 $sql .= " ORDER BY pfp.unitprice - pfp.unitprice * pfp.remise_percent / 100 ASC";
4968 }
4969
4970 dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4971 $result = $this->db->query($sql);
4972
4973 if ($result) {
4974 $num = $this->db->num_rows($result);
4975
4976 $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4977
4978 if (!$num) {
4979 $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4980 } else {
4981 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4982 $form .= '<option value="0">&nbsp;</option>';
4983
4984 $i = 0;
4985 while ($i < $num) {
4986 $objp = $this->db->fetch_object($result);
4987
4988 $opt = '<option value="' . $objp->idprodfournprice . '"';
4989 //if there is only one supplier, preselect it
4990 if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
4991 $opt .= ' selected';
4992 }
4993 $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
4994
4995 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4996 $prod_supplier = new ProductFournisseur($this->db);
4997 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4998 $prod_supplier->id = $productid;
4999 $prod_supplier->fourn_qty = $objp->quantity;
5000 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
5001 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
5002
5003 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
5004 $priceparser = new PriceParser($this->db);
5005 $price_result = $priceparser->parseProductSupplier($prod_supplier);
5006 if ($price_result >= 0) {
5007 $objp->fprice = $price_result;
5008 if ($objp->quantity >= 1) {
5009 $objp->unitprice = $objp->fprice / $objp->quantity;
5010 }
5011 }
5012 }
5013 if ($objp->quantity == 1) {
5014 $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
5015 }
5016
5017 $opt .= $objp->quantity . ' ';
5018
5019 if ($objp->quantity == 1) {
5020 $opt .= $langs->trans("Unit");
5021 } else {
5022 $opt .= $langs->trans("Units");
5023 }
5024 if ($objp->quantity > 1) {
5025 $opt .= " - ";
5026 $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");
5027 }
5028 if ($objp->duration) {
5029 $opt .= " - " . $objp->duration;
5030 }
5031 $opt .= "</option>\n";
5032
5033 $form .= $opt;
5034 $i++;
5035 }
5036 }
5037
5038 $form .= '</select>';
5039 $this->db->free($result);
5040 return $form;
5041 } else {
5042 dol_print_error($this->db);
5043 return '';
5044 }
5045 }
5046
5047
5048 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5055 {
5056 // phpcs:enable
5057 global $langs, $hookmanager;
5058
5059 $num = count($this->cache_conditions_paiements);
5060 if ($num > 0) {
5061 return 0; // Cache already loaded
5062 }
5063
5064 dol_syslog(__METHOD__, LOG_DEBUG);
5065
5066 $this->cache_conditions_paiements = array();
5067
5068 $sql = "SELECT rowid, code, libelle as label, deposit_percent, entity";
5069 $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
5070 $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
5071 $sql .= " AND active > 0";
5072 $sql .= " ORDER BY sortorder";
5073
5074 $resql = $this->db->query($sql);
5075 if ($resql) {
5076 $num = $this->db->num_rows($resql);
5077 $i = 0;
5078 while ($i < $num) {
5079 $obj = $this->db->fetch_object($resql);
5080
5081 // If a translation exists, we use it, otherwise, we take the label by default
5082 $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5083
5084 $this->cache_conditions_paiements[$obj->rowid]['code'] = (string) $obj->code;
5085 $this->cache_conditions_paiements[$obj->rowid]['label'] = (string) $label;
5086 $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = (string) $obj->deposit_percent;
5087 $this->cache_conditions_paiements[$obj->rowid]['entity'] = (int) $obj->entity;
5088
5089 $i++;
5090 }
5091
5092 $parameters = array('context' => 'paymentterm');
5093 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5094 if (empty($reshook)) {
5095 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5096 $this->cache_conditions_paiements = array_merge($this->cache_conditions_paiements, $hookmanager->resArray);
5097 }
5098 } else {
5099 $this->cache_conditions_paiements = $hookmanager->resArray;
5100 }
5101
5102 //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
5103
5104 return $num;
5105 } else {
5106 dol_print_error($this->db);
5107 return -1;
5108 }
5109 }
5110
5111 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5112
5119 {
5120 // phpcs:enable
5121 $factureRec = new FactureRec($this->db);
5122
5123 $this->cache_rule_for_lines_dates = $factureRec->fields['rule_for_lines_dates']['arrayofkeyval'];
5124
5125 if (empty($this->cache_rule_for_lines_dates)) {
5126 return -1;
5127 }
5128
5129 return 1;
5130 }
5131
5132 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5133
5139 public function load_cache_availability()
5140 {
5141 // phpcs:enable
5142 global $langs;
5143
5144 $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
5145 if ($num > 0) {
5146 return 0; // Cache already loaded
5147 }
5148
5149 dol_syslog(__METHOD__, LOG_DEBUG);
5150
5151 $this->cache_availability = array();
5152
5153 $langs->load('propal');
5154
5155 $sql = "SELECT rowid, code, label, position";
5156 $sql .= " FROM " . $this->db->prefix() . 'c_availability';
5157 $sql .= " WHERE active > 0";
5158
5159 $resql = $this->db->query($sql);
5160 if ($resql) {
5161 $num = $this->db->num_rows($resql);
5162 $i = 0;
5163 while ($i < $num) {
5164 $obj = $this->db->fetch_object($resql);
5165
5166 // If a translation exists, we use is, otherwise, we take the label by default
5167 $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5168 $this->cache_availability[$obj->rowid]['code'] = (string) $obj->code;
5169 $this->cache_availability[$obj->rowid]['label'] = (string) $label;
5170 $this->cache_availability[$obj->rowid]['position'] = (int) $obj->position;
5171 $i++;
5172 }
5173
5174 // @phan-suppress-next-line PhanTypeMismatchProperty PhanTypeMismatchDimFetch
5175 $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
5176
5177 return $num;
5178 } else {
5179 dol_print_error($this->db);
5180 return -1;
5181 }
5182 }
5183
5195 public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '', $noouput = 0)
5196 {
5197 global $langs, $user;
5198
5199 $this->load_cache_availability();
5200
5201 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5202
5203 $out = '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5204 if ($addempty) {
5205 $out .= '<option value="-1">'.(is_numeric($addempty) ? '&nbsp;' : $langs->trans($addempty)).'</option>';
5206 }
5207 foreach ($this->cache_availability as $id => $arrayavailability) {
5208 if ($selected == $id) {
5209 $out .= '<option value="' . $id . '" selected>';
5210 } else {
5211 $out .= '<option value="' . $id . '">';
5212 }
5213 $out .= dol_escape_htmltag($arrayavailability['label']);
5214 $out .= '</option>';
5215 }
5216 $out .= '</select>';
5217 if ($user->admin) {
5218 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5219 }
5220 $out .= ajax_combobox($htmlname);
5221
5222 if ($noouput) {
5223 return $out;
5224 } else {
5225 print $out;
5226 return '';
5227 }
5228 }
5229
5235 public function loadCacheInputReason()
5236 {
5237 global $langs;
5238
5239 $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
5240 if ($num > 0) {
5241 return 0; // Cache already loaded
5242 }
5243
5244 $sql = "SELECT rowid, code, label";
5245 $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
5246 $sql .= " WHERE active > 0";
5247
5248 $resql = $this->db->query($sql);
5249 if ($resql) {
5250 $num = $this->db->num_rows($resql);
5251 $i = 0;
5253 $tmparray = array();
5254 while ($i < $num) {
5255 $obj = $this->db->fetch_object($resql);
5256
5257 // If a translation exists, we use is, otherwise, we take the label by default
5258 $label = ($obj->label != '-' ? (string) $obj->label : '');
5259 if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
5260 $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
5261 }
5262 if ($langs->trans($obj->code) != $obj->code) {
5263 $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
5264 }
5265
5266 $tmparray[(int) $obj->rowid]
5267 = array(
5268 'id' => (int) $obj->rowid,
5269 'code' => (string) $obj->code,
5270 'label' => $label,
5271 );
5272 $i++;
5273 }
5274
5275 $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
5276
5277 unset($tmparray);
5278 return $num;
5279 } else {
5280 dol_print_error($this->db);
5281 return -1;
5282 }
5283 }
5284
5297 public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
5298 {
5299 global $langs, $user;
5300
5301 $this->loadCacheInputReason();
5302
5303 print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5304 if ($addempty) {
5305 print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
5306 }
5307 foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
5308 if ($arraydemandreason['code'] == $exclude) {
5309 continue;
5310 }
5311
5312 if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
5313 print '<option value="' . $arraydemandreason['id'] . '" selected>';
5314 } else {
5315 print '<option value="' . $arraydemandreason['id'] . '">';
5316 }
5317 $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
5318 print $langs->trans($label);
5319 print '</option>';
5320 }
5321 print '</select>';
5322 if ($user->admin && empty($notooltip)) {
5323 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5324 }
5325 print ajax_combobox('select_' . $htmlname);
5326 }
5327
5328 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5329
5336 {
5337 // phpcs:enable
5338 global $langs, $hookmanager;
5339
5340 $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
5341 if ($num > 0) {
5342 return $num; // Cache already loaded
5343 }
5344
5345 dol_syslog(__METHOD__, LOG_DEBUG);
5346
5347 $this->cache_types_paiements = array();
5348
5349 $sql = "SELECT id, code, libelle as label, type, entity, active";
5350 $sql .= " FROM " . $this->db->prefix() . "c_paiement";
5351 $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
5352
5353 $resql = $this->db->query($sql);
5354 if ($resql) {
5355 $num = $this->db->num_rows($resql);
5356 $i = 0;
5357 while ($i < $num) {
5358 $obj = $this->db->fetch_object($resql);
5359
5360 // If a translation exists, we use is, otherwise, we take the label by default
5361 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5362 $this->cache_types_paiements[$obj->id]['id'] = (int) $obj->id;
5363 $this->cache_types_paiements[$obj->id]['code'] = (string) $obj->code;
5364 $this->cache_types_paiements[$obj->id]['label'] = (string) $label;
5365 $this->cache_types_paiements[$obj->id]['type'] = (int) $obj->type;
5366 $this->cache_types_paiements[$obj->id]['entity'] = (int) $obj->entity;
5367 $this->cache_types_paiements[$obj->id]['active'] = (int) $obj->active;
5368 $i++;
5369 }
5370
5371 $parameters = array('context' => 'paymenttype');
5372 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5373 if (empty($reshook)) {
5374 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5375 $this->cache_types_paiements = array_merge($this->cache_types_paiements, $hookmanager->resArray);
5376 }
5377 } else {
5378 $this->cache_types_paiements = $hookmanager->resArray;
5379 }
5380
5381 $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
5382
5383 return $num;
5384 } else {
5385 dol_print_error($this->db);
5386 return -1;
5387 }
5388 }
5389
5390
5391 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5392
5411 public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
5412 {
5413 // phpcs:enable
5414 $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
5415 if (empty($noprint)) {
5416 print $out;
5417 } else {
5418 return $out;
5419 }
5420 }
5421
5422
5439 public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
5440 {
5441 global $langs, $user;
5442
5443 $out = '';
5444 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5445
5447
5448 // Set default value if not already set by caller
5449 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
5450 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
5451 $selected = getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID');
5452 }
5453
5454 $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5455 if ($addempty) {
5456 $out .= '<option value="0">&nbsp;</option>';
5457 }
5458
5459 $selectedDepositPercent = null;
5460
5461 foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
5462 if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
5463 continue;
5464 }
5465
5466 if ($selected == $id) {
5467 $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
5468 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
5469 } else {
5470 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
5471 }
5472 $label = $arrayconditions['label'];
5473
5474 if (!empty($arrayconditions['deposit_percent'])) {
5475 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
5476 }
5477
5478 $out .= $label;
5479 $out .= '</option>';
5480 }
5481 $out .= '</select>';
5482 if ($user->admin && empty($noinfoadmin)) {
5483 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5484 }
5485 $out .= ajax_combobox($htmlname);
5486
5487 if ($deposit_percent >= 0) {
5488 $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
5489 $out .= $langs->trans('DepositPercent') . ' : ';
5490 $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
5491 $out .= '</span>';
5492 $out .= '
5493 <script nonce="' . getNonce() . '">
5494 $(document).ready(function () {
5495 $("#' . $htmlname . '").change(function () {
5496 let $selected = $(this).find("option:selected");
5497 let depositPercent = $selected.attr("data-deposit_percent");
5498
5499 if (depositPercent.length > 0) {
5500 $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
5501 } else {
5502 $("#' . $htmlname . '_deposit_percent_container").hide();
5503 }
5504
5505 return true;
5506 });
5507 });
5508 </script>';
5509 }
5510
5511 return $out;
5512 }
5513
5514
5523 public function getSelectRuleForLinesDates($selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0)
5524 {
5525 global $langs;
5526
5527 $out = '';
5528
5530
5531 $out .= '<select id="' . $htmlname . '" class="flat selectbillingterm" name="' . $htmlname . '">';
5532 if ($addempty) {
5533 $out .= '<option value="-1">&nbsp;</option>';
5534 }
5535
5536
5537 foreach ($this->cache_rule_for_lines_dates as $rule_for_lines_dates_key => $rule_for_lines_dates_name) {
5538 if ($selected == $rule_for_lines_dates_key) {
5539 $out .= '<option value="' . $rule_for_lines_dates_key . '" selected>';
5540 } else {
5541 $out .= '<option value="' . $rule_for_lines_dates_key . '">';
5542 }
5543
5544 $out .= $langs->trans($rule_for_lines_dates_name);
5545 $out .= '</option>';
5546 }
5547 $out .= '</select>';
5548
5549 $out .= ajax_combobox($htmlname);
5550
5551 return $out;
5552 }
5553
5554
5555 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5556
5573 public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
5574 {
5575 // phpcs:enable
5576 global $langs, $user;
5577
5578 $out = '';
5579
5580 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
5581
5582 $filterarray = array();
5583 if ($filtertype == 'CRDT') {
5584 $filterarray = array(0, 2, 3);
5585 } elseif ($filtertype == 'DBIT') {
5586 $filterarray = array(1, 2, 3);
5587 } elseif ($filtertype != '' && $filtertype != '-1') {
5588 $filterarray = explode(',', $filtertype);
5589 }
5590
5592
5593 // Set default value if not already set by caller
5594 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
5595 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
5596 $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
5597 }
5598
5599 $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5600 if ($empty) {
5601 $out .= '<option value="">&nbsp;</option>';
5602 }
5603 foreach ($this->cache_types_paiements as $id => $arraytypes) {
5604 // If not good status
5605 if ($active >= 0 && $arraytypes['active'] != $active) {
5606 continue;
5607 }
5608
5609 // We skip of the user requested to filter on specific payment methods
5610 if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
5611 continue;
5612 }
5613
5614 // We discard empty lines if showempty is on because an empty line has already been output.
5615 if ($empty && empty($arraytypes['code'])) {
5616 continue;
5617 }
5618
5619 if ($format == 0) {
5620 $out .= '<option value="' . $id . '" data-code="'.$arraytypes['code'].'"';
5621 } elseif ($format == 1) {
5622 $out .= '<option value="' . $arraytypes['code'] . '"';
5623 } elseif ($format == 2) {
5624 $out .= '<option value="' . $arraytypes['code'] . '"';
5625 } elseif ($format == 3) {
5626 $out .= '<option value="' . $id . '"';
5627 }
5628 // Print attribute selected or not
5629 if ($format == 1 || $format == 2) {
5630 if ($selected == $arraytypes['code']) {
5631 $out .= ' selected';
5632 }
5633 } else {
5634 if ($selected == $id) {
5635 $out .= ' selected';
5636 }
5637 }
5638 $out .= '>';
5639 $value = '';
5640 if ($format == 0) {
5641 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5642 } elseif ($format == 1) {
5643 $value = $arraytypes['code'];
5644 } elseif ($format == 2) {
5645 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5646 } elseif ($format == 3) {
5647 $value = $arraytypes['code'];
5648 }
5649 $out .= $value ? $value : '&nbsp;';
5650 $out .= '</option>';
5651 }
5652 $out .= '</select>';
5653 if ($user->admin && !$noadmininfo) {
5654 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5655 }
5656 $out .= ajax_combobox('select' . $htmlname);
5657
5658 if (empty($nooutput)) {
5659 print $out;
5660 } else {
5661 return $out;
5662 }
5663 }
5664
5665
5674 public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
5675 {
5676 global $langs;
5677
5678 $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5679 $options = array(
5680 'HT' => $langs->trans("HT"),
5681 'TTC' => $langs->trans("TTC")
5682 );
5683 foreach ($options as $id => $value) {
5684 if ($selected == $id) {
5685 $return .= '<option value="' . $id . '" selected>' . $value;
5686 } else {
5687 $return .= '<option value="' . $id . '">' . $value;
5688 }
5689 $return .= '</option>';
5690 }
5691 $return .= '</select>';
5692 if ($addjscombo) {
5693 $return .= ajax_combobox('select_' . $htmlname);
5694 }
5695
5696 return $return;
5697 }
5698
5699 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5700
5707 {
5708 // phpcs:enable
5709 global $langs;
5710
5711 $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
5712 if ($num > 0) {
5713 return $num; // Cache already loaded
5714 }
5715
5716 dol_syslog(__METHOD__, LOG_DEBUG);
5717
5718 $this->cache_transport_mode = array();
5719
5720 $sql = "SELECT rowid, code, label, active";
5721 $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
5722 $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
5723
5724 $resql = $this->db->query($sql);
5725 if ($resql) {
5726 $num = $this->db->num_rows($resql);
5727 $i = 0;
5728 while ($i < $num) {
5729 $obj = $this->db->fetch_object($resql);
5730
5731 // If traduction exist, we use it else we take the default label
5732 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5733 $this->cache_transport_mode[(int) $obj->rowid]
5734 = array(
5735 'rowid' => (int) $obj->rowid,
5736 'code' => (string) $obj->code,
5737 'label' => (string) $label,
5738 'active' => (int) $obj->active,
5739 );
5740 $i++;
5741 }
5742
5743 $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
5744
5745 return $num;
5746 } else {
5747 dol_print_error($this->db);
5748 return -1;
5749 }
5750 }
5751
5765 public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
5766 {
5767 global $langs, $user;
5768
5769 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
5770
5772
5773 print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5774 if ($empty) {
5775 print '<option value="">&nbsp;</option>';
5776 }
5777 foreach ($this->cache_transport_mode as $id => $arraytypes) {
5778 // If not good status
5779 if ($active >= 0 && $arraytypes['active'] != $active) {
5780 continue;
5781 }
5782
5783 // We discard empty line if showempty is on because an empty line has already been output.
5784 if ($empty && empty($arraytypes['code'])) {
5785 continue;
5786 }
5787
5788 if ($format == 0) {
5789 print '<option value="' . $id . '"';
5790 } elseif ($format == 1) {
5791 print '<option value="' . $arraytypes['code'] . '"';
5792 } elseif ($format == 2) {
5793 print '<option value="' . $arraytypes['code'] . '"';
5794 } elseif ($format == 3) {
5795 print '<option value="' . $id . '"';
5796 }
5797 // If text is selected, we compare with code, else with id
5798 if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
5799 print ' selected';
5800 } elseif ($selected == $id) {
5801 print ' selected';
5802 }
5803 print '>';
5804 $value = '';
5805 if ($format == 0) {
5806 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5807 } elseif ($format == 1) {
5808 $value = $arraytypes['code'];
5809 } elseif ($format == 2) {
5810 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5811 } elseif ($format == 3) {
5812 $value = $arraytypes['code'];
5813 }
5814 print $value ? $value : '&nbsp;';
5815 print '</option>';
5816 }
5817 print '</select>';
5818
5819 print ajax_combobox("select".$htmlname);
5820
5821 if ($user->admin && !$noadmininfo) {
5822 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5823 }
5824 }
5825
5838 public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
5839 {
5840 global $langs, $user;
5841
5842 $langs->loadLangs(array("admin", "sendings"));
5843
5844 $sql = "SELECT rowid, code, libelle as label";
5845 $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
5846 $sql .= " WHERE active > 0";
5847 if ($filtre) {
5848 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
5849 }
5850 $sql .= " ORDER BY libelle ASC";
5851
5852 dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
5853
5854 $result = $this->db->query($sql);
5855 if ($result) {
5856 $num = $this->db->num_rows($result);
5857 $i = 0;
5858 if ($num) {
5859 print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5860 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5861 print '<option value="-1">&nbsp;</option>';
5862 }
5863 while ($i < $num) {
5864 $obj = $this->db->fetch_object($result);
5865 if ($selected == $obj->rowid) {
5866 print '<option value="' . $obj->rowid . '" selected>';
5867 } else {
5868 print '<option value="' . $obj->rowid . '">';
5869 }
5870 print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
5871 print '</option>';
5872 $i++;
5873 }
5874 print "</select>";
5875 if ($user->admin && empty($noinfoadmin)) {
5876 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5877 }
5878
5879 print ajax_combobox('select' . $htmlname);
5880 } else {
5881 print $langs->trans("NoShippingMethodDefined");
5882 }
5883 } else {
5884 dol_print_error($this->db);
5885 }
5886 }
5887
5897 public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
5898 {
5899 global $langs;
5900
5901 $langs->load("sendings");
5902
5903 if ($htmlname != "none") {
5904 print '<form method="POST" action="' . $page . '">';
5905 print '<input type="hidden" name="action" value="setshippingmethod">';
5906 print '<input type="hidden" name="token" value="' . newToken() . '">';
5907 $this->selectShippingMethod($selected, $htmlname, '', $addempty);
5908 print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
5909 print '</form>';
5910 } else {
5911 if ($selected) {
5912 $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
5913 print $langs->trans("SendingMethod" . strtoupper($code));
5914 } else {
5915 print "&nbsp;";
5916 }
5917 }
5918 }
5919
5928 public function selectSituationInvoices($selected = '', $socid = 0)
5929 {
5930 global $langs;
5931
5932 $langs->load('bills');
5933
5934 $opt = '';
5935
5936 $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
5937 $sql .= ' FROM ' . $this->db->prefix() . 'facture';
5938 $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
5939 $sql .= ' AND situation_counter >= 1';
5940 $sql .= ' AND fk_soc = ' . (int) $socid;
5941 $sql .= ' AND type <> 2';
5942 $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
5943 $resql = $this->db->query($sql);
5944
5945 $nbSituationInvoiceForThirdparty = 0;
5946
5947 if ($resql && $this->db->num_rows($resql) > 0) {
5948 // Last seen cycle
5949 $ref = 0;
5950 while ($obj = $this->db->fetch_object($resql)) {
5951 //Same cycle ?
5952 if ($obj->situation_cycle_ref != $ref) {
5953 // Just seen this cycle
5954 $ref = $obj->situation_cycle_ref;
5955 //not final ?
5956 if ($obj->situation_final != 1) {
5957 //Not prov?
5958 if (substr($obj->ref, 1, 4) != 'PROV') {
5959 $nbSituationInvoiceForThirdparty++;
5960
5961 if ($selected == $obj->rowid) {
5962 $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
5963 } else {
5964 $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
5965 }
5966 }
5967 }
5968 }
5969 }
5970 } else {
5971 dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
5972 }
5973
5974 if ($nbSituationInvoiceForThirdparty > 0) {
5975 $opt = '<option class="minwidth100" value="" selected>&nbsp;</option>'.$opt;
5976 } else {
5977 $opt = '<option class="minwidth100" value="-1" selected>'.$langs->trans('NoSituations').'</option>';
5978 }
5979
5980 return $opt;
5981 }
5982
5992 public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
5993 {
5994 global $langs;
5995
5996 $langs->load('products');
5997
5998 $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
5999
6000 $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
6001 $sql .= ' WHERE active > 0';
6002 if (!empty($unit_type)) {
6003 $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
6004 }
6005 $sql .= " ORDER BY sortorder";
6006
6007 $resql = $this->db->query($sql);
6008 if ($resql && $this->db->num_rows($resql) > 0) {
6009 if ($showempty) {
6010 $return .= '<option value="-1"></option>';
6011 }
6012
6013 while ($res = $this->db->fetch_object($resql)) {
6014 $unitLabel = $res->label;
6015 if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
6016 $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
6017 }
6018
6019 if ($selected == $res->rowid) {
6020 $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
6021 } else {
6022 $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
6023 }
6024 }
6025 $return .= '</select>';
6026
6027 $return .= ajax_combobox($htmlname);
6028 }
6029 return $return;
6030 }
6031
6032 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6033
6049 public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0, $addentrynone = 0)
6050 {
6051 // phpcs:enable
6052 global $langs;
6053
6054 $out = '';
6055
6056 $langs->loadLangs(array("admin", "banks"));
6057 $num = 0;
6058
6059 $sql = "SELECT rowid, label, bank, clos as status, currency_code";
6060 $sql .= " FROM " . $this->db->prefix() . "bank_account";
6061 $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
6062 if ($status != 2) {
6063 $sql .= " AND clos = " . (int) $status;
6064 }
6065 if ($filtre) {
6066 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6067 }
6068 $sql .= " ORDER BY label";
6069
6070 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6071 $result = $this->db->query($sql);
6072 if ($result) {
6073 $num = $this->db->num_rows($result);
6074 $i = 0;
6075
6076 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6077
6078 if ($num == 0) {
6079 if ($status == 0) {
6080 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
6081 } else {
6082 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6083 }
6084 } else {
6085 if (!empty($useempty) && !is_numeric($useempty)) {
6086 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6087 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6088 $out .= '<option value="-1">&nbsp;</option>';
6089 }
6090 }
6091
6092 while ($i < $num) {
6093 $obj = $this->db->fetch_object($result);
6094
6095 $labeltoshow = trim($obj->label);
6096 $labeltoshowhtml = trim($obj->label);
6097 if ($showcurrency) {
6098 $labeltoshow .= ' (' . $obj->currency_code . ')';
6099 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $obj->currency_code . ')</span>';
6100 }
6101 if ($status == 2 && $obj->status == 1) {
6102 $labeltoshow .= ' (' . $langs->trans("Closed") . ')';
6103 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $langs->trans("Closed") . ')</span>';
6104 }
6105
6106 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6107 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'" selected>';
6108 } else {
6109 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'">';
6110 }
6111 $out .= $labeltoshow;
6112 $out .= '</option>';
6113 $i++;
6114 }
6115
6116 if (!empty($addentrynone)) {
6117 $out .= '<option value="-2"'.($selected == -2 ? ' selected="selected"' : '').' data-html="'.dolPrintHTMLForAttribute('<span class="opacitymedium">'.$langs->trans("None").'</span>').'">'.$langs->trans("None").'</option>';
6118 }
6119
6120 $out .= "</select>";
6121 $out .= ajax_combobox('select' . $htmlname);
6122 } else {
6123 dol_print_error($this->db);
6124 }
6125
6126 // Output or return
6127 if (empty($nooutput)) {
6128 print $out;
6129 } else {
6130 return $out;
6131 }
6132
6133 return $num;
6134 }
6135
6149 public function selectRib($selected = '', $htmlname = 'ribcompanyid', $filtre = '', $useempty = 0, $moreattrib = '', $showibanbic = 0, $morecss = '', $nooutput = 0)
6150 {
6151 // phpcs:enable
6152 global $langs;
6153
6154 $out = '';
6155
6156 $langs->loadLangs(array("admin", "banks"));
6157 $num = 0;
6158
6159 $sql = "SELECT rowid, label, bank, status, iban_prefix, bic, default_rib";
6160 $sql .= " FROM " . $this->db->prefix() . "societe_rib";
6161 $sql .= " WHERE type = 'ban'";
6162 if ($filtre) {
6163 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6164 }
6165 $sql .= " ORDER BY label";
6166 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6167 $result = $this->db->query($sql);
6168 if ($result) {
6169 $num = $this->db->num_rows($result);
6170 $i = 0;
6171
6172 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6173
6174 if ($num == 0) {
6175 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6176 } else {
6177 if (!empty($useempty) && !is_numeric($useempty)) {
6178 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6179 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6180 $out .= '<option value="-1">&nbsp;</option>';
6181 }
6182 }
6183
6184 while ($i < $num) {
6185 $obj = $this->db->fetch_object($result);
6186 $iban = dolDecrypt($obj->iban_prefix);
6187 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6188 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '" selected>';
6189 } else {
6190 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '">';
6191 }
6192 $out .= trim($obj->label);
6193 if ($showibanbic) {
6194 $out .= ' (' . $iban . '/' .$obj->bic. ')' . ($obj->default_rib ? ' ['.$langs->trans("ByDefault").']' : '');
6195 }
6196 $out .= '</option>';
6197 $i++;
6198 }
6199 $out .= "</select>";
6200 $out .= ajax_combobox('select' . $htmlname);
6201 } else {
6202 dol_print_error($this->db);
6203 }
6204
6205 // Output or return
6206 if (empty($nooutput)) {
6207 print $out;
6208 } else {
6209 return $out;
6210 }
6211
6212 return $num;
6213 }
6214
6226 public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
6227 {
6228 global $langs;
6229
6230 $langs->load("admin");
6231 $num = 0;
6232
6233 $sql = "SELECT rowid, name, fk_country, status, entity";
6234 $sql .= " FROM " . $this->db->prefix() . "establishment";
6235 $sql .= " WHERE 1=1";
6236 if ($status != 2) {
6237 $sql .= " AND status = " . (int) $status;
6238 }
6239 if ($filtre) {
6240 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6241 }
6242 $sql .= " ORDER BY name";
6243
6244 dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
6245 $result = $this->db->query($sql);
6246 if ($result) {
6247 $num = $this->db->num_rows($result);
6248 $i = 0;
6249 if ($num) {
6250 print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6251 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6252 print '<option value="-1">&nbsp;</option>';
6253 }
6254
6255 while ($i < $num) {
6256 $obj = $this->db->fetch_object($result);
6257 if ($selected == $obj->rowid) {
6258 print '<option value="' . $obj->rowid . '" selected>';
6259 } else {
6260 print '<option value="' . $obj->rowid . '">';
6261 }
6262 print trim($obj->name);
6263 if ($status == 2 && $obj->status == 1) {
6264 print ' (' . $langs->trans("Closed") . ')';
6265 }
6266 print '</option>';
6267 $i++;
6268 }
6269 print "</select>";
6270 } else {
6271 if ($status == 0) {
6272 print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
6273 } else {
6274 print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
6275 }
6276 }
6277
6278 return $num;
6279 } else {
6280 dol_print_error($this->db);
6281 return -1;
6282 }
6283 }
6284
6294 public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
6295 {
6296 global $langs;
6297 if ($htmlname != "none") {
6298 print '<form method="POST" action="' . $page . '">';
6299 print '<input type="hidden" name="action" value="setbankaccount">';
6300 print '<input type="hidden" name="token" value="' . newToken() . '">';
6301 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
6302 $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
6303 if ($nbaccountfound > 0) {
6304 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6305 }
6306 print '</form>';
6307 } else {
6308 $langs->load('banks');
6309
6310 if ($selected) {
6311 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
6312 $bankstatic = new Account($this->db);
6313 $result = $bankstatic->fetch((int) $selected);
6314 if ($result) {
6315 print $bankstatic->getNomUrl(1);
6316 }
6317 } else {
6318 print "&nbsp;";
6319 }
6320 }
6321 }
6322
6334 public function formRib($page, $selected = '', $htmlname = 'ribcompanyid', $filtre = '', $addempty = 0, $showibanbic = 0)
6335 {
6336 global $langs;
6337 if ($htmlname != "none") {
6338 print '<form method="POST" action="' . $page . '">';
6339 print '<input type="hidden" name="action" value="setbankaccountcustomer">';
6340 print '<input type="hidden" name="token" value="' . newToken() . '">';
6341 $nbaccountfound = $this->selectRib($selected, $htmlname, $filtre, $addempty, '', $showibanbic);
6342 if ($nbaccountfound > 0) {
6343 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6344 }
6345 print '</form>';
6346 } else {
6347 $langs->load('banks');
6348
6349 if ($selected) {
6350 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
6351 $bankstatic = new CompanyBankAccount($this->db);
6352 $result = $bankstatic->fetch((int) $selected);
6353 if ($result) {
6354 print $bankstatic->label;
6355 if ($showibanbic) {
6356 print ' (' . $bankstatic->iban . '/' .$bankstatic->bic. ')';
6357 }
6358 }
6359 } else {
6360 print "&nbsp;";
6361 }
6362 }
6363 }
6364
6374 public function selectCategories($categtype, $htmlname, $object = null)
6375 {
6376 global $langs;
6377
6378 $out = '';
6379
6380 $cate_arbo = $this->select_all_categories($categtype, '', '', 64, 0, 3);
6381
6382 $arrayselected = array();
6383 if (GETPOSTISARRAY($htmlname)) {
6384 $arrayselected = GETPOST($htmlname, 'array:int');
6385 } elseif (is_object($object) && $object->id > 0) {
6386 $c = new Categorie($this->db);
6387 $cats = $c->containing($object->id, $categtype);
6388 $arrayselected = array();
6389 foreach ($cats as $cat) {
6390 $arrayselected[] = $cat->id;
6391 }
6392 }
6393
6394 $out .= img_picto('', 'category', 'class="pictofixedwidth"');
6395 $out .= $this->multiselectarray($htmlname, $cate_arbo, $arrayselected, 0, 0, 'minwidth100 widthcentpercentminusxx', 0, 0);
6396
6397 if (!getDolGlobalString('CATEGORY_EDIT_IN_MENU_NOT_IN_POPUP')) {
6398 // Add html code to add the edit button and go back
6399 $jsonclose = 'doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'()';
6400 $urltoopen = '/categories/categorie_list.php?type='.urlencode($categtype).'&nosearch=1';
6401
6402 $s = dolButtonToOpenUrlInDialogPopup($htmlname, $langs->transnoentitiesnoconv("Categories"), img_picto('', 'add', 'class="editfielda"'), $urltoopen, '', '', '', $jsonclose);
6403 $out .= $s;
6404 // Add js code to add the edit button and go back
6405 $out .= '<!-- Add js code to open the popup for category/edit/add -->'."\n";
6406 $out .= '<script>function doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'() {
6407 console.log("doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).' has been called, we refresh the combo content + refresh select2...");
6408
6409 // Call an ajax to reload values and update the select
6410
6411 $.ajax({
6412 url: \''.DOL_URL_ROOT.'/core/ajax/fetchCategories.php\',
6413 data: {
6414 action: \'getCategories\',
6415 type: \''.dol_escape_htmltag($categtype).'\'
6416 },
6417 type: \'GET\',
6418 dataType: \'json\',
6419 success: function (data) {
6420 var $select = $(\'#'.dol_sanitizeKeyCode($htmlname).'\');
6421 var selectedValues = $select.val(); // This is an array of selected values
6422 console.log(selectedValues);
6423 $select.empty();
6424 $.each(data, function (index, item) {
6425 $select.append(\'<option value="\' + item.id + \'" data-html="\' + item.htmlforattribute + \'">\' + item.htmlforoption + \'</option>\');
6426 });
6427 $select.val(selectedValues);
6428 },
6429 error: function (xhr, status, error) {
6430 console.log("Error when loading ajax page : " + error);
6431 }
6432 });
6433
6434 // Refresh select2 to take account of new values (enough for small change)
6435 $("#'.dol_sanitizeKeyCode($htmlname).'").trigger("change");
6436
6437 // Alternative if change in select is complex
6438 /*
6439 $("#'.dol_sanitizeKeyCode($htmlname).'").select2("destroy");
6440 $("#'.dol_sanitizeKeyCode($htmlname).'").select2();
6441 */
6442 }</script>';
6443 }
6444
6445 return $out;
6446 }
6447
6448
6449 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6469 public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
6470 {
6471 // phpcs:enable
6472 global $langs;
6473
6474 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
6475
6476 $cat = new Categorie($this->db);
6477
6478 if (is_numeric($type)) {
6479 $type = array_search($type, $cat->MAP_ID); // For backward compatibility
6480 }
6481
6482 $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
6483
6484 $outarray = array();
6485 $outarrayrichhtml = array();
6486
6487
6488 $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6489 $num = 0;
6490 if (is_array($cate_arbo)) {
6491 $num = count($cate_arbo);
6492
6493 if (!$num) {
6494 $langs->load("categories");
6495 $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
6496 } else {
6497 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6498 $output .= '<option value="-1">&nbsp;</option>';
6499 }
6500 foreach ($cate_arbo as $key => $value) {
6501 if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
6502 $add = 'selected ';
6503 } else {
6504 $add = '';
6505 }
6506
6507 $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth"'.(empty($cate_arbo[$key]['color']) ? '' : ' style="color: #' . $cate_arbo[$key]['color'] . '"'));
6508 $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
6509
6510 $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
6511
6512 $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
6513
6514 $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
6515 $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
6516 $output .= '>';
6517 // The visible (truncated) label is rendered via data-html in
6518 // templateResult of the select2 combobox; the bare option text
6519 // must keep the full label so that the select2 search matcher
6520 // (ajax_combobox in core/lib/ajax.lib.php) can find a hit on
6521 // characters that lie outside the truncated portion.
6522 $output .= dol_escape_htmltag($cate_arbo[$key]['fulllabel']);
6523 $output .= '</option>';
6524
6525 $cate_arbo[$key]['data-html'] = $labeltoshow;
6526 }
6527 }
6528 }
6529 $output .= '</select>';
6530 $output .= "\n";
6531
6532 $this->num = $num;
6533
6534 if ($outputmode == 2) {
6535 // TODO: handle error when $cate_arbo is not an array
6536 return $cate_arbo;
6537 } elseif ($outputmode == 1) {
6538 return $outarray;
6539 } elseif ($outputmode == 3) {
6540 return $outarrayrichhtml;
6541 }
6542 return $output;
6543 }
6544
6553 public function getHelpBlock($content, $icon = 'fa-question-circle')
6554 {
6555 global $langs;
6556
6557 // Sanitize content (assuming it might contain HTML, but escaping text nodes if needed)
6558 // We trust the caller to pass safe HTML or translated strings.
6559
6560 $html = '<details class="dolibarr-help-block" style="margin-top:8px;">';
6561 $html .= '<summary style="cursor:pointer; color:#0056b3; font-weight:normal; list-style:none; font-size:0.9em; display:flex; align-items:center;">';
6562 $html .= '<span class="fa ' . $icon . '" style="margin-right:6px;"></span>';
6563 $html .= $langs->trans("Help"); // Standardized title
6564 $html .= '</summary>';
6565 $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;">';
6566 $html .= $content;
6567 $html .= '</div>';
6568 $html .= '</details>';
6569
6570 return $html;
6571 }
6572
6573 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6574
6593 public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
6594 {
6595 // phpcs:enable
6596 dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
6597 print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
6598 }
6599
6629 public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 600, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No', $helpContent = '')
6630 {
6631 global $langs, $conf;
6632
6633 $more = '';
6634 $formconfirm = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
6635
6636 $inputok = array();
6637 $inputko = array();
6638
6639 // Clean parameters
6640 $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
6641 if ($conf->browser->layout == 'phone') {
6642 $width = '95%';
6643 }
6644
6645 // Set height automatically if not defined
6646 if (empty($height)) {
6647 $height = 185;
6648 if (is_array($formquestion)) {
6649 $height += (count($formquestion) * 40);
6650 }
6651 if ($question) {
6652 $height += dol_nboflines_bis($question, 80) * 40;
6653 }
6654 }
6655
6656 if (is_array($formquestion) && !empty($formquestion)) {
6657 // First add hidden fields and value
6658 foreach ($formquestion as $key => $input) {
6659 if (is_array($input) && !empty($input)) {
6660 if ($input['type'] == 'hidden') {
6661 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6662 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6663
6664 $more .= '<input type="hidden" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . dol_escape_htmltag($input['value']) . '" class="' . $morecss . '"' . $moreattr . '>' . "\n";
6665 }
6666 }
6667 }
6668
6669 // Now add questions
6670 $moreonecolumn = '';
6671 $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
6672 foreach ($formquestion as $key => $input) {
6673 if (is_array($input) && !empty($input)) {
6674 $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
6675 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6676 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6677
6678 if ($input['type'] == 'text' || $input['type'] == 'input') { // traditional input
6679 $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";
6680 } elseif ($input['type'] == 'password') {
6681 $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";
6682 } elseif ($input['type'] == 'textarea') {
6683 $moreonecolumn .= '<div class="margintoponly">';
6684 $moreonecolumn .= $input['label'] . '<br>';
6685 $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
6686 $moreonecolumn .= $input['value'];
6687 $moreonecolumn .= '</textarea>';
6688 $moreonecolumn .= '</div>';
6689 } elseif (in_array($input['type'], ['select', 'multiselect'])) {
6690 if (empty($morecss)) {
6691 $morecss = 'minwidth100';
6692 }
6693
6694 $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
6695 $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
6696 $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
6697 $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
6698 $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
6699 $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
6700 $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
6701
6702 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
6703 if (!empty($input['label'])) {
6704 $more .= $input['label'] . '</div><div class="tagtd left">';
6705 }
6706 if ($input['type'] == 'select') {
6707 $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);
6708 } else {
6709 $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);
6710 }
6711 $more .= '</div></div>' . "\n";
6712 } elseif ($input['type'] == 'checkbox') {
6713 $more .= '<div class="tagtr">';
6714 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
6715 $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
6716 if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
6717 $more .= ' checked';
6718 }
6719 if (is_bool($input['value']) && $input['value']) {
6720 $more .= ' checked';
6721 }
6722 if (isset($input['disabled'])) {
6723 $more .= ' disabled';
6724 }
6725 $more .= ' /></div>';
6726 $more .= '</div>' . "\n";
6727 } elseif ($input['type'] == 'radio') {
6728 $i = 0;
6729 foreach ($input['values'] as $selkey => $selval) {
6730 $more .= '<div class="tagtr">';
6731 if (isset($input['label'])) {
6732 if ($i == 0) {
6733 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6734 } else {
6735 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
6736 }
6737 }
6738 $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;
6739 if (!empty($input['disabled'])) {
6740 $more .= ' disabled';
6741 }
6742 if (isset($input['default']) && $input['default'] === $selkey) {
6743 $more .= ' checked="checked"';
6744 }
6745 $more .= ' /> ';
6746 $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
6747 $more .= '</div></div>' . "\n";
6748 $i++;
6749 }
6750 } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
6751 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6752 $more .= '<div class="tagtd">';
6753 $addnowlink = (empty($input['datenow']) ? 0 : 1);
6754 $h = $m = 0;
6755 if ($input['type'] == 'datetime') {
6756 $h = isset($input['hours']) ? $input['hours'] : 1;
6757 $m = isset($input['minutes']) ? $input['minutes'] : 1;
6758 }
6759 $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
6760 $more .= '</div></div>'."\n";
6761 $formquestion[] = array('name' => $input['name'].'day');
6762 $formquestion[] = array('name' => $input['name'].'month');
6763 $formquestion[] = array('name' => $input['name'].'year');
6764 $formquestion[] = array('name' => $input['name'].'hour');
6765 $formquestion[] = array('name' => $input['name'].'min');
6766 } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
6767 $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
6768 if (!empty($input['label'])) {
6769 $more .= $input['label'] . '</div><div class="tagtd">';
6770 }
6771 if (!empty($input['value'])) {
6772 $more .= $input['value'];
6773 }
6774 $more .= '</div></div>' . "\n";
6775 } elseif ($input['type'] == 'onecolumn') {
6776 $moreonecolumn .= '<div class="margintoponly">';
6777 $moreonecolumn .= $input['value'];
6778 $moreonecolumn .= '</div>' . "\n";
6779 } elseif ($input['type'] == 'hidden') {
6780 // Do nothing more, already added by a previous loop
6781 } elseif ($input['type'] == 'separator') {
6782 $more .= '<br>';
6783 } else {
6784 $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
6785 }
6786 }
6787 }
6788 $more .= '</div>' . "\n";
6789 $more .= $moreonecolumn;
6790 }
6791
6792 // JQUERY method dialog is broken with smartphone, we use standard HTML.
6793 // 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
6794 // See page product/card.php for example
6795 if (!empty($conf->dol_use_jmobile)) {
6796 $useajax = 0;
6797 }
6798 if (empty($conf->use_javascript_ajax)) {
6799 $useajax = 0;
6800 }
6801
6802 if ($useajax) {
6803 $autoOpen = true;
6804 $dialogconfirm = 'dialog-confirm';
6805 $button = '';
6806 if (!is_numeric($useajax)) {
6807 $button = $useajax;
6808 $useajax = 1;
6809 $autoOpen = false;
6810 $dialogconfirm .= '-' . $button;
6811 }
6812 $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
6813 $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
6814
6815 // Add input fields into list of fields to read during submit (inputok and inputko)
6816 if (is_array($formquestion)) {
6817 foreach ($formquestion as $key => $input) {
6818 //print "xx ".$key." rr ".is_array($input)."<br>\n";
6819 // Add name of fields to propagate with the GET when submitting the form with button OK.
6820 if (is_array($input) && isset($input['name'])) {
6821 if (strpos($input['name'], ',') > 0) {
6822 $inputok = array_merge($inputok, explode(',', $input['name']));
6823 } else {
6824 array_push($inputok, $input['name']);
6825 }
6826 }
6827 // Add name of fields to propagate with the GET when submitting the form with button KO.
6828 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
6829 if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
6830 array_push($inputko, $input['name']);
6831 }
6832 }
6833 }
6834
6835 // Show JQuery confirm box.
6836 // Add 'flex-direction: column' and 'justify-content: space-between' to push content to top and buttons to bottom
6837 $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
6838 $formconfirm .= '<div style="display: flex; flex-direction: column; height: 100%;">';
6839 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6840 $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
6841 }
6842 if (!empty($more)) {
6843 $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
6844 }
6845 // NEW: Add help block if content provided
6846 if (!empty($helpContent)) {
6847 $formconfirm .= '<div style="text-align:left; margin-top:12px; padding-top:8px; border-top:1px solid #eee; clear:both;">';
6848 $formconfirm .= $this->getHelpBlock($helpContent);
6849 $formconfirm .= '</div>';
6850 }
6851 if (!empty($question)) {
6852 $formconfirm .= '<div class="confirmmessage" style="padding-top: 15px;">';
6853 $formconfirm .= img_help(0, '') . ' ' . $question;
6854 $formconfirm .= '</div>';
6855 }
6856 $formconfirm .= '</div>';
6857 $formconfirm .= '</div>' . "\n";
6858
6859 $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
6860 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
6861 $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
6862 $formconfirm .= 'jQuery(document).ready(function() {
6863 $(function() {
6864 $( "#' . $dialogconfirm . '" ).dialog({
6865 autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
6866 if ($newselectedchoice == 'no') {
6867 $formconfirm .= '
6868 open: function() {
6869 $(this).parent().find("button.ui-button:eq(2)").focus();
6870 },';
6871 }
6872
6873 $jsforcursor = '';
6874 if ($useajax == 1) {
6875 $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
6876 $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
6877 }
6878
6879 $postconfirmas = 'GET';
6880
6881 $formconfirm .= '
6882 resizable: false,
6883 height: \'' . dol_escape_js($height) . '\',
6884 width: \'' . dol_escape_js($width) . '\',
6885 modal: true,
6886 closeOnEscape: false,
6887 buttons: {
6888 "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
6889 var options = "token=' . urlencode(newToken()) . '";
6890 var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
6891 var page = \'' . dol_escape_js(!empty($page) ? $page : '') . '\';
6892 var pageyes = \'' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '\';
6893
6894 if (inputok.length > 0) {
6895 $.each(inputok, function(i, inputname) {
6896 var more = "";
6897 var inputvalue;
6898 if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
6899 inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
6900 } else {
6901 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6902 inputvalue = $("#" + inputname + more).val();
6903 }
6904 if (typeof inputvalue == "undefined") { inputvalue=""; }
6905 console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
6906 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6907 });
6908 }
6909 var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
6910 if (pageyes.length > 0) {';
6911 if ($postconfirmas == 'GET') {
6912 $formconfirm .= 'location.href = urljump;';
6913 } else {
6914 $formconfirm .= $jsforcursor;
6915 $formconfirm .= 'var post = $.post(
6916 pageyes,
6917 options,
6918 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6919 );';
6920 }
6921 $formconfirm .= '
6922 console.log("after post ok");
6923 }
6924 $(this).dialog("close");
6925 },
6926 "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
6927 var options = "token=' . urlencode(newToken()) . '";
6928 var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
6929 var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
6930 var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
6931 if (inputko.length > 0) {
6932 $.each(inputko, function(i, inputname) {
6933 var more = "";
6934 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6935 var inputvalue = $("#" + inputname + more).val();
6936 if (typeof inputvalue == "undefined") { inputvalue=""; }
6937 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6938 });
6939 }
6940 var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
6941 //alert(urljump);
6942 if (pageno.length > 0) {';
6943 if ($postconfirmas == 'GET') {
6944 $formconfirm .= 'location.href = urljump;';
6945 } else {
6946 $formconfirm .= $jsforcursor;
6947 $formconfirm .= 'var post = $.post(
6948 pageno,
6949 options,
6950 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6951 );';
6952 }
6953 $formconfirm .= '
6954 console.log("after post ko");
6955 }
6956 $(this).dialog("close");
6957 }
6958 }
6959 }
6960 );
6961
6962 var button = "' . $button . '";
6963 if (button.length > 0) {
6964 $( "#" + button ).click(function() {
6965 $("#' . $dialogconfirm . '").dialog("open");
6966 });
6967 }
6968 });
6969 });
6970 </script>';
6971 $formconfirm .= "<!-- end ajax formconfirm -->\n";
6972 } else {
6973 $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
6974
6975 if (empty($disableformtag)) {
6976 $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftnoright">' . "\n";
6977 }
6978
6979 $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
6980 $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
6981
6982 $formconfirm .= '<div class="valid">' . "\n";
6983
6984 // Line title
6985 $formconfirm .= '<div class="validtitre">';
6986 $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
6987 $formconfirm .= '</div>' . "\n";
6988
6989 // Line text
6990 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6991 $formconfirm .= '<div class="valid">' . $formquestion['text'] . '</div>' . "\n";
6992 }
6993
6994 // Line form fields
6995 if ($more) {
6996 $formconfirm .= '<div>' . "\n";
6997 $formconfirm .= $more;
6998 $formconfirm .= '</div>' . "\n";
6999 }
7000
7001 // NEW: Help block row (between form fields and question)
7002 if (!empty($helpContent)) {
7003 $formconfirm .= '<div style="padding-top:8px; border-top:1px solid #888;">';
7004 $formconfirm .= $this->getHelpBlock($helpContent);
7005 $formconfirm .= '</div>' . "\n";
7006 }
7007
7008 // Let's add a row that acts as a spacer.
7009 $formconfirm .= '<div style="padding-top: 20px;"></div>' . "\n";
7010
7011 // Question row
7012 $formconfirm .= '<div class="inline-block">' . $question . '</div>';
7013
7014 $formconfirm .= '<div class="inline-block">';
7015 $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
7016 $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
7017 $formconfirm .= '</div>';
7018
7019 $formconfirm .= '</div>';
7020
7021 if (empty($disableformtag)) {
7022 $formconfirm .= "</form>\n";
7023 }
7024 $formconfirm .= '<br>';
7025
7026 if (!empty($conf->use_javascript_ajax)) {
7027 $formconfirm .= '<!-- code to disable button to avoid double clic -->';
7028 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
7029 $formconfirm .= '
7030 $(document).ready(function () {
7031 $(".confirmvalidatebutton").on("click", function() {
7032 console.log("We click on button confirmvalidatebutton");
7033 $(this).attr("disabled", "disabled");
7034 setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
7035 //console.log($(this).closest("form"));
7036 $(this).closest("form").submit();
7037 });
7038 });
7039 ';
7040 $formconfirm .= '</script>' . "\n";
7041 }
7042
7043 $formconfirm .= "<!-- end formconfirm -->\n";
7044 }
7045
7046 return $formconfirm;
7047 }
7048
7049 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7050
7067 public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '', $option = '')
7068 {
7069 // phpcs:enable
7070 global $langs;
7071
7072 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
7073 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
7074
7075 $out = '';
7076
7077 $formproject = new FormProjets($this->db);
7078
7079 $langs->load("project");
7080 if ($htmlname != "none") {
7081 $out .= '<form method="post" action="' . $page . '">';
7082 $out .= '<input type="hidden" name="action" value="classin">';
7083 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7084 $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
7085 $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7086 $out .= '</form>';
7087 } else {
7088 $out .= '<span class="project_head_block">';
7089 if ($selected instanceof Project) {
7090 $out .= $selected->getNomUrl(0, $option, 1);
7091 } elseif (is_numeric($selected)) {
7092 $projet = new Project($this->db);
7093 $projet->fetch((int) $selected);
7094 $out .= $projet->getNomUrl(0, $option, 1);
7095 } else {
7096 $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
7097 }
7098 $out .= '</span>';
7099 }
7100
7101 if (empty($nooutput)) {
7102 print $out;
7103 return '';
7104 }
7105 return $out;
7106 }
7107
7108 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7109
7125 public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
7126 {
7127 // phpcs:enable
7128 global $langs;
7129
7130 $selected = (int) $selected;
7131
7132 $out = '';
7133
7134 if ($htmlname != "none") {
7135 $out .= '<form method="POST" action="' . $page . '">';
7136 $out .= '<input type="hidden" name="action" value="setconditions">';
7137 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7138 if ($type) {
7139 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7140 }
7141 $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
7142 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7143 $out .= '</form>';
7144 } else {
7145 if ($selected) {
7146 $this->load_cache_conditions_paiements();
7147
7148 if (isset($this->cache_conditions_paiements[$selected])) {
7149 $label = $this->cache_conditions_paiements[$selected]['label'];
7150
7151 if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
7152 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
7153 }
7154
7155 $out .= $label;
7156 } else {
7157 $langs->load('errors');
7158 $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
7159 }
7160 } else {
7161 $out .= '&nbsp;';
7162 }
7163 }
7164
7165 if (empty($nooutput)) {
7166 print $out;
7167 return '';
7168 }
7169 return $out;
7170 }
7171
7172 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7173
7184 public function form_rule_for_lines_dates($page, $selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0, $nooutput = 0): string
7185 {
7186 // phpcs:enable
7187 global $langs;
7188
7189 $out = '';
7190
7191 if ($htmlname != 'none') {
7192 $out .= '<form method="POST" action="' . $page . '">';
7193 $out .= '<input type="hidden" name="action" value="setruleforlinesdates">';
7194 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7195 $out .= $this->getSelectRuleForLinesDates($selected, $htmlname, $addempty);
7196 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7197 $out .= '</form>';
7198 } else {
7199 if (isset($selected)) {
7200 $this->load_cache_rule_for_lines_dates();
7201 if (isset($this->cache_rule_for_lines_dates[$selected])) {
7202 $label = $this->cache_rule_for_lines_dates[$selected];
7203 $out .= $langs->trans($label);
7204 }
7205 } else {
7206 $out .= '&nbsp;';
7207 }
7208 }
7209
7210 if (empty($nooutput)) {
7211 print $out;
7212 return '';
7213 }
7214
7215 return $out;
7216 }
7217
7218 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7219
7229 public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
7230 {
7231 dol_syslog(__METHOD__, LOG_DEBUG);
7232 // phpcs:enable
7233 global $langs;
7234 if ($htmlname != "none") {
7235 print '<form method="post" action="' . $page . '">';
7236 print '<input type="hidden" name="action" value="setavailability">';
7237 print '<input type="hidden" name="token" value="' . newToken() . '">';
7238 print $this->selectAvailabilityDelay($selected, $htmlname, '', $addempty, '', 1);
7239 print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7240 print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
7241 print '</form>';
7242 } else {
7243 if ($selected) {
7244 $this->load_cache_availability();
7245 // @phan-suppress-next-line PhanTypeMismatchProperty
7246 if (isset($this->cache_availability[$selected])) {
7247 print $this->cache_availability[$selected]['label'];
7248 } else {
7249 print "&nbsp;";
7250 }
7251 } else {
7252 print "&nbsp;";
7253 }
7254 }
7255 }
7256
7268 public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0, $morecss = '')
7269 {
7270 global $langs;
7271 if ($htmlname != "none") {
7272 print '<form method="post" action="' . $page . '">';
7273 print '<input type="hidden" name="action" value="setdemandreason">';
7274 print '<input type="hidden" name="token" value="' . newToken() . '">';
7275 $this->selectInputReason($selected, $htmlname, '-1', $addempty, $morecss);
7276 print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7277 print '</form>';
7278 } else {
7279 if ($selected) {
7280 $this->loadCacheInputReason();
7281 foreach ($this->cache_demand_reason as $key => $val) {
7282 if ($val['id'] == $selected) {
7283 print $val['label'];
7284 break;
7285 }
7286 }
7287 } else {
7288 print "&nbsp;";
7289 }
7290 }
7291 }
7292
7293 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7294
7308 public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
7309 {
7310 // phpcs:enable
7311 global $langs;
7312
7313 $ret = '';
7314
7315 if ($htmlname != "none") {
7316 $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7317 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
7318 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
7319 if ($type) {
7320 $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7321 }
7322 $ret .= '<table class="nobordernopadding">';
7323 $ret .= '<tr><td>';
7324 $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
7325 $ret .= '</td>';
7326 $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7327 $ret .= '</tr></table></form>';
7328 } else {
7329 if ($displayhour) {
7330 $ret .= dol_print_date($selected, 'dayhour');
7331 } else {
7332 $ret .= dol_print_date($selected, 'day');
7333 }
7334 }
7335
7336 if (empty($nooutput)) {
7337 print $ret;
7338 }
7339 return $ret;
7340 }
7341
7342
7343 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7344
7355 public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
7356 {
7357 // phpcs:enable
7358 global $langs;
7359
7360 if ($htmlname != "none") {
7361 print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7362 print '<input type="hidden" name="action" value="set' . $htmlname . '">';
7363 print '<input type="hidden" name="token" value="' . newToken() . '">';
7364 print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
7365 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7366 print '</form>';
7367 } else {
7368 if ($selected) {
7369 require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
7370 $theuser = new User($this->db);
7371 $theuser->fetch((int) $selected);
7372 print $theuser->getNomUrl(1);
7373 } else {
7374 print "&nbsp;";
7375 }
7376 }
7377 }
7378
7379
7380 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7381
7395 public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
7396 {
7397 // phpcs:enable
7398 global $langs;
7399
7400 $out = '';
7401 if ($htmlname != "none") {
7402 $out .= '<form method="POST" action="' . $page . '">';
7403 $out .= '<input type="hidden" name="action" value="setmode">';
7404 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7405 if ($type) {
7406 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7407 }
7408 $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
7409 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7410 $out .= '</form>';
7411 } else {
7412 if ($selected) {
7413 $this->load_cache_types_paiements();
7414 $out .= $this->cache_types_paiements[$selected]['label'];
7415 } else {
7416 $out .= "&nbsp;";
7417 }
7418 }
7419
7420 if ($nooutput) {
7421 return $out;
7422 } else {
7423 print $out;
7424 }
7425 return '';
7426 }
7427
7438 public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
7439 {
7440 global $langs;
7441 if ($htmlname != "none") {
7442 print '<form method="POST" action="' . $page . '">';
7443 print '<input type="hidden" name="action" value="settransportmode">';
7444 print '<input type="hidden" name="token" value="' . newToken() . '">';
7445 $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
7446 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7447 print '</form>';
7448 } else {
7449 if ($selected) {
7450 $this->load_cache_transport_mode();
7451 print $this->cache_transport_mode[$selected]['label'];
7452 } else {
7453 print "&nbsp;";
7454 }
7455 }
7456 }
7457
7458 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7459
7468 public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
7469 {
7470 // phpcs:enable
7471 global $langs;
7472 if ($htmlname != "none") {
7473 print '<form method="POST" action="' . $page . '">';
7474 print '<input type="hidden" name="action" value="setmulticurrencycode">';
7475 print '<input type="hidden" name="token" value="' . newToken() . '">';
7476 print $this->selectMultiCurrency($selected, $htmlname, 0);
7477 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7478 print '</form>';
7479 } else {
7480 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
7481 print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
7482 }
7483 }
7484
7485 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7486
7497 public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '', $rate_direct = 0.0)
7498 {
7499 // phpcs:enable
7500 global $langs, $conf;
7501
7502 if ($htmlname != "none") {
7503 print '<form method="POST" action="' . $page . '">';
7504 print '<input type="hidden" name="action" value="setmulticurrencyrate">';
7505 print '<input type="hidden" name="token" value="' . newToken() . '">';
7506 print '<input type="text" class="maxwidth75" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" spellcheck="false" /> ';
7507 print '<select name="calculation_mode" id="calculation_mode">';
7508 print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
7509 print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
7510 print '</select> ';
7511 print ajax_combobox("calculation_mode");
7512 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7513 print '</form>';
7514 } else {
7515 if (!empty($rate)) {
7516 print price($rate, 1, $langs, 0, 0);
7517 if ($currency && $rate != 1) {
7523 if (getDolGlobalString('MULTICURRENCY_USE_RATE_DIRECT')) {
7524 print ' &nbsp; <span class="opacitymedium">(' . price($rate_direct, 1, $langs, 0, 0) . ' ' . $conf->currency . ' = 1 ' . $currency . ')</span>';
7525 } else {
7526 print ' &nbsp; <span class="opacitymedium">(' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')</span>';
7527 }
7528 }
7529 } else {
7530 print 1;
7531 }
7532 }
7533 }
7534
7535 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7536
7554 public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0, $filterabsolutediscount = 0, $filtercreditnote = 0)
7555 {
7556 // phpcs:enable
7557 global $conf, $langs;
7558
7559 if ($htmlname != "none") {
7560 print '<form method="post" action="' . $page . '" class="inline-block">';
7561 print '<input type="hidden" name="action" value="setabsolutediscount">';
7562 print '<input type="hidden" name="token" value="' . newToken() . '">';
7563 print '<div class="inline-block">';
7564 if (!empty($discount_type)) {
7565 if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
7566 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
7567 $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7568 } else {
7569 $translationKey = 'HasCreditNoteFromSupplier';
7570 }
7571 } else {
7572 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7573 $translationKey = 'HasAbsoluteDiscountFromSupplier';
7574 } else {
7575 $translationKey = 'HasCreditNoteFromSupplier';
7576 }
7577 }
7578 } else {
7579 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
7580 if (!$filter || $filter == "fk_facture_source IS NULL") {
7581 $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7582 } else {
7583 $translationKey = 'CompanyHasCreditNote';
7584 }
7585 } else {
7586 if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7587 $translationKey = 'CompanyHasAbsoluteDiscount';
7588 } else {
7589 $translationKey = 'CompanyHasCreditNote';
7590 }
7591 }
7592 }
7593 print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
7594 if (empty($hidelist)) {
7595 print ' ';
7596 }
7597 print '</div>';
7598 if (empty($hidelist)) {
7599 print '<div class="inline-block" style="padding-right: 10px">';
7600 $newfilter = 'discount_type = ' . intval($discount_type);
7601 if (!empty($discount_type)) {
7602 $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
7603 } else {
7604 $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
7605 }
7606 if ($filter) {
7607 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
7608 $newfilter .= ' AND (' . $sanitizedfilter . ')';
7609 }
7610 // output the combo of discounts
7611 $nbqualifiedlines = $this->select_remises((string) $selected, $htmlname, $newfilter, $socid, $maxvalue);
7612 if ($nbqualifiedlines > 0) {
7613 print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
7614 if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7615 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7616 }
7617 if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7618 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7619 }
7620
7621 print '>';
7622 }
7623 print '</div>';
7624 }
7625 if ($more) {
7626 print '<div class="inline-block">';
7627 print $more;
7628 print '</div>';
7629 }
7630 print '</form>';
7631 } else {
7632 if ($selected) {
7633 print $selected;
7634 } else {
7635 print "0";
7636 }
7637 }
7638 }
7639
7640
7641 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7642
7652 public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
7653 {
7654 // phpcs:enable
7655 global $langs;
7656
7657 if ($htmlname != "none") {
7658 print '<form method="post" action="' . $page . '">';
7659 print '<input type="hidden" name="action" value="set_contact">';
7660 print '<input type="hidden" name="token" value="' . newToken() . '">';
7661 print '<table class="nobordernopadding">';
7662 print '<tr><td>';
7663 print $this->selectcontacts($societe->id, $selected, $htmlname);
7664 $num = $this->num;
7665 if ($num == 0) {
7666 $addcontact = (getDolGlobalString('SOCIETE_ADDRESSES_MANAGEMENT') ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
7667 print '<a href="' . DOL_URL_ROOT . '/contact/card.php?socid=' . $societe->id . '&action=create&backtoreferer=1">' . $addcontact . '</a>';
7668 }
7669 print '</td>';
7670 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7671 print '</tr></table></form>';
7672 } else {
7673 if ($selected) {
7674 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
7675 $contact = new Contact($this->db);
7676 $contact->fetch((int) $selected);
7677 print $contact->getFullName($langs);
7678 } else {
7679 print "&nbsp;";
7680 }
7681 }
7682 }
7683
7684 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7685
7702 public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
7703 {
7704 // phpcs:enable
7705 global $langs;
7706
7707 $out = '';
7708 if ($htmlname != "none") {
7709 $limit = getDolGlobalInt('THIRDPARTY_LIMIT_SIZE');
7710
7711 $out .= '<form method="post" action="' . $page . '">';
7712 $out .= '<input type="hidden" name="action" value="set_thirdparty">';
7713 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7714 $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, $limit, 'minwidth100', '', '', 1, array(), false, $excludeids);
7715 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7716 $out .= '</form>';
7717 } else {
7718 if ($selected) {
7719 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
7720 $soc = new Societe($this->db);
7721 $soc->fetch((int) $selected);
7722 $out .= $soc->getNomUrl(0, '');
7723 } else {
7724 $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
7725 }
7726 }
7727
7728 if ($nooutput) {
7729 return $out;
7730 } else {
7731 print $out;
7732 }
7733
7734 return '';
7735 }
7736
7737 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7738
7747 public function select_currency($selected = '', $htmlname = 'currency_id')
7748 {
7749 // phpcs:enable
7750 print $this->selectCurrency($selected, $htmlname);
7751 }
7752
7762 public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
7763 {
7764 global $langs, $user;
7765
7766 $langs->loadCacheCurrencies('');
7767
7768 $out = '';
7769
7770 if ($selected == 'euro' || $selected == 'euros') {
7771 $selected = 'EUR'; // Pour compatibilite
7772 }
7773
7774 $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
7775 if ($useempty) {
7776 $out .= '<option value="-1" selected></option>';
7777 }
7778 foreach ($langs->cache_currencies as $code_iso => $currency) {
7779 $labeltoshow = $currency['label'];
7780 if ($mode == 1) {
7781 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
7782 } elseif ($mode == 2) {
7783 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso.' - '.$langs->getCurrencySymbol($code_iso) . ')</span>';
7784 } else {
7785 $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
7786 }
7787
7788 if ($selected && $selected == $code_iso) {
7789 $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7790 } else {
7791 $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7792 }
7793 $out .= dol_string_nohtmltag($labeltoshow);
7794 $out .= '</option>';
7795 }
7796 $out .= '</select>';
7797 if ($user->admin) {
7798 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
7799 }
7800
7801 // Make select dynamic
7802 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7803 $out .= ajax_combobox($htmlname);
7804
7805 return $out;
7806 }
7807
7820 public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = 'maxwidth200 widthcentpercentminusx')
7821 {
7822 global $conf, $langs;
7823
7824 $langs->loadCacheCurrencies(''); // Load ->cache_currencies
7825
7826 $TCurrency = array();
7827
7828 $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
7829 $sql .= " WHERE entity IN ('" . getEntity('multicurrency') . "')";
7830 if ($filter) {
7831 $sql .= forgeSQLFromUniversalSearchCriteria($filter);
7832 }
7833 $resql = $this->db->query($sql);
7834 if ($resql) {
7835 while ($obj = $this->db->fetch_object($resql)) {
7836 $TCurrency[$obj->code] = $obj->code;
7837 }
7838 }
7839
7840 $out = '';
7841 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7842 if ($useempty) {
7843 $out .= '<option value="">&nbsp;</option>';
7844 }
7845 // If company current currency not in table, we add it into list. Should always be available.
7846 if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
7847 $TCurrency[$conf->currency] = $conf->currency;
7848 }
7849 if (count($TCurrency) > 0) {
7850 foreach ($langs->cache_currencies as $code_iso => $currency) {
7851 if (isset($TCurrency[$code_iso])) {
7852 if (!empty($selected) && $selected == $code_iso) {
7853 $out .= '<option value="' . $code_iso . '" selected="selected">';
7854 } else {
7855 $out .= '<option value="' . $code_iso . '">';
7856 }
7857
7858 $out .= $currency['label'];
7859 $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
7860 $out .= '</option>';
7861 }
7862 }
7863 }
7864
7865 $out .= '</select>';
7866
7867 // Make select dynamic
7868 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7869 $out .= ajax_combobox($htmlname);
7870
7871 return $out;
7872 }
7873
7874 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7875
7882 public function load_cache_vatrates($country_code)
7883 {
7884 // phpcs:enable
7885 global $langs, $user;
7886
7887 $num = count($this->cache_vatrates);
7888 if ($num > 0) {
7889 return $num; // Cache already loaded
7890 }
7891
7892 dol_syslog(__METHOD__, LOG_DEBUG);
7893
7894 $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";
7895 $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
7896 $sql .= " WHERE t.fk_pays = c.rowid";
7897 $sql .= " AND t.active > 0";
7898 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7899 $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
7900 $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
7901
7902 $resql = $this->db->query($sql);
7903 if ($resql) {
7904 $num = $this->db->num_rows($resql);
7905 if ($num) {
7906 for ($i = 0; $i < $num; $i++) {
7907 $obj = $this->db->fetch_object($resql);
7908
7909 $tmparray = array();
7910 $tmparray['rowid'] = (int) $obj->rowid;
7911 $tmparray['type_vat'] = ($obj->type_vat <= 0 ? 0 : $obj->type_vat); // Some version have type_vat corrupted with value -1
7912 $tmparray['code'] = $obj->code;
7913 $tmparray['txtva'] = $obj->taux;
7914 $tmparray['nprtva'] = $obj->recuperableonly;
7915 $tmparray['localtax1'] = $obj->localtax1;
7916 $tmparray['localtax1_type'] = $obj->localtax1_type;
7917 $tmparray['localtax2'] = $obj->localtax2;
7918 $tmparray['localtax2_type'] = $obj->localtax1_type;
7919 $tmparray['einvoice_vatex'] = $obj->einvoice_vatex;
7920
7921 $tmparray['label'] = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
7922 $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
7923 $positiverates = '';
7924 if ($obj->taux) {
7925 $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
7926 }
7927 if ($obj->localtax1) {
7928 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
7929 }
7930 if ($obj->localtax2) {
7931 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
7932 }
7933 if (empty($positiverates)) {
7934 $positiverates = '0';
7935 }
7936 $tmparray['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
7937
7938 $this->cache_vatrates[$obj->rowid] = $tmparray;
7939 }
7940
7941 return $num;
7942 } else {
7943 $this->error = '<span class="error">';
7944 $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
7945 $reg = array();
7946 if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
7947 $langs->load("errors");
7948 $new_country_code = $reg[1];
7949 $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_country', 'code', 'rowid');
7950 $this->error .= '<br>'.$langs->trans("ErrorFixThisHere", DOL_URL_ROOT.'/admin/dict.php?id=10'.($country_id > 0 ? '&countryidforinsert='.$country_id : ''));
7951 }
7952 $this->error .= '</span>';
7953 return -1;
7954 }
7955 } else {
7956 $this->error = '<span class="error">' . $this->db->error() . '</span>';
7957 return -2;
7958 }
7959 }
7960
7961 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7962
7985 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)
7986 {
7987 // phpcs:enable
7988 global $langs, $mysoc, $hookmanager;
7989
7990 $langs->load('errors');
7991
7992 $return = '';
7993 // Bypass the default method
7994 $hookmanager->initHooks(array('commonobject'));
7995 $info_bits == 1 ? $is_npr = 1 : $is_npr = 0;
7996 $parameters = array(
7997 'htmlname' => $htmlname,
7998 'selectedrate' => $selectedrate,
7999 'seller' => $societe_vendeuse,
8000 'buyer' => $societe_acheteuse,
8001 'idprod' => $idprod,
8002 'is_npr' => $is_npr,
8003 'type' => $type,
8004 'options_only' => $options_only,
8005 'mode' => $mode,
8006 'type_vat' => $type_vat
8007 );
8008 $reshook = $hookmanager->executeHooks('load_tva', $parameters);
8009 if ($reshook > 0) {
8010 return $hookmanager->resPrint;
8011 } elseif ($reshook === 0) {
8012 $return .= $hookmanager->resPrint;
8013 }
8014
8015 // Define defaultnpr, defaultttx and defaultcode
8016 $defaultnpr = ($info_bits & 0x01);
8017 $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
8018 $defaulttx = str_replace('*', '', $selectedrate);
8019 $defaultcode = '';
8020 $reg = array();
8021 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8022 $defaultcode = $reg[1];
8023 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8024 }
8025 //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
8026
8027 // Check parameters
8028 if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
8029 if ($societe_vendeuse->id == $mysoc->id) {
8030 $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
8031 } else {
8032 $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
8033 }
8034 return $return;
8035 }
8036
8037 //var_dump($societe_acheteuse);
8038 //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";
8039 //exit;
8040
8041 // Define list of countries to use to search VAT rates to show
8042 // First we defined code_country to use to find list.
8043 // country_code must be a c_country ISO code (e.g. 'FR', 'CH'). In some setups it may hold a
8044 // country label (e.g. 'Suisse') instead, which would make the "c.code IN (...)" lookup done by
8045 // load_cache_vatrates() match nothing and wrongly force the VAT rate to 0%. A valid ISO code is
8046 // always 2 chars, so when the value is not a well formed ISO code (empty or a label) and we have
8047 // a valid country id, we recover the ISO code from the authoritative country id. This way we do
8048 // not run any SQL on each page access when we already have a valid ISO code.
8049 $sellercountrycode = is_object($societe_vendeuse) ? $societe_vendeuse->country_code : $mysoc->country_code;
8050 $sellercountryid = is_object($societe_vendeuse) ? $societe_vendeuse->country_id : $mysoc->country_id;
8051 if ((int) $sellercountryid > 0 && strlen((string) $sellercountrycode) != 2) {
8052 $tmpcountrycode = dol_getIdFromCode($this->db, (string) $sellercountryid, 'c_country', 'rowid', 'code');
8053 if (!empty($tmpcountrycode) && !is_numeric($tmpcountrycode)) {
8054 $sellercountrycode = $tmpcountrycode;
8055 }
8056 }
8057 $code_country = "'" . $sellercountrycode . "'"; // Pour compatibilite ascendente
8058
8059 if ($societe_vendeuse == $mysoc && getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) { // If option to have vat for end customer for services is on
8060 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8061 // If SERVICE_ARE_ECOMMERCE_200238EC=1 combo list vat rate of purchaser and seller countries
8062 // If SERVICE_ARE_ECOMMERCE_200238EC=2 combo list only the vat rate of the purchaser country
8063 $selectVatComboMode = getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC');
8064 if (is_object($societe_vendeuse) && is_object($societe_acheteuse) && isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()) {
8065 // We also add the buyer country code
8066 if (is_numeric($type)) {
8067 if ($type == 1) { // We know product is a service
8068 switch ($selectVatComboMode) {
8069 case '1':
8070 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8071 break;
8072 case '2':
8073 $code_country = "'" . $societe_acheteuse->country_code . "'";
8074 break;
8075 }
8076 }
8077 } elseif (!$idprod) { // We don't know type of product
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 } else {
8087 $prodstatic = new Product($this->db);
8088 $prodstatic->fetch($idprod);
8089 if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
8090 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8091 }
8092 }
8093 }
8094 }
8095
8096 // Now we load the list of VAT
8097 $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
8098
8099 // Keep only the VAT qualified for $type_vat
8100 $arrayofvatrates = array();
8101 foreach ($this->cache_vatrates as $cachevalue) {
8102 if (empty($cachevalue['type_vat']) || $cachevalue['type_vat'] == $type_vat) {
8103 $arrayofvatrates[] = $cachevalue;
8104 }
8105 }
8106
8107 $num = count($arrayofvatrates);
8108 if ($num > 0) {
8109 // Define the vat rate to preselect (if defaulttx not forced so is -1 or '')
8110 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8111 // Define a default thirdparty to use if the seller or buyer is not defined
8112 $tmpthirdparty = new Societe($this->db);
8113 $tmpthirdparty->country_code = $mysoc->country_code;
8114
8115 $defaulttx = get_default_tva(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8116 $defaultnpr = get_default_npr(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8117
8118 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8119 $defaultcode = $reg[1];
8120 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8121 }
8122 if (empty($defaulttx)) {
8123 $defaultnpr = 0;
8124 }
8125 }
8126
8127 // If we fails to find a default vat rate, we take the last one in list
8128 // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
8129 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8130 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8131 // We take the last one found in list
8132 $defaulttx = $arrayofvatrates[$num - 1]['txtva'];
8133 } else {
8134 // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
8135 $defaulttx = '';
8136 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8137 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8138 }
8139 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8140 $defaultcode = $reg[1];
8141 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8142 }
8143 }
8144 }
8145
8146 // Disabled is true if the seller is not subject to VAT
8147 $disabled = false;
8148 $title = '';
8149 if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && empty($societe_vendeuse->tva_assuj)) {
8150 // 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
8151 // EXPENSEREPORT_OVERRIDE_VAT is a strange option that allow to override/enable VAT regardless of sellet vat option - needed for expense report if
8152 // expense report used for business expenses instead of using supplier invoices (but this is a very bad idea !)
8153 if (!getDolGlobalString('EXPENSEREPORT_OVERRIDE_VAT')) {
8154 $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
8155 $disabled = true;
8156 }
8157 }
8158
8159 if (!$options_only) {
8160 $return .= '<select class="flat valignmiddle minwidth75imp maxwidth100 right" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
8161 }
8162
8163 $selectedfound = false;
8164 foreach ($arrayofvatrates as $rate) {
8165 // Keep only 0 if seller is not subject to VAT
8166 if ($disabled && $rate['txtva'] != 0) {
8167 continue;
8168 }
8169
8170 // Define key to use into select list
8171 $key = $rate['txtva'];
8172 $key .= $rate['nprtva'] ? '*' : '';
8173 if ($mode > 0 && $rate['code']) {
8174 $key .= ' (' . $rate['code'] . ')';
8175 }
8176 if ($mode < 0) {
8177 $key = $rate['rowid'];
8178 }
8179
8180 $return .= '<option value="' . $key . '" data-vatid="'.$rate['rowid'].'"';
8181 if (!$selectedfound) {
8182 if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
8183 if ($defaultcode == $rate['code']) {
8184 $return .= ' selected';
8185 $selectedfound = true;
8186 }
8187 } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
8188 $return .= ' selected';
8189 $selectedfound = true;
8190 }
8191 }
8192 $return .= '>';
8193
8194 // Show label of VAT
8195 if ($mysoc->country_code == 'IN' || getDolGlobalString('MAIN_VAT_LABEL_IS_POSITIVE_RATES')) {
8196 // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
8197 $return .= $rate['labelpositiverates'];
8198 } else {
8199 // Simple label
8200 $return .= vatrate($rate['label']);
8201 }
8202
8203 //$return.=($rate['code']?' '.$rate['code']:'');
8204 $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
8205
8206 $return .= '</option>';
8207 }
8208
8209 if (!$options_only) {
8210 $return .= '</select>';
8211 //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
8212 }
8213 } else {
8214 $return .= $this->error;
8215 }
8216
8217 $this->num = $num;
8218 return $return;
8219 }
8220
8221
8222 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8223
8248 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 = '')
8249 {
8250 // phpcs:enable
8251 dol_syslog(__METHOD__ . ': using select_date is deprecated. Use selectDate instead.', LOG_WARNING);
8252 $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
8253 if (!empty($nooutput)) {
8254 return $retstring;
8255 }
8256 print $retstring;
8257
8258 return '';
8259 }
8260
8276 public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
8277 {
8278 global $langs;
8279
8280 $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
8281 if ($forcenewline) {
8282 $ret .= '<br>';
8283 }
8284 $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
8285 return $ret;
8286 }
8287
8316 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 = '')
8317 {
8318 global $conf, $langs;
8319
8320 if ($gm === 'auto') {
8321 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
8322 }
8323
8324 $retstring = '';
8325
8326 if ($prefix == '') {
8327 $prefix = 're';
8328 }
8329 if ($h == '') {
8330 $h = 0;
8331 }
8332 if ($m == '') {
8333 $m = 0;
8334 }
8335 $emptydate = 0;
8336 $emptyhours = 0;
8337 if ($stepminutes <= 0 || $stepminutes > 30) {
8338 $stepminutes = 1;
8339 }
8340 if ($empty == 1) {
8341 $emptydate = 1;
8342 $emptyhours = 1;
8343 }
8344 if ($empty == 2) {
8345 $emptydate = 0;
8346 $emptyhours = 1;
8347 }
8348 $orig_set_time = $set_time;
8349
8350 if ($set_time === '' && $emptydate == 0) {
8351 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8352 if ($gm == 'tzuser' || $gm == 'tzuserrel') {
8353 $set_time = dol_now($gm);
8354 } else {
8355 $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
8356 }
8357 }
8358
8359 // Analysis of the preselected date
8360 $reg = array();
8361 $shour = '';
8362 $smin = '';
8363 $ssec = '';
8364 if (!empty($set_time) && preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', (string) $set_time, $reg)) { // deprecated usage
8365 // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
8366 $syear = (!empty($reg[1]) ? $reg[1] : '');
8367 $smonth = (!empty($reg[2]) ? $reg[2] : '');
8368 $sday = (!empty($reg[3]) ? $reg[3] : '');
8369 $shour = (!empty($reg[4]) ? $reg[4] : '');
8370 $smin = (!empty($reg[5]) ? $reg[5] : '');
8371 } elseif (strval($set_time) != '' && $set_time != -1) {
8372 // set_time est un timestamps (0 possible)
8373 $syear = dol_print_date($set_time, "%Y", $gm);
8374 $smonth = dol_print_date($set_time, "%m", $gm);
8375 $sday = dol_print_date($set_time, "%d", $gm);
8376 if ($orig_set_time != '') {
8377 $shour = dol_print_date($set_time, "%H", $gm);
8378 $smin = dol_print_date($set_time, "%M", $gm);
8379 $ssec = dol_print_date($set_time, "%S", $gm);
8380 }
8381 } else {
8382 // Date est '' ou vaut -1
8383 $syear = '';
8384 $smonth = '';
8385 $sday = '';
8386 $shour = getDolGlobalString('MAIN_DEFAULT_DATE_HOUR', ($h == -1 ? '23' : ''));
8387 $smin = getDolGlobalString('MAIN_DEFAULT_DATE_MIN', ($h == -1 ? '59' : ''));
8388 $ssec = getDolGlobalString('MAIN_DEFAULT_DATE_SEC', ($h == -1 ? '59' : ''));
8389 }
8390 if ($h == 3 || $h == 4) {
8391 $shour = '';
8392 }
8393 if ($m == 3) {
8394 $smin = '';
8395 }
8396
8397 $nowgmt = dol_now('gmt');
8398 //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
8399
8400 // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
8401 $usecalendar = 'combo';
8402 if (!empty($conf->use_javascript_ajax) && (!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') != "none")) {
8403 $usecalendar = ((!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') == 'eldy') ? 'jquery' : getDolGlobalString("MAIN_POPUP_CALENDAR"));
8404 }
8405 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8406 // If we use a text browser or screen reader, we use the 'combo' date selector
8407 $usecalendar = 'html';
8408 }
8409
8410 if ($d) {
8411 // Show date with popup
8412 if ($usecalendar != 'combo') {
8413 // Set $format and $formatjs and $formatjquery
8414 $reduceformat = (!empty($conf->dol_optimize_smallscreen) ? 1 : 0); // Test on original $format param.
8415 if ($reduceformat) {
8416 $format = str_replace('%Y', '%y', $langs->transnoentitiesnoconv("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8417 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8418 $formatjs = str_replace('yyyy', 'yy', $langs->transnoentitiesnoconv("FormatDateShortJavaInput"));
8419 $formatjquery = str_replace('yyyy', 'yy', $langs->trans("FormatDateShortJQueryInput"));
8420 } else {
8421 $format = $langs->transnoentitiesnoconv("FormatDateShortInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8422 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8423 $formatjs = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8424 $formatjquery = $langs->trans("FormatDateShortJQueryInput");
8425 }
8426
8427 // Set formatted_date (for example: '%d/%m/%Y', '%m-%d-%y', ...
8428 $formatted_date = '';
8429 if (strval($set_time) != '' && $set_time != -1) {
8430 $formatted_date = dol_print_date($set_time, $format, $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8431 }
8432
8433 // Calendrier popup version eldy
8434 if ($usecalendar == "eldy") {
8435 // To have this manager working back, you must retrieve all functions showDP child found into the lib_head.js of v4 for example
8436 // and load the js that contains them so the call of showDP will works.
8437 /*
8438 // Input area to enter date manually
8439 $retstring .= '<!-- datepicker usecalendar=eldy --><input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formatted_date . '"';
8440 $retstring .= ($disabled ? ' disabled' : '');
8441 $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($formatjslong")) . '\'); "'; // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8442 $retstring .= ' autocomplete="off">';
8443
8444 // Icon calendar
8445 $retstringbuttom = '';
8446 if (!$disabled) {
8447 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
8448 $base = DOL_URL_ROOT . '/core/';
8449 $retstringbuttom .= ' onClick="showDP(\'' . dol_escape_js($base) . '\',\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\',\'' . dol_escape_js($langs->defaultlang) . '\');"';
8450 $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8451 } else {
8452 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8453 }
8454 $retstring = $retstringbuttom . $retstring;
8455
8456 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8457 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8458 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8459 */
8460 } elseif ($usecalendar == 'jquery' || $usecalendar == 'html') {
8461 if (!$disabled && $usecalendar != 'html') {
8462 // Output javascript for datepicker
8463 $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (idate('Y') - 100));
8464 $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (idate('Y') + 100));
8465
8466 $retstring .= '<!-- datepicker usecalendar='.$usecalendar.' --><script nonce="' . getNonce() . '" type="text/javascript">';
8467 $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
8468 dateFormat: '" . dol_escape_js($formatjquery) . "',
8469 autoclose: true,
8470 todayHighlight: true,
8471 yearRange: '" . $minYear . ":" . $maxYear . "',";
8472 if (!empty($conf->dol_use_jmobile)) {
8473 $retstring .= "
8474 beforeShow: function (input, datePicker) {
8475 input.disabled = true;
8476 },
8477 onClose: function (dateText, datePicker) {
8478 this.disabled = false;
8479 },
8480 ";
8481 }
8482 // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
8483 if (!getDolGlobalString('MAIN_POPUP_CALENDAR_ON_FOCUS')) {
8484 $buttonImage = $calendarpicto ?: DOL_URL_ROOT . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png";
8485 $retstring .= "
8486 showOn: 'button', /* both has problem with autocompletion */
8487 buttonImage: '" . $buttonImage . "',
8488 buttonImageOnly: true";
8489 }
8490 $retstring .= "
8491 }) });";
8492 $retstring .= "</script>";
8493 }
8494
8495 // Input area to enter date manually
8496 $retstring .= '<div class="nowraponall inline-block divfordateinput">';
8497 $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="'.($usecalendar == 'html' ? "date" : "text").'" class="maxwidthdate'.(getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' textbrowser' : '').' center" maxlength="11" value="'.$formatted_date.'"';
8498 $retstring .= ($disabled ? ' disabled' : '');
8499 $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
8500 $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
8501 $retstring .= ' autocomplete="off">';
8502
8503 // Icon calendar
8504 if ($disabled) {
8505 $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink ui-datepicker-notrigger"') . '</button>';
8506 $retstring .= $retstringbutton;
8507 }
8508
8509 $retstring .= '</div>';
8510 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8511 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8512 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8513 } else {
8514 $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
8515 }
8516 } else {
8517 // Show date with combo selects
8518 // Day
8519 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
8520
8521 if ($emptydate || $set_time == -1) {
8522 $retstring .= '<option value="0" selected>&nbsp;</option>';
8523 }
8524
8525 for ($day = 1; $day <= 31; $day++) {
8526 $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
8527 }
8528
8529 $retstring .= "</select>";
8530
8531 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
8532 if ($emptydate || $set_time == -1) {
8533 $retstring .= '<option value="0" selected>&nbsp;</option>';
8534 }
8535
8536 // Month
8537 for ($month = 1; $month <= 12; $month++) {
8538 $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
8539 $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
8540 $retstring .= "</option>";
8541 }
8542 $retstring .= "</select>";
8543
8544 // Year
8545 if ($emptydate || $set_time == -1) {
8546 $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 . '">';
8547 } else {
8548 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
8549
8550 $syear = (int) $syear;
8551 for ($year = $syear - 10; $year < (int) $syear + 10; $year++) {
8552 $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
8553 }
8554 $retstring .= "</select>\n";
8555 }
8556 }
8557 }
8558
8559 if ($d && $h) {
8560 $retstring .= (($h == 2 || $h == 4) ? '<br>' : ' ');
8561 $retstring .= '<span class="nowraponall">';
8562 }
8563
8564 if ($h) {
8565 $hourstart = 0;
8566 $hourend = 24;
8567 if ($openinghours != '') {
8568 $openinghours = explode(',', $openinghours);
8569 $hourstart = $openinghours[0];
8570 $hourend = $openinghours[1];
8571 if ($hourend < $hourstart) {
8572 $hourend = $hourstart;
8573 }
8574 }
8575
8576 // Show hour
8577 $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
8578 $retstring .= ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
8579 if ($emptyhours) {
8580 $retstring .= '<option value="-1">&nbsp;</option>';
8581 }
8582 for ($hour = $hourstart; $hour < $hourend; $hour++) {
8583 if (strlen($hour) < 2) {
8584 $hour = "0" . $hour;
8585 }
8586 $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
8587 $retstring .= '</option>';
8588 }
8589 $retstring .= '</select>';
8590
8591 if ($disabled) {
8592 $retstring .= '<input type="hidden" id="' . $prefix . 'hour" name="' . $prefix . 'hour" value="' . $shour . '">' . "\n";
8593 }
8594 if ($m) {
8595 $retstring .= ":";
8596 }
8597 }
8598
8599 if ($m) {
8600 // Show minutes
8601 $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
8602 $retstring .= ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
8603 if ($emptyhours) {
8604 $retstring .= '<option value="-1">&nbsp;</option>';
8605 }
8606 for ($min = 0; $min < 60; $min += $stepminutes) {
8607 $min_str = sprintf("%02d", $min);
8608 $retstring .= '<option value="' . $min_str . '"' . (($min_str == $smin) ? ' selected' : '') . '>' . $min_str . '</option>';
8609 }
8610 $retstring .= '</select>';
8611 if ($disabled) {
8612 $retstring .= '<input type="hidden" id="' . $prefix . 'min" name="' . $prefix . 'min" value="' . $smin . '">' . "\n";
8613 }
8614 // Add also seconds
8615 $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
8616 }
8617
8618 if ($d && $h) {
8619 $retstring .= '</span>';
8620 }
8621
8622 // Add a "Now" link
8623 if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
8624 // Script which will be inserted in the onClick of the "Now" link
8625 $reset_scripts = "";
8626 if ($addnowlink == 2) { // local computer time
8627 // pad add leading 0 on numbers
8628 $reset_scripts .= "Number.prototype.pad = function(size) {
8629 var s = String(this);
8630 while (s.length < (size || 2)) {s = '0' + s;}
8631 return s;
8632 };
8633 var d = new Date();";
8634 }
8635
8636 // Generate the date part, depending on the use or not of the javascript calendar
8637 if ($addnowlink == 1) { // server time expressed in user time setup
8638 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8639 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8640 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8641 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8642 } elseif ($addnowlink == 2) {
8643 /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
8644 * This break application for foreign languages.
8645 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
8646 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
8647 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
8648 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
8649 */
8650 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8651 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8652 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8653 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8654 }
8655 /*if ($usecalendar == "eldy")
8656 {
8657 $base=DOL_URL_ROOT.'/core/';
8658 $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
8659 }
8660 else
8661 {
8662 $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
8663 $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
8664 $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
8665 }*/
8666 // Update the hour part
8667 if ($h) {
8668 if ($fullday) {
8669 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8670 }
8671 //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
8672 if ($addnowlink == 1) {
8673 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8674 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8675 } elseif ($addnowlink == 2) {
8676 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
8677 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8678 }
8679
8680 if ($fullday) {
8681 $reset_scripts .= ' } ';
8682 }
8683 }
8684 // Update the minute part
8685 if ($m) {
8686 if ($fullday) {
8687 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8688 }
8689 //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
8690 if ($addnowlink == 1) {
8691 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8692 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8693 } elseif ($addnowlink == 2) {
8694 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
8695 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8696 }
8697 if ($fullday) {
8698 $reset_scripts .= ' } ';
8699 }
8700 }
8701 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8702 if ($reset_scripts && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8703 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
8704 $retstring .= $langs->trans("Now");
8705 $retstring .= '</button> ';
8706 }
8707 }
8708
8709 // Add a "Plus one hour" link
8710 if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
8711 // Script which will be inserted in the onClick of the "Add plusone" link
8712 $reset_scripts = "";
8713
8714 // Generate the date part, depending on the use or not of the javascript calendar
8715 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
8716 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8717 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8718 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8719 // Update the hour part
8720 if ($h) {
8721 if ($fullday) {
8722 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8723 }
8724 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8725 if ($fullday) {
8726 $reset_scripts .= ' } ';
8727 }
8728 }
8729 // Update the minute part
8730 if ($m) {
8731 if ($fullday) {
8732 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8733 }
8734 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8735 if ($fullday) {
8736 $reset_scripts .= ' } ';
8737 }
8738 }
8739 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8740 if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
8741 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
8742 $retstring .= $langs->trans("DateStartPlusOne");
8743 $retstring .= '</button> ';
8744 }
8745 }
8746
8747 // Add a link to set data
8748 if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
8749 if (!is_array($adddateof)) {
8750 $arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
8751 } else {
8752 $arrayofdateof = $adddateof;
8753 }
8754 foreach ($arrayofdateof as $valuedateof) {
8755 $tmpadddateof = empty($valuedateof['adddateof']) ? 0 : $valuedateof['adddateof'];
8756 $tmplabeladddateof = empty($valuedateof['labeladddateof']) ? '' : $valuedateof['labeladddateof'];
8757 $tmparray = dol_getdate($tmpadddateof);
8758 if (empty($tmplabeladddateof)) {
8759 $tmplabeladddateof = $langs->trans("DateInvoice");
8760 }
8761 $reset_scripts = 'console.log(\'Click on now link\'); ';
8762 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($tmpadddateof, 'dayinputnoreduce').'\');';
8763 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
8764 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
8765 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
8766 $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="'.$reset_scripts.'">'.$tmplabeladddateof.'</button>';
8767 }
8768 }
8769
8770 return $retstring;
8771 }
8772
8782 public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array(), $morecss = 'minwidth75 maxwidth100')
8783 {
8784 global $langs;
8785
8786 $TDurationTypes = $this->getDurationTypes($langs);
8787
8788 // Removed undesired duration types
8789 foreach ($excludetypes as $value) {
8790 unset($TDurationTypes[$value]);
8791 }
8792
8793 $retstring = '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
8794 foreach ($TDurationTypes as $key => $typeduration) {
8795 $retstring .= '<option value="' . $key . '"';
8796 if ($key == $selected) {
8797 $retstring .= " selected";
8798 }
8799 $retstring .= ">" . $typeduration . "</option>";
8800 }
8801 $retstring .= "</select>";
8802
8803 $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
8804
8805 return $retstring;
8806 }
8807
8808 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8809
8823 public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
8824 {
8825 // phpcs:enable
8826 global $langs;
8827
8828 $retstring = '<span class="nowraponall">';
8829
8830 $hourSelected = '';
8831 $minSelected = '';
8832
8833 // Hours
8834 if ($iSecond != '') {
8835 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8836
8837 $hourSelected = convertSecondToTime($iSecond, 'allhour');
8838 $minSelected = convertSecondToTime($iSecond, 'min');
8839 }
8840
8841 if ($typehour == 'select') {
8842 $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
8843 for ($hour = 0; $hour < 25; $hour++) { // For a duration, we allow 24 hours
8844 $retstring .= '<option value="' . $hour . '"';
8845 if (is_numeric($hourSelected) && $hourSelected == $hour) {
8846 $retstring .= " selected";
8847 }
8848 $retstring .= ">" . $hour . "</option>";
8849 }
8850 $retstring .= "</select>";
8851 } elseif ($typehour == 'text' || $typehour == 'textselect') {
8852 $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
8853 } else {
8854 return 'BadValueForParameterTypeHour';
8855 }
8856
8857 if ($typehour != 'text') {
8858 $retstring .= ' ' . $langs->trans('HourShort');
8859 } else {
8860 $retstring .= '<span class="">:</span>';
8861 }
8862
8863 // Minutes
8864 if ($minunderhours) {
8865 $retstring .= '<br>';
8866 } else {
8867 if ($typehour != 'text') {
8868 $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
8869 }
8870 }
8871
8872 if ($typehour == 'select' || $typehour == 'textselect') {
8873 $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
8874 $step = getDolGlobalInt('MAIN_DURATION_STEP');
8875 $duration_step = ($step > 0) ? $step : 5;
8876 for ($min = 0; $min <= 59; $min += $duration_step) {
8877 $retstring .= '<option value="' . $min . '"';
8878 if (is_numeric($minSelected) && $minSelected == $min) {
8879 $retstring .= ' selected';
8880 }
8881 $retstring .= '>' . $min . '</option>';
8882 }
8883 $retstring .= "</select>";
8884 } elseif ($typehour == 'text') {
8885 $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
8886 }
8887
8888 if ($typehour != 'text') {
8889 $retstring .= ' ' . $langs->trans('MinuteShort');
8890 }
8891
8892 $retstring .= "</span>";
8893
8894 if (!empty($nooutput)) {
8895 return $retstring;
8896 }
8897
8898 print $retstring;
8899
8900 return '';
8901 }
8902
8922 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)
8923 {
8924 global $langs, $conf;
8925
8926 $out = '';
8927
8928 // check parameters
8929 if (is_null($ajaxoptions)) {
8930 $ajaxoptions = array();
8931 }
8932
8933 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
8934 $placeholder = '';
8935
8936 if ($selected && empty($selected_input_value)) {
8937 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
8938 $tickettmpselect = new Ticket($this->db);
8939 $tickettmpselect->fetch((int) $selected);
8940 $selected_input_value = $tickettmpselect->ref;
8941 unset($tickettmpselect);
8942 }
8943
8944 $urloption = '';
8945 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
8946
8947 if (empty($hidelabel)) {
8948 $out .= $langs->trans("RefOrLabel") . ' : ';
8949 } elseif ($hidelabel > 1) {
8950 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
8951 if ($hidelabel == 2) {
8952 $out .= img_picto($langs->trans("Search"), 'search');
8953 }
8954 }
8955 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
8956 if ($hidelabel == 3) {
8957 $out .= img_picto($langs->trans("Search"), 'search');
8958 }
8959 } else {
8960 $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
8961 }
8962
8963 if (empty($nooutput)) {
8964 print $out;
8965 } else {
8966 return $out;
8967 }
8968 return '';
8969 }
8970
8971
8988 public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
8989 {
8990 global $langs;
8991
8992 $out = '';
8993 $outarray = array();
8994
8995 $selectFields = " p.rowid, p.ref, p.message";
8996
8997 $sql = "SELECT ";
8998 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
8999 $sql .= " FROM " . $this->db->prefix() . "ticket as p";
9000 $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
9001
9002 // Add criteria on ref/label
9003 if ($filterkey != '') {
9004 $sql .= ' AND (';
9005 $prefix = getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if TICKET_DONOTSEARCH_ANYWHERE is on
9006 // For natural search
9007 $search_crit = explode(' ', $filterkey);
9008 $i = 0;
9009 if (count($search_crit) > 1) {
9010 $sql .= "(";
9011 }
9012 foreach ($search_crit as $crit) {
9013 if ($i > 0) {
9014 $sql .= " AND ";
9015 }
9016 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9017 $sql .= ")";
9018 $i++;
9019 }
9020 if (count($search_crit) > 1) {
9021 $sql .= ")";
9022 }
9023 $sql .= ')';
9024 }
9025
9026 $sql .= $this->db->plimit($limit, 0);
9027
9028 // Build output string
9029 dol_syslog(get_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
9030 $result = $this->db->query($sql);
9031 if ($result) {
9032 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
9033 require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
9034
9035 $num = $this->db->num_rows($result);
9036
9037 $events = array();
9038
9039 if (!$forcecombo) {
9040 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9041 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('TICKET_USE_SEARCH_TO_SELECT'));
9042 }
9043
9044 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9045
9046 $textifempty = '';
9047 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9048 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9049 if (getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9050 if ($showempty && !is_numeric($showempty)) {
9051 $textifempty = $langs->trans($showempty);
9052 } else {
9053 $textifempty .= $langs->trans("All");
9054 }
9055 } else {
9056 if ($showempty && !is_numeric($showempty)) {
9057 $textifempty = $langs->trans($showempty);
9058 }
9059 }
9060 if ($showempty) {
9061 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9062 }
9063
9064 $i = 0;
9065 while ($num && $i < $num) {
9066 $opt = '';
9067 $optJson = array();
9068 $objp = $this->db->fetch_object($result);
9069
9070 $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
9071 '@phan-var-force array{key:string,value:mixed,type:int} $optJson';
9072 // Add new entry
9073 // "key" value of json key array is used by jQuery automatically as selected value
9074 // "label" value of json key array is used by jQuery automatically as text for combo box
9075 $out .= $opt;
9076 array_push($outarray, $optJson);
9077
9078 $i++;
9079 }
9080
9081 $out .= '</select>';
9082
9083 $this->db->free($result);
9084
9085 if (empty($outputmode)) {
9086 return $out;
9087 }
9088 return $outarray;
9089 } else {
9090 dol_print_error($this->db);
9091 }
9092
9093 return array();
9094 }
9095
9107 protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9108 {
9109 $outkey = '';
9110 $outref = '';
9111 $outtype = '';
9112
9113 $outkey = $objp->rowid;
9114 $outref = $objp->ref;
9115
9116 $opt = '<option value="' . $objp->rowid . '"';
9117 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9118 $opt .= '>';
9119 $opt .= $objp->ref;
9120 $objRef = $objp->ref;
9121 if (!empty($filterkey) && $filterkey != '') {
9122 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
9123 }
9124
9125 $opt .= "</option>\n";
9126 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9127 }
9128
9148 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)
9149 {
9150 global $langs, $conf;
9151
9152 $out = '';
9153
9154 // check parameters
9155 if (is_null($ajaxoptions)) {
9156 $ajaxoptions = array();
9157 }
9158
9159 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9160 $placeholder = '';
9161
9162 if ($selected && empty($selected_input_value)) {
9163 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9164 $projecttmpselect = new Project($this->db);
9165 $projecttmpselect->fetch((int) $selected);
9166 $selected_input_value = $projecttmpselect->ref;
9167 unset($projecttmpselect);
9168 }
9169
9170 $urloption = '';
9171 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9172
9173 if (empty($hidelabel)) {
9174 $out .= $langs->trans("RefOrLabel") . ' : ';
9175 } elseif ($hidelabel > 1) {
9176 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9177 if ($hidelabel == 2) {
9178 $out .= img_picto($langs->trans("Search"), 'search');
9179 }
9180 }
9181 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9182 if ($hidelabel == 3) {
9183 $out .= img_picto($langs->trans("Search"), 'search');
9184 }
9185 } else {
9186 $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
9187 }
9188
9189 if (empty($nooutput)) {
9190 print $out;
9191 } else {
9192 return $out;
9193 }
9194 return '';
9195 }
9196
9213 public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9214 {
9215 global $langs, $conf;
9216
9217 $out = '';
9218 $outarray = array();
9219
9220 $selectFields = " p.rowid, p.ref";
9221
9222 $sql = "SELECT ";
9223 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9224 $sql .= " FROM " . $this->db->prefix() . "projet as p";
9225 $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
9226
9227 // Add criteria on ref/label
9228 if ($filterkey != '') {
9229 $sql .= ' AND (';
9230 $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9231 // For natural search
9232 $search_crit = explode(' ', $filterkey);
9233 $i = 0;
9234 if (count($search_crit) > 1) {
9235 $sql .= "(";
9236 }
9237 foreach ($search_crit as $crit) {
9238 if ($i > 0) {
9239 $sql .= " AND ";
9240 }
9241 $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9242 $sql .= "";
9243 $i++;
9244 }
9245 if (count($search_crit) > 1) {
9246 $sql .= ")";
9247 }
9248 $sql .= ')';
9249 }
9250
9251 $sql .= $this->db->plimit($limit, 0);
9252
9253 // Build output string
9254 dol_syslog(get_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
9255 $result = $this->db->query($sql);
9256 if ($result) {
9257 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9258 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
9259
9260 $num = $this->db->num_rows($result);
9261
9262 $events = array();
9263
9264 if (!$forcecombo) {
9265 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9266 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9267 }
9268
9269 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9270
9271 $textifempty = '';
9272 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9273 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9274 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9275 if ($showempty && !is_numeric($showempty)) {
9276 $textifempty = $langs->trans($showempty);
9277 } else {
9278 $textifempty .= $langs->trans("All");
9279 }
9280 } else {
9281 if ($showempty && !is_numeric($showempty)) {
9282 $textifempty = $langs->trans($showempty);
9283 }
9284 }
9285 if ($showempty) {
9286 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9287 }
9288
9289 $i = 0;
9290 while ($num && $i < $num) {
9291 $opt = '';
9292 $optJson = array();
9293 $objp = $this->db->fetch_object($result);
9294
9295 $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
9296 // Add new entry
9297 // "key" value of json key array is used by jQuery automatically as selected value
9298 // "label" value of json key array is used by jQuery automatically as text for combo box
9299 $out .= $opt;
9300 array_push($outarray, $optJson);
9301
9302 $i++;
9303 }
9304
9305 $out .= '</select>';
9306
9307 $this->db->free($result);
9308
9309 if (empty($outputmode)) {
9310 return $out;
9311 }
9312 return $outarray;
9313 } else {
9314 dol_print_error($this->db);
9315 }
9316
9317 return array();
9318 }
9319
9333 protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9334 {
9335 $outkey = '';
9336 $outref = '';
9337 $outtype = '';
9338
9339 $label = $objp->label;
9340
9341 $outkey = $objp->rowid;
9342 $outref = $objp->ref;
9343 $outlabel = $objp->label;
9344 $outtype = $objp->fk_product_type;
9345
9346 $opt = '<option value="' . $objp->rowid . '"';
9347 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9348 $opt .= '>';
9349 $opt .= $objp->ref;
9350 $objRef = $objp->ref;
9351 if (!empty($filterkey) && $filterkey != '') {
9352 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', (string) $objRef, 1);
9353 }
9354
9355 $opt .= "</option>\n";
9356 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9357 }
9358
9359
9380 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())
9381 {
9382 global $langs, $conf;
9383
9384 $out = '';
9385
9386 // check parameters
9387 if (is_null($ajaxoptions)) {
9388 $ajaxoptions = array();
9389 }
9390
9391 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9392 $placeholder = '';
9393
9394 if ($selected && empty($selected_input_value)) {
9395 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9396 $adherenttmpselect = new Adherent($this->db);
9397 $adherenttmpselect->fetch((int) $selected);
9398 $selected_input_value = $adherenttmpselect->ref;
9399 unset($adherenttmpselect);
9400 }
9401
9402 $urloption = '';
9403
9404 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9405
9406 if (empty($hidelabel)) {
9407 $out .= $langs->trans("RefOrLabel") . ' : ';
9408 } elseif ($hidelabel > 1) {
9409 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9410 if ($hidelabel == 2) {
9411 $out .= img_picto($langs->trans("Search"), 'search');
9412 }
9413 }
9414 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9415 if ($hidelabel == 3) {
9416 $out .= img_picto($langs->trans("Search"), 'search');
9417 }
9418 } else {
9419 $filterkey = '';
9420
9421 $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss, $excludeids);
9422 }
9423
9424 if (empty($nooutput)) {
9425 print $out;
9426 } else {
9427 return $out;
9428 }
9429 return '';
9430 }
9431
9449 public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $excludeids = array())
9450 {
9451 global $langs, $conf;
9452
9453 $out = '';
9454 $outarray = array();
9455
9456 $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
9457
9458 $sql = "SELECT ";
9459 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9460 $sql .= " FROM " . $this->db->prefix() . "adherent as p";
9461 $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
9462
9463 // Add criteria on ref/label
9464 if ($filterkey != '') {
9465 $sql .= ' AND (';
9466 $prefix = !getDolGlobalString('MEMBER_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9467 // For natural search
9468 $search_crit = explode(' ', $filterkey);
9469 $i = 0;
9470 if (count($search_crit) > 1) {
9471 $sql .= "(";
9472 }
9473 foreach ($search_crit as $crit) {
9474 if ($i > 0) {
9475 $sql .= " AND ";
9476 }
9477 $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9478 $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
9479 $i++;
9480 }
9481 if (count($search_crit) > 1) {
9482 $sql .= ")";
9483 }
9484 $sql .= ')';
9485 }
9486 if ($status != -1) {
9487 $sql .= ' AND statut = ' . ((int) $status);
9488 }
9489 if (!empty($excludeids)) {
9490 $sql .= " AND p.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
9491 }
9492 $sql .= $this->db->plimit($limit, 0);
9493
9494 // Build output string
9495 dol_syslog(get_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
9496 $result = $this->db->query($sql);
9497 if ($result) {
9498 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9499 require_once DOL_DOCUMENT_ROOT . '/core/lib/member.lib.php';
9500
9501 $num = $this->db->num_rows($result);
9502
9503 $events = array();
9504
9505 if (!$forcecombo) {
9506 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9507 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9508 }
9509
9510 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9511
9512 $textifempty = '';
9513 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9514 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9515 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9516 if ($showempty && !is_numeric($showempty)) {
9517 $textifempty = $langs->trans($showempty);
9518 } else {
9519 $textifempty .= $langs->trans("All");
9520 }
9521 } else {
9522 if ($showempty && !is_numeric($showempty)) {
9523 $textifempty = $langs->trans($showempty);
9524 }
9525 }
9526 if ($showempty) {
9527 $out .= '<option value="-1" selected>' . $textifempty . '</option>';
9528 }
9529
9530 $i = 0;
9531 while ($num && $i < $num) {
9532 $opt = '';
9533 $optJson = array();
9534 $objp = $this->db->fetch_object($result);
9535
9536 $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
9537
9538 // Add new entry
9539 // "key" value of json key array is used by jQuery automatically as selected value
9540 // "label" value of json key array is used by jQuery automatically as text for combo box
9541 $out .= $opt;
9542 array_push($outarray, $optJson);
9543
9544 $i++;
9545 }
9546
9547 $out .= '</select>';
9548
9549 $this->db->free($result);
9550
9551 if (empty($outputmode)) {
9552 return $out;
9553 }
9554 return $outarray;
9555 } else {
9556 dol_print_error($this->db);
9557 }
9558
9559 return array();
9560 }
9561
9573 protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9574 {
9575 $outkey = '';
9576 $outlabel = '';
9577 $outtype = '';
9578
9579 $outkey = $objp->rowid;
9580 $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
9581 $outtype = $objp->fk_adherent_type;
9582
9583 $opt = '<option value="' . $objp->rowid . '"';
9584 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9585 $opt .= '>';
9586 if (!empty($filterkey) && $filterkey != '') {
9587 $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
9588 }
9589 $opt .= $outlabel;
9590 $opt .= "</option>\n";
9591
9592 $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
9593 }
9594
9616 public function selectForForms($objectdesc, $htmlname, $preSelectedValue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '', $objectfield = '')
9617 {
9618 global $conf, $extrafields, $user, $hookmanager, $action;
9619
9620 // Example of common usage for a link to a thirdparty
9621
9622 // We got this in a modulebuilder form of "MyObject" of module "mymodule".
9623 // 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
9624 // $objectdesc = 'Societe'
9625 // $objectfield = Method 1: 'myobject@mymodule:fk_soc' ('fk_soc' is code to retrieve myobject->fields['fk_soc'])
9626 // 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__))" ...)
9627
9628 // We got this when showing an extrafields on resource that is a link to societe
9629 // 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
9630 // $objectdesc = 'Societe'
9631 // $objectfield = Method 1: 'resource:options_link_to_societe'
9632 // Method 2 recommended (it can be the array): array("type"=>'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...)
9633
9634 // With old usage:
9635 // $objectdesc = 'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))'
9636 // $objectfield = ''
9637
9638 //var_dump($objectdesc.' '.$objectfield);
9639 //debug_print_backtrace();
9640
9641 $objectdescorig = $objectdesc;
9642 $objecttmp = null;
9643 $InfoFieldList = array();
9644 $classname = '';
9645 $filter = ''; // Ensure filter has value (for static analysis)
9646 $sortfield = ''; // Ensure filter has value (for static analysis)
9647
9648 if (is_array($objectfield)) { // objectfield is an array
9649 $objectdesc = $objectfield['type'];
9650 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9651 } 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.
9652 // Example: $objectfield = 'product:options_package' or 'myobject@mymodule:options_myfield'
9653 $tmparray = explode(':', $objectfield);
9654
9655 // Get instance of object from $element
9656 $objectforfieldstmp = fetchObjectByElement(0, strtolower($tmparray[0]));
9657
9658 if (is_object($objectforfieldstmp)) {
9659 $objectdesc = '';
9660
9661 $reg = array();
9662 if (preg_match('/^options_(.*)$/', $tmparray[1], $reg)) {
9663 // For a property in extrafields
9664 $key = $reg[1];
9665 // fetch optionals attributes and labels
9666 $extrafields->fetch_name_optionals_label($objectforfieldstmp->table_element);
9667
9668 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key]) && $extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key] == 'link') {
9669 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options'])) {
9670 $tmpextrafields = array_keys($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options']);
9671 $objectdesc = $tmpextrafields[0];
9672 }
9673 }
9674 } else {
9675 // For a property in ->fields
9676 if (array_key_exists($tmparray[1], $objectforfieldstmp->fields)) {
9677 $objectdesc = $objectforfieldstmp->fields[$tmparray[1]]['type'];
9678 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9679 }
9680 }
9681 }
9682 }
9683
9684 if ($objectdesc) {
9685 // Example of value for $objectdesc:
9686 // Bom:bom/class/bom.class.php:0:t.status=1
9687 // Bom:bom/class/bom.class.php:0:t.status=1:ref
9688 // Bom:bom/class/bom.class.php:0:(t.status:=:1) OR (t.field2:=:2):ref
9689 $InfoFieldList = explode(":", $objectdesc, 4);
9690 $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
9691 $reg = array();
9692 if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
9693 $InfoFieldList[4] = $reg[1]; // take the sort field
9694 }
9695 $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp); // take the filter field
9696
9697 $classname = $InfoFieldList[0];
9698 $classpath = empty($InfoFieldList[1]) ? '' : $InfoFieldList[1];
9699 //$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
9700 $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
9701 $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
9702
9703 // Load object according to $id and $element
9704 $objecttmp = fetchObjectByElement(0, strtolower($InfoFieldList[0]));
9705
9706 // Fallback to another solution to get $objecttmp
9707 if (empty($objecttmp) && !empty($classpath)) {
9708 dol_include_once($classpath);
9709
9710 if ($classname && class_exists($classname)) {
9711 $objecttmp = new $classname($this->db);
9712 }
9713 }
9714 }
9715
9716 // Make some replacement in $filter. May not be used if we used the ajax mode with $objectfield. In such a case
9717 // we propagate the $objectfield and not the filter and replacement is done by the ajax/selectobject.php component.
9718 $sharedentities = (is_object($objecttmp) && property_exists($objecttmp, 'element')) ? getEntity($objecttmp->element) : strtolower($classname);
9719 $filter = str_replace(
9720 array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
9721 array($conf->entity, $sharedentities, $user->id),
9722 $filter
9723 );
9724
9725 if (!is_object($objecttmp)) {
9726 dol_syslog('selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc, LOG_WARNING);
9727 return 'selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc;
9728 }
9729 '@phan-var-force CommonObject $objecttmp';
9731 //var_dump($filter);
9732 $prefixforautocompletemode = $objecttmp->element;
9733 if ($prefixforautocompletemode == 'societe') {
9734 $prefixforautocompletemode = 'company';
9735 }
9736 if ($prefixforautocompletemode == 'product') {
9737 $prefixforautocompletemode = 'produit';
9738 }
9739
9740 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9741
9742 dol_syslog(get_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
9743
9744 // Generate the combo HTML component
9745 $out = '';
9746 if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
9747 // No immediate load of all database
9748 $placeholder = '';
9749
9750 if ($preSelectedValue && empty($selected_input_value)) {
9751 $objecttmp->fetch($preSelectedValue);
9752 $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
9753
9754 $oldValueForShowOnCombobox = 0;
9755 foreach ($objecttmp->fields as $fieldK => $fielV) {
9756 if (!array_key_exists('showoncombobox', $fielV) || !$fielV['showoncombobox'] || empty($objecttmp->$fieldK)) {
9757 continue;
9758 }
9759
9760 if (!$oldValueForShowOnCombobox) {
9761 $selected_input_value = '';
9762 }
9763
9764 $selected_input_value .= $oldValueForShowOnCombobox ? ' - ' : '';
9765 $selected_input_value .= $objecttmp->$fieldK;
9766 $oldValueForShowOnCombobox = empty($fielV['showoncombobox']) ? 0 : $fielV['showoncombobox'];
9767 }
9768 }
9769
9770 // Set url and param to call to get json of the search results
9771 $urlforajaxcall = DOL_URL_ROOT . '/core/ajax/selectobject.php';
9772 $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdescorig) . (is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9773 //$urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1'.(is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9774
9775 // Hook 'selectForFormsListUrl' - Added to allow modules to modify the AJAX URL
9776 $parameters = array(
9777 'urloption' => $urloption,
9778 'object' => $objecttmp,
9779 'htmlname' => $htmlname,
9780 'filter' => $filter,
9781 'searchkey' => $searchkey,
9782 );
9783 $reshook = $hookmanager->executeHooks('selectForFormsListUrl', $parameters, $objecttmp, $action);
9784 if (!empty($reshook)) {
9785 $urloption = $hookmanager->resPrint;
9786 $hookmanager->resPrint = '';
9787 }
9788
9789 // Activate the auto complete using ajax call.
9790 $out .= ajax_autocompleter((string) $preSelectedValue, $htmlname, $urlforajaxcall, $urloption, getDolGlobalInt($confkeyforautocompletemode), 0);
9791 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
9792 $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) . '"' : '') . ' />';
9793 } else {
9794 // Immediate load of table record.
9795 $out .= $this->selectForFormsList($objecttmp, $htmlname, $preSelectedValue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
9796 }
9797
9798 return $out;
9799 }
9800
9801
9823 public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '', $sortorder = 'ASC')
9824 {
9825 global $langs, $user, $hookmanager;
9826
9827 //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
9828
9829 $prefixforautocompletemode = $objecttmp->element;
9830 if ($prefixforautocompletemode == 'societe') {
9831 $prefixforautocompletemode = 'company';
9832 }
9833 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9834
9835 $fieldstoshow = '';
9836 if (!empty($objecttmp->fields)) { // For object that declare it, it is better to use declared fields (like societe, contact, ...)
9837 $tmpfieldstoshow = '';
9838 foreach ($objecttmp->fields as $key => $val) {
9839 if (! (int) dol_eval((string) $val['enabled'], 1, 1, '1')) {
9840 continue;
9841 }
9842 if (!empty($val['showoncombobox'])) {
9843 $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
9844 }
9845 }
9846 if ($tmpfieldstoshow) {
9847 $fieldstoshow = $tmpfieldstoshow;
9848 }
9849 } elseif ($objecttmp->element === 'category') {
9850 $fieldstoshow = 't.label';
9851 } else {
9852 // For backward compatibility
9853 $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'visible' => 4, 'showoncombobox' => 1);
9854 }
9855
9856 if (empty($fieldstoshow)) {
9857 if (!empty($objecttmp->parent_element)) {
9858 $fieldstoshow = 'o.ref';
9859 if (empty($sortfield)) {
9860 $sortfield = 'o.ref';
9861 }
9862 if (in_array($objecttmp->element, ['commandedet', 'propaldet', 'facturedet', 'expeditiondet'])) {
9863 $fieldstoshow .= ',p.ref AS p_ref,p.label,t.description';
9864 $sortfield .= ', p.ref';
9865 }
9866 } elseif (isset($objecttmp->fields['ref'])) {
9867 $fieldstoshow = 't.ref';
9868 } else {
9869 $langs->load("errors");
9870 $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
9871 return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
9872 }
9873 }
9874
9875 $out = '';
9876 $outarray = array();
9877 $tmparray = array();
9878
9879 $num = 0;
9880
9881 $sanitizedfieldstoshow = $fieldstoshow;
9882
9883 // Search data
9884 $sql = "SELECT t.rowid, " . $sanitizedfieldstoshow . " FROM " . $this->db->prefix() . $this->db->sanitize($objecttmp->table_element) . " as t";
9885 if (!empty($objecttmp->isextrafieldmanaged)) {
9886 $extrafieldTable = $objecttmp->table_element;
9887 if ($extrafieldTable == 'categorie') {
9888 $extrafieldTable = 'categories'; // For compatibility
9889 }
9890 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($extrafieldTable) . "_extrafields as e ON t.rowid = e.fk_object";
9891 }
9892 if (!empty($objecttmp->parent_element)) { // If parent_element is defined
9893 '@phan-var-force CommonObjectLine $objecttmp';
9894 $parent_properties = getElementProperties($objecttmp->parent_element);
9895 // @phan-suppress-next-line SqlInjection
9896 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($parent_properties['table_element']) . " as o ON o.rowid = t.".$objecttmp->fk_parent_attribute;
9897 }
9898 if (!empty($objecttmp->parent_element) && in_array($objecttmp->parent_element, ['commande', 'propal', 'facture', 'expedition'])) {
9899 $sql .= " LEFT JOIN " . $this->db->prefix() . "product as p ON p.rowid = t.fk_product";
9900 }
9901 if (!empty($objecttmp->ismultientitymanaged)) {
9902 if ($objecttmp->ismultientitymanaged == 1) { // @phan-suppress-current-line PhanPluginEmptyStatementIf
9903 // No need to join/link another table
9904 }
9905 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9906 $tmparray = explode('@', $objecttmp->ismultientitymanaged);
9907 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($tmparray[1]) . " as parenttable ON parenttable.rowid = t." . $this->db->sanitize($tmparray[0]);
9908 }
9909 }
9910
9911 // Add where from hooks
9912 $parameters = array(
9913 'object' => $objecttmp,
9914 'htmlname' => $htmlname,
9915 'filter' => $filter,
9916 'searchkey' => $searchkey
9917 );
9918
9919 $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
9920 if (!empty($hookmanager->resPrint)) {
9921 $sql .= $hookmanager->resPrint;
9922 } else {
9923 $sql .= " WHERE 1=1";
9924
9925 // If table need a multientity restriction
9926 if (!empty($objecttmp->ismultientitymanaged)) {
9927 if ($objecttmp->ismultientitymanaged == 1) {
9928 $sql .= " AND t.entity IN (" . getEntity($objecttmp->element) . ")";
9929 }
9930 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9931 $sql .= " AND parenttable.entity = t." . $this->db->sanitize($tmparray[0]);
9932 }
9933 // If the parent table is llx_societe and user is not an external user (a more robust test done later for external users),
9934 // then we must also check that user has permissions
9935 if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
9936 if (!$user->hasRight('societe', 'client', 'voir') && empty($user->socid)) {
9937 $sql .= " AND EXISTS (SELECT sc.rowid FROM ".$this->db->prefix() . "societe_commerciaux as sc";
9938 $sql .= " WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $user->id).")";
9939 }
9940 }
9941 }
9942
9943 // If user is external user, we must also make a test on llx_societe_commerciaux
9944 if (!empty($user->socid)) {
9945 if ($objecttmp->element == 'societe') {
9946 $sql .= " AND t.rowid = " . ((int) $user->socid);
9947 } elseif (!empty($objecttmp->fields['fk_soc']) || !empty($objecttmp->fields['t.fk_soc']) || property_exists($objecttmp, 'fk_soc') || property_exists($objecttmp, 'socid')) {
9948 $sql .= " AND t.fk_soc = " . ((int) $user->socid);
9949 }
9950 }
9951
9952 $splittedfieldstoshow = explode(',', $fieldstoshow);
9953 foreach ($splittedfieldstoshow as &$field2) {
9954 if (is_numeric($pos = strpos($field2, ' '))) {
9955 $field2 = substr($field2, 0, $pos);
9956 }
9957 }
9958 if ($searchkey != '') {
9959 $sql .= natural_search($splittedfieldstoshow, $searchkey);
9960 }
9961
9962 if ($filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:>:'20160101')"
9963 $errormessage = '';
9964 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
9965 if ($errormessage) {
9966 return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
9967 }
9968 }
9969 }
9970 $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, $sortorder);
9971 //$sql.=$this->db->plimit($limit, 0);
9972 //print $sql;
9973
9974 // Build output string
9975 $resql = $this->db->query($sql);
9976 if ($resql) {
9977 // Construct $out and $outarray
9978 $out .= '<select id="' . $htmlname . '" class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
9979
9980 // 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
9981 $textifempty = '&nbsp;';
9982
9983 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9984 if (getDolGlobalInt($confkeyforautocompletemode)) {
9985 if ($showempty && !is_numeric($showempty)) {
9986 $textifempty = $langs->trans($showempty);
9987 } else {
9988 $textifempty .= $langs->trans("All");
9989 }
9990 }
9991 if ($showempty) {
9992 $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
9993 }
9994
9995 $num = $this->db->num_rows($resql);
9996 $i = 0;
9997 if ($num) {
9998 while ($i < $num) {
9999 $obj = $this->db->fetch_object($resql);
10000 $label = '';
10001 $labelhtml = '';
10002 $tmparray = explode(',', $fieldstoshow);
10003 $oldvalueforshowoncombobox = 0;
10004 foreach ($tmparray as $key => $val) {
10005 $val = preg_replace('/(t|p|o)\./', '', $val);
10006 $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10007 $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10008 $label .= $obj->$val;
10009 $labelhtml .= $obj->$val;
10010
10011 $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
10012 }
10013 if (empty($outputmode)) {
10014 if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
10015 $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>';
10016 } else {
10017 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
10018 }
10019 } else {
10020 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
10021 }
10022
10023 $i++;
10024 if (($i % 10) == 0) {
10025 $out .= "\n";
10026 }
10027 }
10028 }
10029
10030 $out .= '</select>' . "\n";
10031
10032 if (!$forcecombo) {
10033 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10034 $out .= ajax_combobox($htmlname, array(), getDolGlobalInt($confkeyforautocompletemode, 0));
10035 }
10036 } else {
10037 dol_print_error($this->db);
10038 }
10039
10040 $this->result = array('nbofelement' => $num);
10041
10042 if ($outputmode) {
10043 return $outarray;
10044 }
10045 return $out;
10046 }
10047
10058 public static function radio($htmlName, $radioItems, $selected = '', $moreGlobalParams = [])
10059 {
10060 // Default parameters for each radio input
10061 $defaultParams = [
10062 'disabled' => false,
10063 'attr' => [
10064 'type' => 'radio',
10065 'name' => $htmlName,
10066 ],
10067 'attrLabel' => [],
10068 'labelIsHtml' => false
10069 ];
10070
10071 // Merge global parameters with defaults
10072 $params = array_merge_recursive_distinct($defaultParams, $moreGlobalParams);
10073
10074 $out = '';
10075 if (!empty($radioItems)) {
10076 foreach ($radioItems as $key => $item) {
10077 // Normalize item to array structure if it's a simple string
10078 if (!is_array($item)) {
10079 $item = [
10080 'attr' => [
10081 'value' => $key,
10082 ],
10083 'label' => $item
10084 ];
10085 }
10086
10087 // Default properties for individual item
10088 $defaultItem = [
10089 'attr' => [
10090 'value' => !isset($item['attr']['value']) ? $key : '',
10091 ],
10092 'label' => '',
10093 ];
10094
10095 // Merge defaults with global params and item-specific properties
10096 $defaultItem = array_merge_recursive_distinct($params, $defaultItem);
10097 $item = array_merge_recursive_distinct($defaultItem, $item);
10098
10099 // Determine if this radio should be checked
10100 if ((is_array($selected) && in_array($item['attr']['value'], $selected, true)) || $selected === $item['attr']['value']) {
10101 $item['attr']['checked'] = true;
10102 }
10103
10104 // Build HTML attributes for input and label
10105 $inputAttributes = implode(' ', commonHtmlAttributeBuilder($item['attr']));
10106 $labelAttributes = implode(' ', commonHtmlAttributeBuilder($item['attrLabel']));
10107
10108 // prevent accidental Xss todo : escape $item['label'] but html friendly compatible
10109 $text = $item['labelIsHtml'] ? $item['label'] : htmlspecialchars($item['label'], ENT_QUOTES | ENT_SUBSTITUTE);
10110
10111 // Generate HTML
10112 $out .= '<label ' . $labelAttributes . '><input ' . $inputAttributes . ' /> ' . $text . '</label> ';
10113 }
10114 }
10115
10116 return $out;
10117 }
10118
10119
10143 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)
10144 {
10145 global $conf, $langs;
10146
10147 // Do we want a multiselect ?
10148 //$jsbeautify = 0;
10149 //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
10150 $jsbeautify = 1;
10151
10152 if ($value_as_key) {
10153 $array = array_combine($array, $array);
10154 }
10155
10156 '@phan-var-force array{label:string,data-html:string,disable?:int<0,1>,css?:string} $array'; // Array combine breaks information
10157
10158 $out = '';
10159
10160 if ($addjscombo < 0) {
10161 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10162 $addjscombo = 1;
10163 } else {
10164 $addjscombo = 0;
10165 }
10166 }
10167 $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
10168 $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . ' selectformat"';
10169 $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
10170 $out .= '>'."\n";
10171
10172 if ($show_empty) {
10173 $textforempty = ' ';
10174 if (!empty($conf->use_javascript_ajax)) {
10175 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
10176 }
10177 if (!is_numeric($show_empty)) {
10178 $textforempty = $show_empty;
10179 }
10180 $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . (((int) $show_empty) < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
10181 }
10182 if (is_array($array)) {
10183 // Translate
10184 if ($translate) {
10185 foreach ($array as $key => $value) {
10186 if (!is_array($value)) {
10187 $array[$key] = $langs->trans($value);
10188 } else {
10189 $array[$key]['label'] = $langs->trans($value['label']);
10190 }
10191 }
10192 }
10193 // Sort
10194 if ($sort == 'ASC') {
10195 asort($array);
10196 } elseif ($sort == 'DESC') {
10197 arsort($array);
10198 }
10199
10200 foreach ($array as $key => $tmpvalue) {
10201 if (is_array($tmpvalue)) {
10202 $value = $tmpvalue['label'];
10203 //$valuehtml = empty($tmpvalue['data-html']) ? $value : $tmpvalue['data-html'];
10204 $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
10205 $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
10206 } else {
10207 $value = $tmpvalue;
10208 //$valuehtml = $tmpvalue;
10209 $disabled = '';
10210 $style = '';
10211 }
10212 if (!empty($disablebademail)) {
10213 if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
10214 || ($disablebademail == 2 && preg_match('/---/', $value))) {
10215 $disabled = ' disabled';
10216 $style = ' class="warning"';
10217 }
10218 }
10219 if ($key_in_label) {
10220 if (empty($nohtmlescape)) {
10221 $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
10222 } else {
10223 $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
10224 }
10225 } else {
10226 if (empty($nohtmlescape)) {
10227 $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
10228 } else {
10229 $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
10230 }
10231 if ($value == '' || $value == '-') {
10232 $selectOptionValue = '&nbsp;';
10233 }
10234 }
10235 $out .= '<option value="' . $key . '"';
10236 $out .= $style . $disabled;
10237 $out .= is_array($tmpvalue) && !empty($tmpvalue['parent']) ? ' parent="' . dolPrintHTMLForAttribute($tmpvalue['parent']) . '"' : '';
10238 if (is_array($id)) {
10239 if (in_array($key, $id) && !$disabled) {
10240 $out .= ' selected'; // To preselect a value
10241 }
10242 } else {
10243 $id = (string) $id; // if $id = 0, then $id = '0'
10244 if ($id != '' && (($id == (string) $key) || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
10245 $out .= ' selected'; // To preselect a value
10246 }
10247 }
10248
10249 if (is_array($tmpvalue)) {
10250 foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
10251 if ($keyforvalue == 'labelhtml') {
10252 $keyforvalue = 'data-html';
10253 }
10254 if (preg_match('/^data-/', $keyforvalue)) { // The best solution if you want to use HTML values into the list is to use data-html.
10255 $out .= ' '.dol_escape_htmltag($keyforvalue).'="'.dol_escape_htmltag($valueforvalue).'"';
10256 }
10257 }
10258 } elseif (!empty($nohtmlescape)) { // deprecated. Use instead the previous cas, an array with 'data-html', 'data-xxx' ... to use HTML content in the select
10259 $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
10260 }
10261
10262 $out .= '>';
10263 $out .= $selectOptionValue;
10264 $out .= "</option>\n";
10265 }
10266 }
10267 $out .= "</select>";
10268
10269 // Add code for jquery to use multiselect
10270 if ($addjscombo && $jsbeautify) {
10271 // Enhance with select2
10272 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10273 $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', (((int) $show_empty) < 0 ? (string) $show_empty : '-1'), $morecss);
10274 }
10275
10276 return $out;
10277 }
10278
10297 public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
10298 {
10299 global $conf;
10300 global $delayedhtmlcontent; // Will be used later outside of this function
10301
10302 // TODO Use an internal dolibarr component instead of select2
10303 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10304 return '';
10305 }
10306
10307 $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
10308
10309 $outdelayed = '';
10310 if (!empty($conf->use_javascript_ajax)) {
10311 $tmpplugin = 'select2';
10312 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10313 <script nonce="' . getNonce() . '">
10314 $(document).ready(function () {
10315
10316 ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
10317
10318 $(".' . $htmlname . '").select2({
10319 ajax: {
10320 dir: "ltr",
10321 url: "' . $url . '",
10322 dataType: \'json\',
10323 delay: 250,
10324 data: function (params) {
10325 return {
10326 q: params.term, // search term
10327 page: params.page
10328 }
10329 },
10330 processResults: function (data) {
10331 // parse the results into the format expected by Select2.
10332 // since we are using custom formatting functions we do not need to alter the remote JSON data
10333 //console.log(data);
10334 saveRemoteData = data;
10335 /* format json result for select2 */
10336 result = []
10337 $.each( data, function( key, value ) {
10338 result.push({id: key, text: value.text});
10339 });
10340 //return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
10341 //console.log(result);
10342 return {results: result, more: false}
10343 },
10344 cache: true
10345 },
10346 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10347 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10348 placeholder: \'' . dol_escape_js($placeholder) . '\',
10349 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10350 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10351 formatResult: function (result, container, query, escapeMarkup) {
10352 return escapeMarkup(result.text);
10353 },
10354 });
10355
10356 ' . ($callurlonselect ? '
10357 /* Code to execute a GET when we select a value */
10358 $(".' . $htmlname . '").change(function() {
10359 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10360 console.log("We select in selectArrayAjax the entry "+selected)
10361 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10362 $.each( saveRemoteData, function( key, value ) {
10363 if (key == selected)
10364 {
10365 console.log("selectArrayAjax - Do a redirect to "+value.url)
10366 location.assign(value.url);
10367 }
10368 });
10369 });' : '') . '
10370
10371 });
10372 </script>';
10373 }
10374
10375 if ($acceptdelayedhtml) {
10376 $delayedhtmlcontent .= $outdelayed;
10377 } else {
10378 $out .= $outdelayed;
10379 }
10380 return $out;
10381 }
10382
10402 public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
10403 {
10404 global $conf;
10405 global $delayedhtmlcontent; // Will be used later outside of this function
10406
10407 // TODO Use an internal dolibarr component instead of select2
10408 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10409 return '';
10410 }
10411
10412 $out = '<select type="text"'.($textfortitle ? ' title="'.dol_escape_htmltag($textfortitle).'"' : '').' id="'.$htmlname.'" class="'.$htmlname.($morecss ? ' ' . $morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.'"><option></option></select>';
10413
10414 $formattedarrayresult = array();
10415
10416 foreach ($array as $key => $value) {
10417 $o = new stdClass();
10418 $o->id = $key;
10419 $o->text = $value['text'];
10420 $o->url = $value['url'];
10421 $formattedarrayresult[] = $o;
10422 }
10423
10424 $outdelayed = '';
10425 if (!empty($conf->use_javascript_ajax)) {
10426 $tmpplugin = 'select2';
10427 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10428 <script nonce="' . getNonce() . '">
10429 $(document).ready(function () {
10430 var data = ' . json_encode($formattedarrayresult) . ';
10431
10432 ' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
10433
10434 $(\'.' . dol_escape_js($htmlname) . '\').select2({
10435 data: data,
10436 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10437 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10438 placeholder: \'' . dol_escape_js($placeholder) . '\',
10439 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10440 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10441 formatResult: function (result, container, query, escapeMarkup) {
10442 return escapeMarkup(result.text);
10443 },
10444 matcher: function (params, data) {
10445
10446 if(! data.id) return null;';
10447
10448 if ($callurlonselect) {
10449 // We forge the url with 'sall='
10450 $outdelayed .= '
10451
10452 var urlBase = data.url;
10453 var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
10454 /* console.log("params.term="+params.term); */
10455 /* console.log("params.term encoded="+encodeURIComponent(params.term)); */
10456 saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
10457 }
10458
10459 if (!$disableFiltering) {
10460 $outdelayed .= '
10461
10462 if(data.text.match(new RegExp(params.term))) {
10463 return data;
10464 }
10465
10466 return null;';
10467 } else {
10468 $outdelayed .= '
10469
10470 return data;';
10471 }
10472
10473 $outdelayed .= '
10474 }
10475 });
10476
10477 ' . ($callurlonselect ? '
10478 /* Code to execute a GET when we select a value */
10479 $(\'.' . dol_escape_js($htmlname) . '\').change(function() {
10480 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10481 console.log("We select "+selected)
10482
10483 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10484 $.each( saveRemoteData, function( key, value ) {
10485 if (key == selected)
10486 {
10487 console.log("selectArrayFilter - Do a redirect to "+value.url)
10488 location.assign(value.url);
10489 }
10490 });
10491 });' : '') . '
10492
10493 });
10494 </script>';
10495 }
10496
10497 if ($acceptdelayedhtml) {
10498 $delayedhtmlcontent .= $outdelayed;
10499 } else {
10500 $out .= $outdelayed;
10501 }
10502 return $out;
10503 }
10504
10523 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)
10524 {
10525 global $conf, $langs;
10526 $out = '';
10527
10528 if ($addjscombo < 0) {
10529 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10530 $addjscombo = 1;
10531 } else {
10532 $addjscombo = 0;
10533 }
10534 }
10535
10536 $useenhancedmultiselect = 0;
10537 if (!empty($conf->use_javascript_ajax) && !defined('MAIN_DO_NOT_USE_JQUERY_MULTISELECT') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
10538 if ($addjscombo) {
10539 $useenhancedmultiselect = 1; // Use the js multiselect in one line. Possible only if $addjscombo not 0.
10540 }
10541 }
10542
10543 $out .= '<span class="multiselectarray'.$htmlname.'">';
10544
10545 // We need a hidden field because when using the multiselect, if we unselect all, there is no
10546 // variable submitted at all, so no way to make a difference between variable not submitted and variable
10547 // submitted to nothing.
10548 $out .= '<input type="hidden" name="'.$htmlname.'_multiselect" value="1">';
10549 // Output select component
10550 $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";
10551 if (is_array($array) && !empty($array)) {
10552 if ($value_as_key) {
10553 $array = array_combine($array, $array);
10554 }
10555
10556 if (!empty($array)) {
10557 foreach ($array as $key => $value) {
10558 $tmpkey = $key;
10559 $tmplabel = $value;
10560 $tmplabelhtml = '';
10561 $tmpcolor = '';
10562 $tmppicto = '';
10563 $tmpdisabled = '';
10564 if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
10565 $tmpkey = $value['id'];
10566 $tmplabel = empty($value['label']) ? '' : $value['label'];
10567 $tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
10568 $tmpcolor = empty($value['color']) ? '' : $value['color'];
10569 $tmppicto = empty($value['picto']) ? '' : $value['picto'];
10570 $tmpdisabled = empty($value['disabled']) ? '' : $value['disabled'];
10571 }
10572 $newval = ($translate ? $langs->trans($tmplabel) : $tmplabel);
10573 $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
10574
10575 $tmplabelhtml = ($translate ? $langs->trans($tmplabelhtml) : $tmplabelhtml);
10576 $tmplabelhtml = ($key_in_label ? $tmpkey . ' - ' . $tmplabelhtml : $tmplabelhtml);
10577
10578 $out .= '<option value="' . $tmpkey . '"';
10579 if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
10580 $out .= ' selected';
10581 }
10582 $out .= is_array($value) && array_key_exists('parent', $value) && !empty($value['parent']) ? ' parent="' . dolPrintHTMLForAttribute($value['parent']) . '"' : '';
10583 if ($tmpdisabled) {
10584 $out .= ' disabled="disabled"';
10585 }
10586 if (!empty($tmplabelhtml)) {
10587 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10588 } else {
10589 $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
10590 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10591 }
10592 $out .= '>';
10593 $out .= dol_htmlentitiesbr($newval);
10594 $out .= '</option>' . "\n";
10595 }
10596 }
10597 }
10598 $out .= '</select>' . "\n";
10599
10600 $out .= '</span>';
10601
10602 // Add code for jquery to use multiselect
10603 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT')) {
10604 $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
10605 $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
10606 if ($addjscombo == 1) {
10607 $tmpplugin = getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT', (defined('REQUIRE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : 'select2'));
10608
10609 // If property data-html set, we decode html entities and use this.
10610 // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
10611 // TODO Move this into common js ?
10612 $out .= 'function formatResult(record, container) {' . "\n";
10613 $out .= ' if ($(record.element).attr("data-html") != undefined && typeof htmlEntityDecodeJs === "function") {';
10614 $out .= ' return htmlEntityDecodeJs($(record.element).attr("data-html"));';
10615 $out .= ' }'."\n";
10616 $out .= ' return record.text;';
10617 $out .= '}' . "\n";
10618
10619 $out .= 'function formatSelection(record) {' . "\n";
10620 $out .= ' return record.text;';
10621 $out .= '}' . "\n";
10622
10623 // Load the select2 enhancer
10624 //$out .= 'console.log(\'addjscombo=1 for htmlname=' . dol_escape_js($htmlname) . '\');';
10625 $out .= '$(document).ready(function () {
10626 $(\'#' . dol_escape_js($htmlname) . '\').' . $tmpplugin . '({';
10627 if ($placeholder) {
10628 $out .= '
10629 placeholder: {
10630 id: \'-1\',
10631 text: \''.dol_escape_js($placeholder).'\'
10632 },';
10633 }
10634 $out .= ' dir: \'ltr\',
10635 containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
10636 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. */
10637 // Specify format function for dropdown item
10638 formatResult: formatResult,
10639 templateResult: formatResult, /* For 4.0 */
10640 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10641 // Specify format function for selected item
10642 formatSelection: formatSelection,
10643 templateSelection: formatSelection, /* For 4.0 */
10644 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage
10645 });
10646
10647 /* Add also morecss to the css .select2 that is after the #htmlname, for component that are shown dynamically after load, because select2 set
10648 the size only if component is not hidden by default on load */
10649 $(\'#' . dol_escape_js($htmlname) . ' + .select2\').addClass(\'' . dol_escape_js($morecss) . '\');
10650 });' . "\n";
10651 } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
10652 // Add other js lib
10653 // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
10654 // ...
10655 $out .= 'console.log(\'addjscombo=2 for htmlname=' . dol_escape_js($htmlname) . '\');';
10656 $out .= '$(document).ready(function () {
10657 $(\'#' . dol_escape_js($htmlname) . '\').multiSelect({
10658 containerHTML: \'<div class="multi-select-container">\',
10659 menuHTML: \'<div class="multi-select-menu">\',
10660 buttonHTML: \'<span class="multi-select-button ' . dol_escape_js($morecss) . '">\',
10661 menuItemHTML: \'<label class="multi-select-menuitem">\',
10662 activeClass: \'multi-select-container--open\',
10663 noneText: \'' . dol_escape_js($placeholder) . '\'
10664 });
10665 })';
10666 }
10667 $out .= '</script>';
10668 }
10669
10670 return $out;
10671 }
10672
10673
10687 public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '', $draganddrop = 0)
10688 {
10689 global $conf, $langs, $user;
10690
10691 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10692 return '';
10693 }
10694 if (empty($array)) {
10695 return '';
10696 }
10697
10698 $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
10699
10700 if (!empty($user->conf->$tmpvar)) { // A list of fields was already customized for user
10701 $tmparray = explode(',', $user->conf->$tmpvar);
10702 foreach ($array as $key => $val) {
10703 //var_dump($key);
10704 //var_dump($tmparray);
10705 if (in_array($key, $tmparray)) {
10706 $array[$key]['checked'] = 1;
10707 } else {
10708 $array[$key]['checked'] = 0;
10709 }
10710 }
10711 } else { // There is no list of fields already customized for user
10712 foreach ($array as $key => $val) {
10713 if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
10714 $array[$key]['checked'] = 0;
10715 }
10716 }
10717 }
10718
10719 $listoffieldsforselection = '';
10720 $listcheckedstring = '';
10721
10722 foreach ($array as $key => $val) {
10723 // var_dump($val);
10724 // var_dump(array_key_exists('enabled', $val));
10725 // var_dump(!$val['enabled']);
10726 if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
10727 unset($array[$key]); // We don't want this field
10728 continue;
10729 }
10730 if (!empty($val['type']) && $val['type'] == 'separate') {
10731 // Field remains in array but we don't add it into $listoffieldsforselection
10732 //$listoffieldsforselection .= '<li>-----</li>';
10733 continue;
10734 }
10735 if (!empty($val['label']) && $val['label']) {
10736 if (!empty($val['langfile']) && is_object($langs)) {
10737 $langs->load($val['langfile']);
10738 }
10739
10740 // Note: $val['checked'] <> 0 means we must show the field into the combo list @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10741 $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']).'" />';
10742 $listoffieldsforselection .= '<label for="checkbox' . $key . '" class="paddingleft">';
10743 $listoffieldsforselection .= dolPrintHTML(dol_string_nohtmltag($langs->trans($val['label'])));
10744 $listoffieldsforselection .= '</label>';
10745 if (!empty($draganddrop)) {
10746 $listoffieldsforselection .= img_picto($langs->trans("MoveField", !empty($key) ? $key : 'none'), 'grip_title', 'class="opacitymedium boxhandle hideonsmartphone cursormove marginleftonly"');
10747 }
10748 $listoffieldsforselection .= '</li>';
10749 $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
10750 }
10751 }
10752
10753 $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
10754
10755 <dl class="dropdown">
10756 <dt>
10757 <a href="#' . $htmlname . '" class="multiselectpicto">
10758 ' . img_picto('', 'list') . '
10759 </a>
10760 <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
10761 </dt>
10762 <dd class="dropdowndd">
10763 <div class="multiselectcheckbox'.$htmlname.'">
10764 <ul class="'.$htmlname.(((string) $pos == '1' || (string) $pos == 'left') ? 'left' : '').(!empty($draganddrop) ? ' sortable' : '').'">
10765 <li class="liinputsearch">
10766 <input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="'.$langs->trans('Search').'">
10767 </li>
10768 '.$listoffieldsforselection.'
10769 </ul>
10770 </div>
10771 </dd>
10772 </dl>
10773
10774 <script>
10775 function updateFieldOrder() {
10776 var positionfields = $(".sortable").sortable("toArray");
10777 $.ajax({
10778 url: \''.DOL_URL_ROOT.'/core/ajax/changepositionfields.php?positionfields=\'+positionfields+\'&token='.newToken().'&action=listafterchangingpositionfields&contextpage='.$varpage.'&userid='.$user->id.'\',
10779 async: false,
10780 success: function () {
10781 // reload page
10782 window.location.href = "'.$_SERVER["PHP_SELF"].'";
10783 }
10784 });
10785 }
10786 $( ".sortable" ).sortable({
10787 handle: \'.boxhandle\',
10788 revert: \'invalid\',
10789 items: \'.fieldsortable\',
10790 stop: function(event, ui) {
10791 console.log("We moved box so we call updateBoxOrder with ajax actions");
10792 updateFieldOrder(); /* 1 to avoid message after a move */
10793 }
10794 });
10795 </script>
10796
10797 <script nonce="' . getNonce() . '" type="text/javascript">
10798 jQuery(document).ready(function () {
10799 $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on("click", function () {
10800 console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
10801
10802 $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\'); // Update field so we know we changed something on selected fields after POST
10803
10804 var title = $(this).val() + ",";
10805 if ($(this).is(\':checked\')) {
10806 $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
10807 }
10808 else {
10809 $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
10810 }
10811 // Now, we submit page
10812 //$(this).parents(\'form:first\').submit();
10813 });
10814
10815 $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
10816 console.log("keyup on inputsearch_dropdownselectedfields");
10817 var value = $(this).val().toLowerCase();
10818 $(\'.multiselectcheckbox'.$htmlname.' li > label\').filter(function() {
10819 $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
10820 });
10821 });
10822 ';
10823 if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') {
10824 $out .= '
10825 $(".dropdown dt a").on("click", function () {
10826 console.log("Click on dropdown, we set focus to search field");
10827 setTimeout(() => { $(\'.inputsearch_dropdownselectedfields\').focus(); }, 200);
10828 });';
10829 }
10830 $out .= '
10831 });
10832 </script>
10833
10834 ';
10835 return $out;
10836 }
10837
10847 public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
10848 {
10849 global $conf;
10850
10851 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
10852
10853 $cat = new Categorie($this->db);
10854 $categories = $cat->containing($id, $type);
10855
10856 if ($rendermode == 1 || $rendermode == 2) {
10857 $toprint = array();
10858 foreach ($categories as $c) {
10859 $ways = $c->print_all_ways('auto', ($nolink ? 'none' : ''), 0, 1, ($rendermode == 2 ? 0 : 1)); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
10860 foreach ($ways as $way) {
10861 $color = $c->color;
10862 $sfortag = '<li class="select2-search-choice-dolibarr noborderoncategories'.(empty($toprint) ? ' nomarginleft' : '');
10863 $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.
10864 if ($c->color && colorIsLight($c->color)) {
10865 $forced_color = 'categtextblack';
10866 }
10867 $sfortag .= ' '.$forced_color;
10868 $sfortag .= '"';
10869 $sfortag .= ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"');
10870 $titlestring = $ways[0];
10871 $titlestring = str_replace('>', ' - ', dol_string_nohtmltag($titlestring));
10872 $sfortag .= ' title="' . dolPrintHTMLForAttribute($titlestring) . '"';
10873 $sfortag .= '>';
10874 if ($rendermode == 1) {
10875 $sfortag .= '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.((int) $c->id).'&type='.urlencode($c->type).'" class="'.$forced_color.'">';
10876 $sfortag .= img_picto('', 'category', 'class="paddingright"');
10877 if ($conf->dol_optimize_smallscreen) {
10878 $sfortag .= dolPrintHTML(dol_trunc($c->label, 8));
10879 } else {
10880 $sfortag .= dolPrintHTML($c->label);
10881 }
10882 $sfortag .= '</a>';
10883 } else {
10884 $sfortag .= $way;
10885 }
10886 $sfortag .= '</li>';
10887
10888 $toprint[] = $sfortag; // Add tag in list of tag to show
10889 }
10890 }
10891 if (empty($toprint)) {
10892 return '';
10893 } else {
10894 return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
10895 }
10896 }
10897
10898 if ($rendermode == 0) {
10899 $arrayselected = array();
10900 $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 3);
10901 foreach ($categories as $c) {
10902 $arrayselected[(string) $c->id] = (string) $c->id;
10903 }
10904
10905 return $this->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, '', 0, '100%', 'disabled', 'category');
10906 }
10907
10908 return 'ErrorBadValueForParameterRenderMode'; // Should not happened
10909 }
10910
10920 public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = array(), $title = 'RelatedObjects')
10921 {
10922 global $conf, $langs, $hookmanager;
10923 global $action;
10924 global $db, $user; // Will be used into tpl
10925
10926 dol_syslog(__METHOD__, LOG_DEBUG);
10927
10928 $object->fetchObjectLinked();
10929
10930 // Bypass the default method
10931 $hookmanager->initHooks(array('commonobject'));
10932 $parameters = array(
10933 'morehtmlright' => $morehtmlright,
10934 'compatibleImportElementsList' => &$compatibleImportElementsList,
10935 );
10936 $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
10937
10938 $nbofdifferenttypes = count($object->linkedObjects);
10939
10940 if (empty($reshook)) {
10941 print '<!-- showLinkedObjectBlock -->';
10942 print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, '', 'showlinkedobjectblock');
10943
10944
10945 print '<div class="div-table-responsive-no-min">';
10946 print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '" data-elementid="' . $object->id . '" >';
10947
10948 print '<tr class="liste_titre">';
10949 print '<td>' . $langs->trans("Type") . '</td>';
10950 print '<td>' . $langs->trans("Ref") . '</td>';
10951 print '<td></td>';
10952 print '<td></td>';
10953 print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
10954 print '<td class="right">' . $langs->trans("Status") . '</td>';
10955 print '<td></td>';
10956 print '</tr>';
10957
10958 $nboftypesoutput = 0;
10959
10960 foreach ($object->linkedObjects as $objecttype => $objects) {
10961 $tplpath = $element = $subelement = $objecttype;
10962
10963 // to display import button on tpl
10964 global $showImportButton; // Will be used into tpl
10965 $showImportButton = false;
10966 if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
10967 $showImportButton = true;
10968 }
10969
10970 $regs = array();
10971
10972 if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
10973 $element = $regs[1];
10974 $subelement = $regs[2];
10975 $tplpath = $element . '/' . $subelement;
10976 }
10977 $tplname = 'linkedobjectblock';
10978
10979 // If we ask a resource form external module (instead of default path)
10980 if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) { // 'myobject@mymodule'
10981 $element = $regs[1];
10982 $module = $regs[2];
10983 $tplpath = $module. '/' . $element;
10984 $tplname = $tplname.'_'.$element;
10985 }
10986
10987 // To work with non standard path
10988 if ($objecttype == 'facture') {
10989 $tplpath = 'compta/' . $element;
10990 if (!isModEnabled('invoice')) {
10991 continue; // Do not show if module disabled
10992 }
10993 } elseif ($objecttype == 'facturerec') {
10994 $tplpath = 'compta/facture';
10995 $tplname = 'linkedobjectblockForRec';
10996 if (!isModEnabled('invoice')) {
10997 continue; // Do not show if module disabled
10998 }
10999 } elseif ($objecttype == 'propal') {
11000 $tplpath = 'comm/' . $element;
11001 if (!isModEnabled('propal')) {
11002 continue; // Do not show if module disabled
11003 }
11004 } elseif ($objecttype == 'supplier_proposal') {
11005 if (!isModEnabled('supplier_proposal')) {
11006 continue; // Do not show if module disabled
11007 }
11008 } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
11009 $tplpath = 'expedition';
11010 if (!isModEnabled('shipping')) {
11011 continue; // Do not show if module disabled
11012 }
11013 } elseif ($objecttype == 'reception') {
11014 $tplpath = 'reception';
11015 if (!isModEnabled('reception')) {
11016 continue; // Do not show if module disabled
11017 }
11018 } elseif ($objecttype == 'delivery') {
11019 $tplpath = 'delivery';
11020 if (!getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) {
11021 continue; // Do not show if sub module disabled
11022 }
11023 } elseif ($objecttype == 'ficheinter') {
11024 $tplpath = 'fichinter';
11025 if (!isModEnabled('intervention')) {
11026 continue; // Do not show if module disabled
11027 }
11028 } elseif ($objecttype == 'invoice_supplier') {
11029 $tplpath = 'fourn/facture';
11030 } elseif ($objecttype == 'order_supplier') {
11031 $tplpath = 'fourn/commande';
11032 } elseif ($objecttype == 'expensereport') {
11033 $tplpath = 'expensereport';
11034 } elseif ($objecttype == 'subscription') {
11035 $tplpath = 'adherents';
11036 } elseif ($objecttype == 'conferenceorbooth') {
11037 $tplpath = 'eventorganization';
11038 } elseif ($objecttype == 'conferenceorboothattendee') {
11039 $tplpath = 'eventorganization';
11040 } elseif ($objecttype == 'mo') {
11041 $tplpath = 'mrp';
11042 if (!isModEnabled('mrp')) {
11043 continue; // Do not show if module disabled
11044 }
11045 } elseif ($objecttype == 'project_task') {
11046 $tplpath = 'projet/tasks';
11047 }
11048
11049 global $linkedObjectBlock; // Will be used into tpl
11050 $linkedObjectBlock = $objects;
11051
11052 // Output template part (modules that overwrite templates must declare this into descriptor)
11053 $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
11054
11055 foreach ($dirtpls as $reldir) {
11056 $reldir = rtrim($reldir, '/');
11057 if ($nboftypesoutput == ($nbofdifferenttypes - 1)) { // No more type to show after
11058 global $noMoreLinkedObjectBlockAfter; // Will be used into tpl
11059 $noMoreLinkedObjectBlockAfter = 1;
11060 }
11061 $file = dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
11062 if (file_exists($file)) {
11063 $res = @include $file;
11064 if ($res) {
11065 $nboftypesoutput++;
11066 break;
11067 }
11068 }
11069 }
11070 }
11071
11072 if (!$nboftypesoutput) {
11073 print '<tr><td colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
11074 }
11075
11076 print '</table>';
11077
11078 if (!empty($compatibleImportElementsList)) {
11079 $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
11080 }
11081
11082 print '</div>';
11083 }
11084
11085 return $nbofdifferenttypes;
11086 }
11087
11097 public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array(), $nooutput = 0)
11098 {
11099 global $conf, $langs, $hookmanager, $form;
11100 global $action;
11101
11102 dol_syslog(__METHOD__, LOG_DEBUG);
11103
11104 if (empty($form)) {
11105 $form = new Form($this->db);
11106 }
11107
11108 $linktoelem = '';
11109 $linktoelemlist = '';
11110 $listofidcompanytoscan = '';
11111
11112 if (!is_object($object->thirdparty)) {
11113 if ($object->element == 'subscription' && isset($object->fk_adherent)) {
11114 $subby = new Subscription($object->db);
11115 $subby->fetch($object->id);
11116 $adh = new Adherent($object->db);
11117 //$fk_adherent = $object->fk_adherent;
11118 // creating new subscription object only to fetch the adherent which obviously exists given the if statement above are Inefficient, but else phan complains
11119 $fk_adherent = $subby->fk_adherent;
11120 $adh->fetch($fk_adherent);
11121 $thirdparty_id = $adh->fetch_thirdparty();
11122 }
11123 } else {
11124 $thirdparty_id = $object->thirdparty->id;
11125 }
11126
11127 $possiblelinks = array();
11128
11129 $dontIncludeCompletedItems = getDolGlobalString('DONT_INCLUDE_COMPLETED_ELEMENTS_LINKS');
11130
11131 if (!empty($thirdparty_id) && $thirdparty_id > 0) {
11132 $listofidcompanytoscan = (int) $thirdparty_id;
11133 if (is_object($object->thirdparty) && ($object->thirdparty->parent > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PARENT_IN_LINKTO')) {
11134 $listofidcompanytoscan .= ',' . (int) $object->thirdparty->parent;
11135 }
11136 if (($object->fk_project > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO')) {
11137 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
11138 $tmpproject = new Project($this->db);
11139 $tmpproject->fetch((int) $object->fk_project);
11140 if ($tmpproject->socid > 0 && ($tmpproject->socid != $thirdparty_id)) {
11141 $listofidcompanytoscan .= ',' . (int) $tmpproject->socid;
11142 }
11143 unset($tmpproject);
11144 }
11145
11146 $possiblelinks = array(
11147 'propal' => array(
11148 'enabled' => isModEnabled('propal'),
11149 'perms' => 1,
11150 'label' => 'LinkToProposal',
11151 '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' : ''),
11152 ),
11153 'shipping' => array(
11154 'enabled' => isModEnabled('shipping'),
11155 'perms' => 1,
11156 'label' => 'LinkToExpedition',
11157 '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' : ''),
11158 ),
11159 'order' => array(
11160 'enabled' => isModEnabled('order'),
11161 'perms' => 1,
11162 'label' => 'LinkToOrder',
11163 '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' : ''),
11164 'linkname' => 'commande',
11165 ),
11166 'subscription' => array(
11167 'enabled' => isModEnabled('member'),
11168 'perms' => 1,
11169 'label' => 'LinkToMemberSubscription',
11170 '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') . ')',
11171 'linkname' => 'subscription',
11172 ),
11173 'conferenceorboothattendee' => array(
11174 'enabled' => isModEnabled('eventorganization'),
11175 'perms' => 1,
11176 'label' => 'LinkToConferenceOrBoothAttendee',
11177 '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),
11178 'linkname' => 'attendee'
11179 ),
11180 'invoice' => array(
11181 'enabled' => isModEnabled('invoice'),
11182 'perms' => 1,
11183 'label' => 'LinkToInvoice',
11184 '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' : ''),
11185 'linkname' => 'facture',
11186 ),
11187 'invoice_template' => array(
11188 'enabled' => isModEnabled('invoice'),
11189 'perms' => 1,
11190 'label' => 'LinkToTemplateInvoice',
11191 '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') . ')',
11192 ),
11193 'contrat' => array(
11194 'enabled' => isModEnabled('contract'),
11195 'perms' => 1,
11196 'label' => 'LinkToContract',
11197 '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
11198 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',
11199 ),
11200 'fichinter' => array(
11201 'enabled' => isModEnabled('intervention'),
11202 'perms' => 1,
11203 'label' => 'LinkToIntervention',
11204 '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') . ')',
11205 ),
11206 'supplier_proposal' => array(
11207 'enabled' => isModEnabled('supplier_proposal'),
11208 'perms' => 1,
11209 'label' => 'LinkToSupplierProposal',
11210 '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' : ''),
11211 ),
11212 'order_supplier' => array(
11213 'enabled' => isModEnabled("supplier_order"),
11214 'perms' => 1,
11215 'label' => 'LinkToSupplierOrder',
11216 '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' : ''),
11217 ),
11218 'invoice_supplier' => array(
11219 'enabled' => isModEnabled("supplier_invoice"),
11220 'perms' => 1, 'label' => 'LinkToSupplierInvoice',
11221 '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' : ''),
11222 ),
11223 'ticket' => array(
11224 'enabled' => isModEnabled('ticket'),
11225 'perms' => 1,
11226 'label' => 'LinkToTicket',
11227 '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' : ''),
11228 ),
11229 'mo' => array(
11230 'enabled' => isModEnabled('mrp'),
11231 'perms' => 1,
11232 'label' => 'LinkToMo',
11233 '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' : ''),
11234 ),
11235 );
11236 }
11237
11238 if ($object->table_element == 'commande_fournisseur') {
11239 $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' : '');
11240 } elseif ($object->table_element == 'mrp_mo') {
11241 $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' : '');
11242 }
11243
11244 $reshook = 0; // Ensure $reshook is defined for static analysis
11245 if (!empty($listofidcompanytoscan)) { // If empty, we don't have criteria to scan the object we can link to
11246 // Can complete the possiblelink array
11247 $hookmanager->initHooks(array('commonobject'));
11248 $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
11249 $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
11250 }
11251
11252 if (empty($reshook)) {
11253 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11254 $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
11255 }
11256 } elseif ($reshook > 0) {
11257 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11258 $possiblelinks = $hookmanager->resArray;
11259 }
11260 }
11261
11262 if (!empty($possiblelinks)) {
11263 $object->fetchObjectLinked();
11264 }
11265
11266 // Build the html part with possible suggested links
11267 $htmltoenteralink = '';
11268 foreach ($possiblelinks as $key => $possiblelink) {
11269 $num = 0;
11270 if (empty($possiblelink['enabled'])) {
11271 continue;
11272 }
11273
11274
11275 // If we ask a resource form external module (instead of default path)
11276 $module = '';
11277 if (preg_match('/^([^@]+)@([^@]+)$/i', $key, $regs)) { // 'myobject@mymodule'
11278 $key = $regs[1];
11279 $module = $regs[2];
11280 }
11281
11282 if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
11283 $htmltoenteralink .= '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
11284
11285 // Section for free ref input
11286 if (!getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11287 $htmltoenteralink .= '<br>'."\n";
11288 $htmltoenteralink .= '<!-- form to add a link from anywhere -->'."\n";
11289 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
11290 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11291 $htmltoenteralink .= '<input type="hidden" name="action" value="addlinkbyref">';
11292 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11293 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key .(!empty($module) ? '@'.$module : ''). '">';
11294 $htmltoenteralink .= '<table class="noborder">';
11295 $htmltoenteralink .= '<tr class="liste_titre">';
11296 //print '<td>' . $langs->trans("Ref") . '</td>';
11297 $htmltoenteralink .= '<td class="center"><input type="text" placeholder="'.dol_escape_htmltag($langs->trans("Ref")).'" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">';
11298 $htmltoenteralink .= '<br>';
11299 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
11300 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp" name="cancel" value="' . $langs->trans('Cancel') . '">';
11301 $htmltoenteralink .= '</td>';
11302 $htmltoenteralink .= '</tr>';
11303 $htmltoenteralink .= '</table>';
11304 $htmltoenteralink .= '</form>';
11305 }
11306
11307 $sql = $possiblelink['sql'];
11308
11309 $resqllist = $this->db->query($sql);
11310 if ($resqllist) {
11311 $num = $this->db->num_rows($resqllist);
11312
11313 if ($num > 0) {
11314 // Section for free predefined list
11315 if (getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11316 $htmltoenteralink .= '<br>';
11317 }
11318 $htmltoenteralink .= '<!-- form to add a link from object to same thirdparty -->'."\n";
11319 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
11320 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11321 $htmltoenteralink .= '<input type="hidden" name="action" value="addlink">';
11322 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11323 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key . (!empty($module) ? '@'.$module : ''). '">';
11324 $htmltoenteralink .= '<table class="noborder">';
11325
11326 switch ($key) {
11327 case 'conferenceorboothattendee':
11328 // Custom logic for linking to attendees
11329 $htmltoenteralink .= $this->makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist);
11330 break;
11331
11332 default:
11333 // Standard logic for all other object types
11334 $htmltoenteralink .= $this->makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist);
11335 break;
11336 }
11337
11338 $htmltoenteralink .= '</table>';
11339 $htmltoenteralink .= '<div class="center">';
11340 if ($num) {
11341 $htmltoenteralink .= '<input type="submit" class="button valignmiddle marginleftonly marginrightonly smallpaddingimp" value="' . $langs->trans('ToLink') . '">';
11342 }
11343 if (empty($conf->use_javascript_ajax)) {
11344 $htmltoenteralink .= '<input type="submit" class="button button-cancel marginleftonly marginrightonly smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
11345 } else {
11346 $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>';
11347 }
11348 $htmltoenteralink .= '</form>';
11349 }
11350
11351 $this->db->free($resqllist);
11352 } else {
11353 dol_print_error($this->db);
11354 }
11355 $htmltoenteralink .= '</div>';
11356
11357
11358 // Complete the list for the combo box
11359 if ($num > 0 || !getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11360 $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
11361 // } else $linktoelem.=$langs->trans($possiblelink['label']);
11362 } else {
11363 $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
11364 }
11365 }
11366 }
11367
11368 if ($linktoelemlist) {
11369 $linktoelem = '
11370 <dl class="dropdown" id="linktoobjectname">
11371 ';
11372 if (!empty($conf->use_javascript_ajax)) {
11373 $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
11374 }
11375 $linktoelem .= '<dd>
11376 <div class="multiselectlinkto">
11377 <ul class="ulselectedfields">' . $linktoelemlist . '
11378 </ul>
11379 </div>
11380 </dd>
11381 </dl>';
11382 } else {
11383 $linktoelem = '';
11384 }
11385
11386 if (!empty($conf->use_javascript_ajax)) {
11387 print '<!-- Add js to show linkto box -->
11388 <script nonce="' . getNonce() . '">
11389 jQuery(document).ready(function() {
11390 jQuery(".linkto").click(function() {
11391 console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
11392 jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
11393 });
11394 });
11395 </script>
11396 ';
11397 }
11398
11399 if ($nooutput) {
11400 return array('linktoelem' => $linktoelem, 'htmltoenteralink' => $htmltoenteralink);
11401 } else {
11402 print $htmltoenteralink;
11403 }
11404
11405 return $linktoelem;
11406 }
11407
11422 public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = 'yesno width75', $labelyes = 'Yes', $labelno = 'No')
11423 {
11424 global $langs;
11425
11426 $yes = "yes";
11427 $no = "no";
11428 if ($option) {
11429 $yes = "1";
11430 $no = "0";
11431 }
11432
11433 $disabled = ($disabled ? ' disabled' : '');
11434
11435 $resultyesno = '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
11436 if ($useempty) {
11437 $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
11438 }
11439 if (("$value" == 'yes') || ($value == 1)) {
11440 $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
11441 $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
11442 } else {
11443 $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
11444 $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
11445 $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
11446 }
11447 $resultyesno .= '</select>' . "\n";
11448
11449 if ($addjscombo) {
11450 $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
11451 }
11452
11453 return $resultyesno;
11454 }
11455
11456 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11457
11467 public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
11468 {
11469 // phpcs:enable
11470 $sql = "SELECT rowid, label";
11471 $sql .= " FROM " . $this->db->prefix() . "export_model";
11472 $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
11473 $sql .= " ORDER BY rowid";
11474 $result = $this->db->query($sql);
11475 if ($result) {
11476 print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
11477 if ($useempty) {
11478 print '<option value="-1">&nbsp;</option>';
11479 }
11480
11481 $num = $this->db->num_rows($result);
11482 $i = 0;
11483 while ($i < $num) {
11484 $obj = $this->db->fetch_object($result);
11485 if ($selected == $obj->rowid) {
11486 print '<option value="' . $obj->rowid . '" selected>';
11487 } else {
11488 print '<option value="' . $obj->rowid . '">';
11489 }
11490 print $obj->label;
11491 print '</option>';
11492 $i++;
11493 }
11494 print "</select>";
11495 } else {
11496 dol_print_error($this->db);
11497 }
11498 }
11499
11518 public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
11519 {
11520 global $conf, $langs, $hookmanager, $extralanguages;
11521
11522 $ret = '';
11523 if (empty($fieldid)) {
11524 $fieldid = 'rowid';
11525 }
11526 if (empty($fieldref)) {
11527 $fieldref = 'ref';
11528 }
11529
11530 // Preparing gender's display if there is one
11531 $addgendertxt = '';
11532 if (property_exists($object, 'gender') && !empty($object->gender)) {
11533 $addgendertxt = ' ';
11534 switch ($object->gender) {
11535 case 'man':
11536 $addgendertxt .= '<i class="fas fa-mars valignmiddle"></i>';
11537 break;
11538 case 'woman':
11539 $addgendertxt .= '<i class="fas fa-venus valignmiddle"></i>';
11540 break;
11541 case 'other':
11542 $addgendertxt .= '<i class="fas fa-transgender valignmiddle"></i>';
11543 break;
11544 }
11545 }
11546
11547 // Add where from hooks
11548 if (is_object($hookmanager)) {
11549 $parameters = array('showrefnav' => true);
11550 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
11551 if (!empty($hookmanager->resPrint)) {
11552 if (empty($object->next_prev_filter) && preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11553 $object->next_prev_filter = (string) preg_replace('/^\s*AND\s*/i', '', $hookmanager->resPrint);
11554 } elseif (!empty($object->next_prev_filter) && !preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11555 $object->next_prev_filter .= ' AND '.$hookmanager->resPrint;
11556 } else {
11557 $object->next_prev_filter .= $hookmanager->resPrint;
11558 }
11559 }
11560 }
11561
11562 $previous_ref = $next_ref = '';
11563 if ($shownav) {
11564 //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,fieldid=$fieldid,filedref=$fieldref,morehtmlref=$morehtmlref,moreparam=$moreparam";
11565 $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
11566
11567 $navurl = $_SERVER["PHP_SELF"];
11568
11569 // Special case for token card
11570 if ($paramid == 'api_token_card') {
11571 if (preg_match('/\/user\/api_token/', $navurl)) {
11572 $navurl = preg_replace('/card/', 'list', $navurl);
11573 $paramid = 'id';
11574 }
11575 }
11576
11577 // Special case for project/task page
11578 if ($paramid == 'project_ref') {
11579 if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) { // TODO Remove this when nav with project_ref on task pages are ok
11580 $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
11581 $paramid = 'ref';
11582 }
11583 }
11584
11585 $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>';
11586 $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>';
11587 }
11588
11589 //print "xx".$previous_ref."x".$next_ref;
11590 $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
11591
11592 // Right part of banner
11593 if ($morehtmlright) {
11594 $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
11595 }
11596
11597 if ($previous_ref || $next_ref || $morehtml) {
11598 $ret .= '<div class="pagination paginationref"><ul class="right">';
11599 }
11600 if ($morehtml && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
11601 $ret .= '<!-- morehtml --><li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
11602 }
11603 if ($shownav && ($previous_ref || $next_ref)) {
11604 $ret .= '<li class="pagination">' . $previous_ref . '</li>';
11605 $ret .= '<li class="pagination">' . $next_ref . '</li>';
11606 }
11607 if ($previous_ref || $next_ref || $morehtml) {
11608 $ret .= '</ul></div>';
11609 }
11610
11611 // Status
11612 $parameters = array('morehtmlstatus' => $morehtmlstatus);
11613 $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
11614 if (empty($reshook)) {
11615 $morehtmlstatus .= $hookmanager->resPrint;
11616 } else {
11617 $morehtmlstatus = $hookmanager->resPrint;
11618 }
11619 if ($morehtmlstatus) {
11620 $ret .= '<!-- status --><div class="statusref">' . $morehtmlstatus . '</div>';
11621 }
11622
11623 $parameters = array();
11624 $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
11625 if (empty($reshook)) {
11626 $morehtmlref .= $hookmanager->resPrint;
11627 } elseif ($reshook > 0) {
11628 $morehtmlref = $hookmanager->resPrint;
11629 }
11630
11631 // Left part of banner
11632 if ($morehtmlleft) {
11633 if ($conf->browser->layout == 'phone') {
11634 $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
11635 } else {
11636 $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
11637 }
11638 }
11639
11640 //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
11641 $ret .= '<!-- Ref or ID --><div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
11642
11643 // For thirdparty, contact, user, member, the ref is the id, so we show something else
11644 if ($object->element == 'societe') {
11645 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->name).'</span>';
11646
11647 // List of extra languages
11648 $arrayoflangcode = array();
11649 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
11650 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
11651 }
11652
11653 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
11654 if (!is_object($extralanguages)) {
11655 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
11656 $extralanguages = new ExtraLanguages($this->db);
11657 }
11658 $extralanguages->fetch_name_extralanguages('societe');
11659
11660 // Guard against PHP 8 'Undefined array key' when MAIN_USE_ALTERNATE_TRANSLATION_FOR
11661 // is not configured and fetch_name_extralanguages() leaves attributes empty (issue #34596).
11662 if (!empty($extralanguages->attributes['societe']) && !empty($extralanguages->attributes['societe']['name'])) {
11663 $object->fetchValuesForExtraLanguages();
11664
11665 $htmltext = '';
11666 // If there is extra languages
11667 foreach ($arrayoflangcode as $extralangcode) {
11668 $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
11669 if ($object->array_languages['name'][$extralangcode]) {
11670 $htmltext .= $object->array_languages['name'][$extralangcode];
11671 } else {
11672 $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
11673 }
11674 }
11675 $ret .= '<!-- Show translations of name -->' . "\n";
11676 $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
11677 }
11678 }
11679 } elseif ($object->element == 'member') {
11680 '@phan-var-force Adherent $object';
11681 $ret .= $object->ref . '<br>';
11682 $fullname = $object->getFullName($langs);
11683 if ($object->morphy == 'mor' && $object->societe) {
11684 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '').'</span>';
11685 } else {
11686 $ret .= '<span class="valignmiddle">'.dolPrintHTML($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities((string) $object->societe) . ')' : '').'</span>';
11687 }
11688 } elseif (in_array($object->element, array('contact', 'user'))) {
11689 $ret .= '<span class="valignmiddle">'.dolPrintHTML($object->getFullName($langs)).'</span>'.$addgendertxt;
11690 } elseif ($object->element == 'usergroup') {
11691 $ret .= dol_htmlentities((string) $object->name);
11692 } elseif (in_array($object->element, array('action', 'agenda'))) {
11693 '@phan-var-force ActionComm $object';
11694 $ret .= $object->ref . '<br>' . $object->label;
11695 } elseif (in_array($object->element, array('adherent_type'))) {
11696 $ret .= $object->label;
11697 } elseif ($object->element == 'ecm_directories') {
11698 $ret .= '';
11699 } elseif ($object->element == 'accountingbookkeeping' && !empty($object->context['mode']) && $object->context['mode'] == '_tmp') {
11700 $ret .= '<span class="valignmiddle">'.$langs->trans("Draft").'</span>';
11701 } elseif ($object instanceof Ticket) {
11702 '@phan-var-force Ticket $object';
11703 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11704 $ret .= ' &nbsp; <span class="nobold small" title="'.dolPrintHTMLForAttribute($langs->trans("TicketTrackId")).'">('.$object->track_id.')</span>';
11705 } elseif ($fieldref != 'none') {
11706 // Generic case
11707 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11708 }
11709 if ($morehtmlref) {
11710 // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
11711 if (substr($morehtmlref, 0, 4) != '<div') {
11712 $ret .= ' ';
11713 }
11714
11715 $ret .= '<!-- morehtmlref -->'.$morehtmlref;
11716 }
11717
11718 $ret .= '</div>';
11719
11720 $ret .= '</div><!-- End banner content -->';
11721
11722 return $ret;
11723 }
11724
11725
11734 public function showbarcode(&$object, $width = 100, $morecss = '')
11735 {
11736 //Check if barcode is filled in the card
11737 if (empty($object->barcode)) {
11738 return '';
11739 }
11740
11741 // Complete object if not complete
11742 if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
11743 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11744 $result = $object->fetchBarCode();
11745 //Check if fetchBarCode() failed
11746 if ($result < 1) {
11747 return '<!-- ErrorFetchBarcode -->';
11748 }
11749 }
11750
11751 // Barcode image @phan-suppress-next-line PhanUndeclaredProperty
11752 $url = DOL_URL_ROOT . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
11753 $out = '<!-- url barcode = ' . $url . ' -->';
11754 $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
11755
11756 return $out;
11757 }
11758
11777 public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0, $usesharelinkifavailable = 0)
11778 {
11779 global $conf, $db, $langs;
11780
11781 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
11782 $id = (empty($object->id) ? $object->rowid : $object->id); // @phan-suppress-current-line PhanUndeclaredProperty (->rowid)
11783
11784 $dir = '';
11785 $file = '';
11786 $originalfile = '';
11787 $altfile = '';
11788 $email = '';
11789 $capture = '';
11790 if ($modulepart == 'societe') {
11791 $dir = $conf->societe->multidir_output[$entity];
11792 if (!empty($object->logo)) {
11793 if (dolIsAllowedForPreview($object->logo)) {
11794 if ((string) $imagesize == 'mini') {
11795 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
11796 } elseif ((string) $imagesize == 'small') {
11797 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
11798 } else {
11799 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11800 }
11801 $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11802 }
11803 }
11804 $email = $object->email;
11805 } elseif ($modulepart == 'contact') {
11806 $dir = $conf->societe->multidir_output[$entity] . '/contact';
11807 $photo = $object->photo; // Copy to help static analysis
11808 if (!empty($photo)) {
11809 if (dolIsAllowedForPreview($photo)) {
11810 if ((string) $imagesize == 'mini') {
11811 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11812 } elseif ((string) $imagesize == 'small') {
11813 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_small');
11814 } else {
11815 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11816 }
11817 $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11818 }
11819 }
11820 $email = $object->email;
11821 $capture = 'user';
11822 } elseif ($modulepart == 'userphoto') {
11823 $dir = $conf->user->dir_output;
11824 $photo = $object->photo; // Copy to help static analysis
11825 if (!empty($photo)) {
11826 if (dolIsAllowedForPreview($photo)) {
11827 if ((string) $imagesize == 'mini') {
11828 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11829 } elseif ((string) $imagesize == 'small') {
11830 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_small');
11831 } else {
11832 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11833 }
11834 $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11835 }
11836 }
11837 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11838 $altfile = $object->id . ".jpg"; // For backward compatibility
11839 }
11840 $email = $object->email;
11841 $capture = 'user';
11842 } elseif ($modulepart == 'memberphoto') {
11843 $dir = $conf->member->dir_output;
11844 $photo = $object->photo; // Copy to help static analysis
11845 if (!empty($photo)) {
11846 if (dolIsAllowedForPreview($photo)) {
11847 if ((string) $imagesize == 'mini') {
11848 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11849 } elseif ((string) $imagesize == 'small') {
11850 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_small');
11851 } else {
11852 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11853 }
11854 $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11855 }
11856 }
11857 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11858 $altfile = $object->id . ".jpg"; // For backward compatibility
11859 }
11860 $email = $object->email;
11861 $capture = 'user';
11862 } else {
11863 // Generic case to show photos
11864 // TODO Implement this method in previous objects so we can always use this generic method.
11865 if ($modulepart != "unknown" && method_exists($object, 'getDataToShowPhoto')) {
11866 $tmpdata = $object->getDataToShowPhoto($modulepart, $imagesize);
11867
11868 $dir = $tmpdata['dir'];
11869 $file = $tmpdata['file'];
11870 $originalfile = $tmpdata['originalfile'];
11871 $altfile = $tmpdata['altfile'];
11872 $email = $tmpdata['email'];
11873 $capture = $tmpdata['capture'];
11874 }
11875 }
11876
11877 if ($forcecapture) {
11878 $capture = $forcecapture;
11879 }
11880
11881 $ret = '';
11882
11883 if ($dir) {
11884 if ($file && file_exists($dir . "/" . $file)) {
11885 if ($addlinktofullsize) {
11886 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11887 if ($urladvanced) {
11888 $ret .= '<a href="' . $urladvanced . '">';
11889 } else {
11890 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11891 }
11892 }
11893
11894 $sharekey = '';
11895 if ($usesharelinkifavailable) {
11896 // $dir is a full path '/home/.../dolibarr_documents/module'
11897 $relativefileforecm = preg_replace('/^'.preg_quote(DOL_DATA_ROOT.'/', '/').'/', '', $dir.'/'.$originalfile);
11898 // $relativefileforecme = 'module/...'
11899 require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php';
11900 $ecmfiles = new EcmFiles($db);
11901 $ecmfiles->fetch(0, '', $relativefileforecm);
11902
11903 $sharekey = (string) $ecmfiles->share;
11904 }
11905
11906 if (!empty($sharekey)) {
11907 $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) . '">';
11908 } else {
11909 $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) . '">';
11910 }
11911 if ($addlinktofullsize) {
11912 $ret .= '</a>';
11913 }
11914 } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
11915 if ($addlinktofullsize) {
11916 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11917 if ($urladvanced) {
11918 $ret .= '<a href="' . $urladvanced . '">';
11919 } else {
11920 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11921 }
11922 }
11923 $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) . '">';
11924 if ($addlinktofullsize) {
11925 $ret .= '</a>';
11926 }
11927 } else {
11928 $nophoto = '/public/theme/common/nophoto.png';
11929 $defaultimg = 'identicon'; // For gravatar
11930 if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) { // For modules that need a special image when photo not found
11931 if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor') !== false)) {
11932 $nophoto = 'company';
11933 } else {
11934 $nophoto = '/public/theme/common/user_anonymous.png';
11935 if (!empty($object->gender) && $object->gender == 'man') {
11936 $nophoto = '/public/theme/common/user_man.png';
11937 }
11938 if (!empty($object->gender) && $object->gender == 'woman') {
11939 $nophoto = '/public/theme/common/user_woman.png';
11940 }
11941 }
11942 }
11943
11944 if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
11945 // see https://gravatar.com/site/implement/images/php/
11946 $ret .= '<!-- Put link to gravatar -->';
11947 $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
11948 } else {
11949 if ($nophoto == 'company') {
11950 $ret .= '<div class="divforspanimg valignmiddle inline-block center photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
11951 //$ret .= '<div class="difforspanimgright"></div>';
11952 } else {
11953 $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '">';
11954 }
11955 }
11956 }
11957
11958 if ($caneditfield) {
11959 if ($object->photo) {
11960 $ret .= "<br>\n";
11961 }
11962 $ret .= '<table class="nobordernopadding centpercent">';
11963 if ($object->photo) {
11964 $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
11965 }
11966 $ret .= '<tr><td class="tdoverflow">';
11967 $maxfilesizearray = getMaxFileSizeArray();
11968 $maxmin = $maxfilesizearray['maxmin'];
11969 if ($maxmin > 0) {
11970 $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">'; // MAX_FILE_SIZE must precede the field type=file
11971 }
11972 $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . dolPrintHTMLForAttribute($capture) . '"' : '') . '>';
11973 $ret .= '</td></tr>';
11974 $ret .= '</table>';
11975 }
11976 }
11977
11978 return $ret;
11979 }
11980
11981 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11982
11999 public function select_dolgroups($selected = 0, $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0', $multiple = false, $morecss = 'minwidth200')
12000 {
12001 // phpcs:enable
12002 global $conf, $user, $langs;
12003
12004 // Allow excluding groups
12005 $excludeGroups = null;
12006 if (is_array($exclude)) {
12007 $excludeGroups = implode(",", $exclude);
12008 }
12009 // Allow including groups
12010 $includeGroups = null;
12011 if (is_array($include)) {
12012 $includeGroups = implode(",", $include);
12013 }
12014
12015 if (!is_array($selected)) {
12016 $selected = array($selected);
12017 }
12018
12019 $out = '';
12020
12021 // Build sql to search groups
12022 $sql = "SELECT ug.rowid, ug.nom as name";
12023 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12024 $sql .= ", e.label";
12025 }
12026 $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
12027 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12028 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
12029 if ($force_entity) {
12030 $sql .= " WHERE ug.entity IN (0, " . ((int) $force_entity) . ")";
12031 } else {
12032 $sql .= " WHERE ug.entity IS NOT NULL";
12033 }
12034 } else {
12035 $sql .= " WHERE ug.entity IN (0, " . ((int) $conf->entity) . ")";
12036 }
12037 if (is_array($exclude) && $excludeGroups) {
12038 $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
12039 }
12040 if (is_array($include) && $includeGroups) {
12041 $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
12042 }
12043 $sql .= " ORDER BY ug.nom ASC";
12044
12045 dol_syslog(get_class($this) . "::select_dolgroups", LOG_DEBUG);
12046 $resql = $this->db->query($sql);
12047 if ($resql) {
12048 // Enhance with select2
12049 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12050
12051 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
12052
12053 $num = $this->db->num_rows($resql);
12054 $i = 0;
12055 if ($num) {
12056 if ($show_empty && !$multiple) {
12057 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
12058 }
12059
12060 while ($i < $num) {
12061 $obj = $this->db->fetch_object($resql);
12062 $disableline = 0;
12063 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
12064 $disableline = 1;
12065 }
12066
12067 $label = $obj->name;
12068 $labelhtml = $obj->name;
12069 if (isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1) {
12070 $label .= " (" . $obj->label . ")";
12071 $labelhtml .= ' <span class="opacitymedium">(' . $obj->label . ')</span>';
12072 }
12073
12074 $out .= '<option value="' . $obj->rowid . '"';
12075 if ($disableline) {
12076 $out .= ' disabled';
12077 }
12078 if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid)
12079 || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
12080 $out .= ' selected';
12081 }
12082 $out .= ' data-html="'.dol_escape_htmltag($labelhtml).'"';
12083 $out .= '>';
12084 $out .= $label;
12085 $out .= '</option>';
12086 $i++;
12087 }
12088 } else {
12089 if ($show_empty) {
12090 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
12091 }
12092 $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
12093 }
12094 $out .= '</select>';
12095
12096 $out .= ajax_combobox($htmlname);
12097 } else {
12098 dol_print_error($this->db);
12099 }
12100
12101 return $out;
12102 }
12103
12104
12111 public function showFilterButtons($pos = '')
12112 {
12113 $out = '<div class="nowraponall">';
12114 $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fas fa-search"></span></button>';
12115 $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fas fa-times"></span></button>';
12116 $out .= '</div>';
12117
12118 return $out;
12119 }
12120
12129 public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12130 {
12131 global $conf;
12132
12133 $out = '';
12134
12135 if (!empty($conf->use_javascript_ajax)) {
12136 $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
12137 }
12138 $out .= '<script nonce="' . getNonce() . '">
12139 $(document).ready(function() {
12140 $("#' . $cssclass . 's").click(function() {
12141 if($(this).is(\':checked\')){
12142 console.log("We check all ' . $cssclass . ' and trigger the change method");
12143 $(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
12144 }
12145 else
12146 {
12147 console.log("We uncheck all");
12148 $(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
12149 }' . "\n";
12150 if ($calljsfunction) {
12151 $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
12152 }
12153 $out .= ' });
12154/*
12155 $(".' . $cssclass . '").change(function() {
12156 console.log("We check and change the tr class highlight after a change on .'.$cssclass.'");
12157 var $row = $(this).closest("tr");
12158 if ($row.length) {
12159 var anyChecked = $row.find(\'input[type="checkbox"].checkforselect:checked\').length > 0;
12160 console.log("anychecked="+anyChecked);
12161 if (!anyChecked) {
12162 $row.removeClass("highlight");
12163 } else {
12164 $row.addClass("highlight");
12165 }
12166 }
12167 });
12168*/
12169 });
12170 </script>';
12171
12172 return $out;
12173 }
12174
12184 public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12185 {
12186 $out = $this->showFilterButtons();
12187 if ($addcheckuncheckall) {
12188 $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
12189 }
12190 return $out;
12191 }
12192
12206 public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
12207 {
12208 global $langs, $user;
12209
12210 $out = '';
12211 $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
12212 $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
12213 if (!empty($excludeid)) {
12214 $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
12215 }
12216 $sql .= " ORDER BY label";
12217
12218 $resql = $this->db->query($sql);
12219 if ($resql) {
12220 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
12221 if ($useempty) {
12222 $out .= '<option value="0">&nbsp;</option>';
12223 }
12224
12225 while ($obj = $this->db->fetch_object($resql)) {
12226 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
12227 }
12228 $out .= '</select>';
12229 $out .= ajax_combobox('select_' . $htmlname);
12230
12231 if (!empty($htmlname) && $user->admin && $info_admin) {
12232 $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
12233 }
12234
12235 if (!empty($target)) {
12236 $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
12237 $resql = $this->db->query($sql);
12238 if ($resql) {
12239 if ($this->db->num_rows($resql) > 0) {
12240 $obj = $this->db->fetch_object($resql);
12241 $out .= '<script nonce="' . getNonce() . '">
12242 $(function() {
12243 $("select[name=' . $target . ']").on("change", function() {
12244 var current_val = $(this).val();
12245 if (current_val == ' . $obj->id . ') {';
12246 if (!empty($default_selected) || !empty($selected)) {
12247 $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
12248 }
12249
12250 $out .= '
12251 $("select[name=' . $htmlname . ']").change();
12252 }
12253 });
12254
12255 $("select[name=' . $htmlname . ']").change(function() {
12256
12257 if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
12258 // get price of kilometer to fill the unit price
12259 $.ajax({
12260 method: "POST",
12261 dataType: "json",
12262 data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
12263 url: "' . (DOL_URL_ROOT . '/expensereport/ajax/ajaxik.php?' . implode('&', $params)) . '",
12264 }).done(function( data, textStatus, jqXHR ) {
12265 console.log(data);
12266 if (typeof data.up != "undefined") {
12267 $("input[name=value_unit]").val(data.up);
12268 $("select[name=' . $htmlname . ']").attr("title", data.title);
12269 } else {
12270 $("input[name=value_unit]").val("");
12271 $("select[name=' . $htmlname . ']").attr("title", "");
12272 }
12273 });
12274 }
12275 });
12276 });
12277 </script>';
12278 }
12279 }
12280 }
12281 } else {
12282 dol_print_error($this->db);
12283 }
12284
12285 return $out;
12286 }
12287
12296 public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
12297 {
12298 global $conf, $langs;
12299
12300 $out = '';
12301 $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
12302 $sql .= " WHERE entity = " . ((int) $conf->entity) . " AND active = 1";
12303
12304 $resql = $this->db->query($sql);
12305 if ($resql) {
12306 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12307 if ($useempty) {
12308 $out .= '<option value="0"></option>';
12309 }
12310
12311 while ($obj = $this->db->fetch_object($resql)) {
12312 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
12313 }
12314 $out .= '</select>';
12315 } else {
12316 dol_print_error($this->db);
12317 }
12318
12319 return $out;
12320 }
12321
12332 public function selectExpenseFees($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
12333 {
12334 global $langs;
12335
12336 $out = '';
12337 $sql = "SELECT id, code, label";
12338 $sql .= " FROM ".$this->db->prefix()."c_type_fees";
12339 $sql .= " WHERE active = 1";
12340
12341 $resql = $this->db->query($sql);
12342 if ($resql) {
12343 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12344 if ($useempty) {
12345 $out .= '<option value="0"></option>';
12346 }
12347 if ($allchoice) {
12348 $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
12349 }
12350
12351 $field = 'code';
12352 if ($useid) {
12353 $field = 'id';
12354 }
12355
12356 while ($obj = $this->db->fetch_object($resql)) {
12357 $key = $langs->trans($obj->code);
12358 $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
12359 }
12360 $out .= '</select>';
12361
12362 $out .= ajax_combobox('select_'.$htmlname);
12363 } else {
12364 dol_print_error($this->db);
12365 }
12366
12367 return $out;
12368 }
12369
12388 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)
12389 {
12390 global $user, $conf, $langs;
12391
12392 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
12393
12394 if (is_null($usertofilter)) {
12395 $usertofilter = $user;
12396 }
12397
12398 $out = '';
12399
12400 $hideunselectables = false;
12401 if (getDolGlobalString('INVOICE_HIDE_UNSELECTABLES')) {
12402 $hideunselectables = true;
12403 }
12404
12405 if (empty($projectsListId)) {
12406 if (!$usertofilter->hasRight('projet', 'all', 'lire')) {
12407 $projectstatic = new Project($this->db);
12408 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
12409 }
12410 }
12411
12412 // Search all projects
12413 $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";
12414 $sql .= " FROM " . $this->db->prefix() . "facture as f";
12415 $sql .= " INNER JOIN " . $this->db->prefix() . "projet as p ON p.entity IN (" . getEntity('project') . ") AND f.fk_projet = p.rowid";
12416 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = p.fk_soc";
12417 $sql .= " WHERE f.fk_statut = 0"; // Draft invoices only
12418 //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
12419 //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
12420 //if ($socid > 0) $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
12421 $sql .= " ORDER BY p.ref, f.ref ASC";
12422
12423 $resql = $this->db->query($sql);
12424 if ($resql) {
12425 // Use select2 selector
12426 if (!empty($conf->use_javascript_ajax)) {
12427 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12428 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12429 $out .= $comboenhancement;
12430 $morecss = 'minwidth200imp maxwidth500';
12431 }
12432
12433 if (empty($option_only)) {
12434 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12435 }
12436 if (!empty($show_empty)) {
12437 $out .= '<option value="0" class="optiongrey">';
12438 if (!is_numeric($show_empty)) {
12439 $out .= $show_empty;
12440 } else {
12441 $out .= '&nbsp;';
12442 }
12443 $out .= '</option>';
12444 }
12445 $num = $this->db->num_rows($resql);
12446 $i = 0;
12447 if ($num) {
12448 while ($i < $num) {
12449 $obj = $this->db->fetch_object($resql);
12450 // 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.
12451 if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && !$usertofilter->hasRight('societe', 'lire')) {
12452 // Do nothing
12453 } else {
12454 if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
12455 $i++;
12456 continue;
12457 }
12458
12459 $labeltoshow = '';
12460
12461 if ($showproject == 'all') {
12462 $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
12463 if ($obj->name) {
12464 $labeltoshow .= ' - ' . $obj->name; // Soc name
12465 }
12466
12467 $disabled = 0;
12468 if ($obj->fk_statut == Project::STATUS_DRAFT) {
12469 $disabled = 1;
12470 $labeltoshow .= ' - ' . $langs->trans("Draft");
12471 } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
12472 if ($discard_closed == 2) {
12473 $disabled = 1;
12474 }
12475 $labeltoshow .= ' - ' . $langs->trans("Closed");
12476 } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
12477 $disabled = 1;
12478 $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
12479 }
12480 }
12481
12482 if (!empty($selected) && $selected == $obj->rowid) {
12483 $out .= '<option value="' . $obj->rowid . '" selected';
12484 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12485 $out .= '>' . $labeltoshow . '</option>';
12486 } else {
12487 if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
12488 $resultat = '';
12489 } else {
12490 $resultat = '<option value="' . $obj->rowid . '"';
12491 if ($disabled) {
12492 $resultat .= ' disabled';
12493 }
12494 //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
12495 //else $labeltoshow.=' ('.$langs->trans("Private").')';
12496 $resultat .= '>';
12497 $resultat .= $labeltoshow;
12498 $resultat .= '</option>';
12499 }
12500 $out .= $resultat;
12501 }
12502 }
12503 $i++;
12504 }
12505 }
12506 if (empty($option_only)) {
12507 $out .= '</select>';
12508 }
12509
12510 $this->db->free($resql);
12511
12512 return $out;
12513 } else {
12514 dol_print_error($this->db);
12515 return '';
12516 }
12517 }
12518
12533 public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
12534 {
12535 global $conf, $langs;
12536
12537 $out = '';
12538
12539 dol_syslog('FactureRec::fetch', LOG_DEBUG);
12540
12541 $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
12542 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
12543 $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
12544 $sql .= " ORDER BY f.titre ASC";
12545
12546 $resql = $this->db->query($sql);
12547 if ($resql) {
12548 // Use select2 selector
12549 if (!empty($conf->use_javascript_ajax)) {
12550 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12551 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12552 $out .= $comboenhancement;
12553 $morecss = 'minwidth200imp maxwidth500';
12554 }
12555
12556 if (empty($option_only)) {
12557 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12558 }
12559 if (!empty($show_empty)) {
12560 $out .= '<option value="0" class="optiongrey">';
12561 if (!is_numeric($show_empty)) {
12562 $out .= $show_empty;
12563 } else {
12564 $out .= '&nbsp;';
12565 }
12566 $out .= '</option>';
12567 }
12568 $num = $this->db->num_rows($resql);
12569 if ($num) {
12570 while ($obj = $this->db->fetch_object($resql)) {
12571 $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
12572
12573 $disabled = 0;
12574 if (!empty($obj->suspended)) {
12575 $disabled = 1;
12576 $labeltoshow .= ' - ' . $langs->trans("Closed");
12577 }
12578
12579
12580 if (!empty($selected) && $selected == $obj->rowid) {
12581 $out .= '<option value="' . $obj->rowid . '" selected';
12582 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12583 $out .= '>' . $labeltoshow . '</option>';
12584 } else {
12585 if ($disabled && ($selected != $obj->rowid)) {
12586 $resultat = '';
12587 } else {
12588 $resultat = '<option value="' . $obj->rowid . '"';
12589 if ($disabled) {
12590 $resultat .= ' disabled';
12591 }
12592 $resultat .= '>';
12593 $resultat .= $labeltoshow;
12594 $resultat .= '</option>';
12595 }
12596 $out .= $resultat;
12597 }
12598 }
12599 }
12600 if (empty($option_only)) {
12601 $out .= '</select>';
12602 }
12603
12604 print $out;
12605
12606 $this->db->free($resql);
12607 return $num;
12608 } else {
12609 $this->errors[] = $this->db->lasterror;
12610 return -1;
12611 }
12612 }
12613
12614
12625 public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '', $arrayoffiltercriterias = array())
12626 {
12627 // TODO: Use $arrayoffiltercriterias param instead of $arrayofcriterias to include linked object fields in search
12628 global $langs, $form;
12629
12630 //require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
12631 //$formother = new FormOther($this->db);
12632
12633 if ($search_component_params_hidden != '' && !preg_match('/^\‍(.*\‍)$/', $search_component_params_hidden)) { // If $search_component_params_hidden does not start and end with ()
12634 $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
12635 }
12636
12637 $ret = '<!-- searchComponent -->';
12638
12639 $ret .= '<div class="divadvancedsearchfieldcomp centpercent inline-block">';
12640 $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
12641 $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
12642 $ret .= '</a>';
12643
12644 $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
12645
12646 // Show select fields as tags.
12647 $ret .= '<div id="divsearch_component_params" name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
12648
12649 if ($search_component_params_hidden) {
12650 // Split the criteria on each AND
12651 //var_dump($search_component_params_hidden);
12652
12653 $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
12654
12655 // $arrayofandtags is now array( '...' , '...', ...)
12656 // Show each AND part
12657 foreach ($arrayofandtags as $tmpkey => $tmpval) {
12658 $errormessage = '';
12659 $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
12660 if ($errormessage) {
12661 $this->error = 'ERROR in parsing search string: '.$errormessage;
12662 }
12663 // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
12664 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12665 $searchtags = removeGlobalParenthesis($searchtags);
12666
12667 $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="'.($tmpkey + 1).'" data-ufilter="'.dol_escape_htmltag($tmpval).'">';
12668 $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="'.($tmpkey + 1).'">x</span> ';
12669 $ret .= dol_escape_htmltag($searchtags);
12670 $ret .= '</span>';
12671 }
12672 }
12673
12674 //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
12675
12676 //$ret .= search_component_params
12677 //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
12678 //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
12679
12680 $show_search_component_params_hidden = 1;
12681 if ($show_search_component_params_hidden) {
12682 $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
12683 }
12684 $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%')) -->";
12685 $ret .= '<input type="hidden" id="search_component_params_hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
12686 // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
12687
12688 // TODO : Use $arrayoffiltercriterias instead of $arrayofcriterias
12689 // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
12690 foreach ($arrayofcriterias as $criteria) {
12691 foreach ($criteria as $criteriafamilykey => $criteriafamilyval) {
12692 if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
12693 continue;
12694 }
12695 if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
12696 continue;
12697 }
12698 if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
12699 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
12700 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
12701 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
12702 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
12703 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
12704 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
12705 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
12706 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
12707 } else {
12708 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
12709 }
12710 }
12711 }
12712
12713 $ret .= '</div>';
12714
12715 $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";
12716 $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" id="search_component_params_input" name="search_component_params_input" class="noborderall search_component_input" value="">';
12717
12718 $ret .= '</div>';
12719 $ret .= '</div>';
12720
12721 $ret .= '<script>
12722 jQuery(".tagsearchdelete").click(function(e) {
12723 var filterid = $(this).parents().attr("data-ufilterid");
12724 console.log("We click to delete the criteria nb "+filterid);
12725
12726 // Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
12727 var newparamstring = \'\';
12728 $(\'.tagsearch\').each(function(index, element) {
12729 tmpfilterid = $(this).attr("data-ufilterid");
12730 if (tmpfilterid != filterid) {
12731 // We keep this criteria
12732 if (newparamstring == \'\') {
12733 newparamstring = $(this).attr("data-ufilter");
12734 } else {
12735 newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
12736 }
12737 }
12738 });
12739 console.log("newparamstring = "+newparamstring);
12740
12741 jQuery("#search_component_params_hidden").val(newparamstring);
12742
12743 // We repost the form
12744 $(this).closest(\'form\').submit();
12745 });
12746
12747 jQuery("#search_component_params_input").keydown(function(e) {
12748 console.log("We press a key on the filter field that is "+jQuery("#search_component_params_input").val());
12749 console.log(e.which);
12750 if (jQuery("#search_component_params_input").val() == "" && e.which == 8) {
12751 /* We click on back when the input field is already empty */
12752 event.preventDefault();
12753 jQuery("#divsearch_component_params .tagsearch").last().remove();
12754 /* Regenerate content of search_component_params_hidden from remaining .tagsearch */
12755 var s = "";
12756 jQuery("#divsearch_component_params .tagsearch").each(function( index ) {
12757 if (s != "") {
12758 s = s + " AND ";
12759 }
12760 s = s + $(this).attr("data-ufilter");
12761 });
12762 console.log("New value for search_component_params_hidden = "+s);
12763 jQuery("#search_component_params_hidden").val(s);
12764 }
12765 });
12766
12767 </script>
12768 ';
12769
12770 // Convert $arrayoffiltercriterias into a json object that can be used in jquery to build the search component dynamically
12771 $arrayoffiltercriterias_json = json_encode($arrayoffiltercriterias);
12772 $ret .= '<script>
12773 var arrayoffiltercriterias = ' . $arrayoffiltercriterias_json . ';
12774 </script>';
12775
12776
12777 $arrayoffilterfieldslabel = array();
12778 foreach ($arrayoffiltercriterias as $key => $val) {
12779 $arrayoffilterfieldslabel[$key]['label'] = $val['label'];
12780 $arrayoffilterfieldslabel[$key]['data-type'] = $val['type'];
12781 }
12782
12783 // Adding the div for search assistance
12784 $ret .= '<div class="search-component-assistance">';
12785 $ret .= '<div>';
12786
12787 $ret .= '<p class="assistance-title">' . img_picto('', 'filter') . ' ' . $langs->trans('FilterAssistance') . ' </p>';
12788
12789 $ret .= '<p class="assistance-errors error" style="display:none">' . $langs->trans('AllFieldsRequired') . ' </p>';
12790
12791 $ret .= '<div class="operand">';
12792 $ret .= $form->selectarray('search_filter_field', $arrayoffilterfieldslabel, '', $langs->trans("Fields"), 0, 0, '', 0, 0, 0, '', 'width200 combolargeelem', 1);
12793 $ret .= '</div>';
12794
12795 $ret .= '<span class="separator"></span>';
12796
12797 // Operator selector (will be populated dynamically)
12798 $ret .= '<div class="operator">';
12799 $ret .= '<select class="operator-selector width150" id="operator-selector"">';
12800 $ret .= '</select>';
12801 $ret .= '<script>$(document).ready(function() {';
12802 $ret .= ' $(".operator-selector").select2({';
12803 $ret .= ' placeholder: \'' . dol_escape_js($langs->transnoentitiesnoconv('Operator')) . '\'';
12804 $ret .= ' });';
12805 $ret .= '});</script>';
12806 $ret .= '</div>';
12807
12808 $ret .= '<span class="separator"></span>';
12809
12810 $ret .= '<div class="value">';
12811 // Input field for entering values
12812 $ret .= '<input type="text" class="flat width100 value-input" placeholder="' . dolPrintHTML($langs->trans('Value')) . '">';
12813
12814 // Date selector
12815 $dateOne = '';
12816 $ret .= '<span class="date-one" style="display:none">';
12817 $ret .= $form->selectDate(($dateOne ? $dateOne : -1), 'dateone', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '');
12818 $ret .= '</span>';
12819
12820 // Value selector (will be populated dynamically) based on search_filter_field value if a selected value has an array of values
12821 $ret .= '<select class="value-selector width150" id="value-selector" style="display:none">';
12822 $ret .= '</select>';
12823 $ret .= '<script>
12824 $(document).ready(function() {
12825 $("#value-selector").select2({
12826 placeholder: "' . dol_escape_js($langs->trans('Value')) . '"
12827 });
12828 $("#value-selector").hide();
12829 $("#value-selector").next(".select2-container").hide();
12830 });
12831 </script>';
12832
12833 $ret .= '</div>';
12834
12835 $ret .= '<div class="btn-div">';
12836 $ret .= '<button class="button buttongen button-save add-filter-btn" type="button">' . $langs->trans("addToFilter") . '</button>';
12837 $ret .= '</div>';
12838
12839 $ret .= '</div>';
12840 //$ret .= '</tbody></table>';
12841
12842 // End of the assistance div
12843 $ret .= '</div>';
12844
12845 // Script jQuery to show/hide the floating assistance
12846 $ret .= '<script>
12847 $(document).ready(function() {
12848 $("#search_component_params_input").on("click", function() {
12849 const inputPosition = $(this).offset();
12850 const inputHeight = $(this).outerHeight();
12851 $(".search-component-assistance").css({
12852 top: inputPosition.top + inputHeight + 5 + "px",
12853 left: $("#divsearch_component_params").position().left
12854 }).slideToggle(200);
12855 });
12856 $(document).on("click", function(e) {
12857 if (!$(e.target).closest("#search_component_params_input, .search-component-assistance, #ui-datepicker-div").length) {
12858 $(".search-component-assistance").hide();
12859 }
12860 });
12861 });
12862 </script>';
12863
12864 $ret .= '<script>
12865 $(document).ready(function() {
12866 $(".search_filter_field").on("change", function() {
12867 console.log("We change search_filter_field");
12868
12869 let maybenull = 0;
12870 const selectedField = $(this).find(":selected");
12871 let fieldType = selectedField.data("type");
12872 const selectedFieldValue = selectedField.val();
12873
12874 // If the selected field has an array of values then ask toshow the value selector instead of the value input
12875 if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12876 fieldType = "select";
12877 }
12878
12879 // If the selected field may be null then ask to append the "IsDefined" and "IsNotDefined" operators
12880 if (arrayoffiltercriterias[selectedFieldValue]["maybenull"] !== undefined) {
12881 maybenull = 1;
12882 }
12883 const operators = getOperatorsForFieldType(fieldType, maybenull);
12884 const operatorSelector = $(".operator-selector");
12885
12886 // Clear existing options
12887 operatorSelector.empty();
12888
12889 // Populate operators
12890 Object.entries(operators).forEach(function([operator, label]) {
12891 operatorSelector.append("<option value=\'" + operator + "\'>" + label + "</option>");
12892 });
12893
12894 operatorSelector.trigger("change.select2");
12895
12896 // Clear and hide all input elements initially
12897 $(".value-input, .dateone, .datemonth, .dateyear").val("").hide();
12898 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12899 $("#dateone").datepicker("setDate", null);
12900 $(".date-one, .date-month, .date-year").hide();
12901 $("#value-selector").val("").hide();
12902 $("#value-selector").next(".select2-container").hide();
12903 $("#value-selector").val(null).trigger("change.select2");
12904
12905 if (fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp") {
12906 $(".date-one").show();
12907 } else if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12908 var arrayofkeyval = arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"];
12909 var valueSelector = $("#value-selector");
12910 valueSelector.empty();
12911 Object.entries(arrayofkeyval).forEach(function([key, val]) {
12912 valueSelector.append("<option value=\'" + key + "\'>" + val + "</option>");
12913 });
12914 valueSelector.trigger("change.select2");
12915
12916 $("#value-selector").show();
12917 $("#value-selector").next(".select2-container").show();
12918 } else {
12919 $(".value-input").show();
12920 }
12921 });
12922
12923 $("#operator-selector").on("change", function() {
12924 console.log("We change operator-selector");
12925
12926 const selectedOperator = $(this).find(":selected").val();
12927 if (selectedOperator === "IsDefined" || selectedOperator === "IsNotDefined") {
12928 // Disable all value input elements
12929 $(".value-input, .dateone, .datemonth, .dateyear").val("").prop("disabled", true);
12930 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12931 $("#dateone").datepicker("setDate", null).datepicker("option", "disabled", true);
12932 $(".date-one, .date-month, .date-year").prop("disabled", true);
12933 $("#value-selector").val("").prop("disabled", true);
12934 $("#value-selector").val(null).trigger("change.select2");
12935 } else {
12936 // Enable all value input elements
12937 $(".value-input, .dateone, .datemonth, .dateyear").prop("disabled", false);
12938 $(".date-one, .date-month, .date-year").prop("disabled", false);
12939 $("#dateone").datepicker("option", "disabled", false);
12940 $("#value-selector").prop("disabled", false);
12941 }
12942 });
12943
12944 $(".add-filter-btn").on("click", function(event) {
12945 console.log("We click on add-filter-btn");
12946
12947 event.preventDefault();
12948
12949 const field = $(".search_filter_field").val();
12950 const operator = $(".operator-selector").val();
12951 let value = $(".value-input").val();
12952 const fieldType = $(".search_filter_field").find(":selected").data("type");
12953
12954 if (["date", "datetime", "timestamp"].includes(fieldType)) {
12955 const year = $("#dateoneyear").val().toString().padStart(4, "0");;
12956 const month = $("#dateonemonth").val().toString().padStart(2, "0");
12957 const day = $("#dateoneday").val().toString().padStart(2, "0");
12958 value = `${year}-${month}-${day}`;
12959 console.log("value="+value);
12960 }
12961
12962 // If the selected field has an array of values then take the selected value
12963 if (arrayoffiltercriterias[field]["arrayofkeyval"] !== undefined) {
12964 value = $("#value-selector").val();
12965 }
12966
12967 // If the operator is "IsDefined" or "IsNotDefined" then set the value to 1 (it will not be used)
12968 if (operator === "IsDefined" || operator === "IsNotDefined") {
12969 value = "1";
12970 }
12971
12972 const filterString = generateFilterString(field, operator, value, fieldType);
12973
12974 // Submit the form
12975 if (filterString !== "" && field !== "" && operator !== "" && value !== "") {
12976 $("#search_component_params_input").val($("#search_component_params_input").val() + " " + filterString);
12977 $("#search_component_params_input").closest("form").submit();
12978 } else {
12979 $(".assistance-errors").show();
12980 }
12981 });
12982 });
12983 </script>';
12984
12985 return $ret;
12986 }
12987
12999 public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0, $selected = 0, $morecss = '')
13000 {
13001 global $langs, $user;
13002
13003 $retstring = '';
13004
13005 $TModels = array();
13006
13007 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
13008 $formmail = new FormMail($this->db);
13009 $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
13010
13011 if ($default) {
13012 $TModels[0] = $langs->trans('DefaultMailModel');
13013 }
13014 if ($result > 0) {
13015 foreach ($formmail->lines_model as $model) {
13016 $TModels[(int) $model->id] = $model->label;
13017 }
13018 }
13019
13020 $retstring .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
13021
13022 foreach ($TModels as $id_model => $label_model) {
13023 $retstring .= '<option value="' . $id_model . '"';
13024 if (!empty($selected) && ((int) $selected) == $id_model) {
13025 $retstring .= "selected";
13026 }
13027 $retstring .= ">" . $label_model . "</option>";
13028 }
13029
13030 $retstring .= "</select>";
13031
13032 if ($addjscombo) {
13033 $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
13034 }
13035
13036 return $retstring;
13037 }
13038
13050 public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = false, $morecss = '', $dol_openinpopup = '')
13051 {
13052 global $langs;
13053
13054 $buttons = array();
13055
13056 $save = array(
13057 'name' => 'save',
13058 'label_key' => $save_label,
13059 );
13060
13061 if ($save_label == 'Create' || $save_label == 'Add') {
13062 $save['name'] = 'add';
13063 } elseif ($save_label == 'Modify') {
13064 $save['name'] = 'edit';
13065 }
13066
13067 $cancel = array(
13068 'name' => 'cancel',
13069 'label_key' => 'Cancel',
13070 );
13071
13072 // If MAIN_BUTTON_POSITION_FIRST_OR_LEFT not set, default is to have main action first, then complementary, then cancel at end
13073 if (!getDolGlobalInt('MAIN_BUTTON_POSITION_FIRST_OR_LEFT')) {
13074 !empty($save_label) ? $buttons[] = $save : '';
13075 if (!empty($morebuttons)) {
13076 $buttons[] = $morebuttons;
13077 }
13078 !empty($cancel_label) ? $buttons[] = $cancel : '';
13079 } else {
13080 if (!empty($morebuttons)) {
13081 $buttons[] = $morebuttons;
13082 }
13083 !empty($cancel_label) ? $buttons[] = $cancel : '';
13084 !empty($save_label) ? $buttons[] = $save : '';
13085 }
13086
13087 $retstring = $withoutdiv ? '' : '<div class="center">';
13088
13089 foreach ($buttons as $button) {
13090 $addclass = empty($button['addclass']) ? '' : $button['addclass'];
13091 $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'])) . '">';
13092 }
13093 $retstring .= $withoutdiv ? '' : '</div>';
13094
13095 if ($dol_openinpopup) {
13096 $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . dol_escape_htmltag($dol_openinpopup) . ' context, so we enable the close of dialog on cancel -->' . "\n";
13097 $retstring .= '<script nonce="' . getNonce() . '">';
13098 $retstring .= 'jQuery(".button-cancel").click(function(e) {
13099 e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . dol_escape_js($dol_openinpopup) . '\');
13100 window.parent.jQuery(\'#idfordialog' . dol_escape_js($dol_openinpopup) . '\').dialog(\'close\');
13101 });';
13102 $retstring .= '</script>';
13103 }
13104
13105 return $retstring;
13106 }
13107
13108
13109 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
13110
13117 {
13118 // phpcs:enable
13119 global $langs;
13120
13121 $num = count($this->cache_invoice_subtype);
13122 if ($num > 0) {
13123 return 0; // Cache already loaded
13124 }
13125
13126 dol_syslog(__METHOD__, LOG_DEBUG);
13127
13128 $sql = "SELECT rowid, code, label as label";
13129 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
13130 $sql .= " WHERE active = 1";
13131
13132 $resql = $this->db->query($sql);
13133 if ($resql) {
13134 $num = $this->db->num_rows($resql);
13135 $i = 0;
13136 while ($i < $num) {
13137 $obj = $this->db->fetch_object($resql);
13138
13139 // If translation exists, we use it, otherwise we take the default wording
13140 $label = ($langs->trans("InvoiceSubtype" . $obj->rowid) != "InvoiceSubtype" . $obj->rowid) ? $langs->trans("InvoiceSubtype" . $obj->rowid) : (($obj->label != '-') ? $obj->label : '');
13141 $this->cache_invoice_subtype[$obj->rowid]['rowid'] = $obj->rowid;
13142 $this->cache_invoice_subtype[$obj->rowid]['code'] = $obj->code;
13143 $this->cache_invoice_subtype[$obj->rowid]['label'] = $label;
13144 $i++;
13145 }
13146
13147 $this->cache_invoice_subtype = dol_sort_array($this->cache_invoice_subtype, 'code', 'asc', 0, 0, 1);
13148
13149 return $num;
13150 } else {
13151 dol_print_error($this->db);
13152 return -1;
13153 }
13154 }
13155
13156
13167 public function getSelectInvoiceSubtype($selected = 0, $htmlname = 'subtypeid', $addempty = 0, $noinfoadmin = 0, $morecss = '')
13168 {
13169 global $langs, $user;
13170
13171 $out = '';
13172 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
13173
13174 $this->load_cache_invoice_subtype();
13175
13176 $out .= '<select id="' . $htmlname . '" class="flat selectsubtype' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
13177 if ($addempty) {
13178 $out .= '<option value="0">&nbsp;</option>';
13179 }
13180
13181 foreach ($this->cache_invoice_subtype as $rowid => $subtype) {
13182 $label = $subtype['label'];
13183 $out .= '<option value="' . $subtype['rowid'] . '"';
13184 if ($selected == $subtype['rowid']) {
13185 $out .= ' selected="selected"';
13186 }
13187 $out .= '>';
13188 $out .= $label;
13189 $out .= '</option>';
13190 }
13191
13192 $out .= '</select>';
13193 if ($user->admin && empty($noinfoadmin)) {
13194 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
13195 }
13196 $out .= ajax_combobox($htmlname);
13197
13198 return $out;
13199 }
13200
13210 public function getSearchFilterToolInput($dataTarget, $htmlName = 'search-tools-input', $value = '', $params = [])
13211 {
13212 global $langs;
13213
13214 $attr = array(
13215 'type' => 'search',
13216 'name' => $htmlName,
13217 'value' => $value,
13218 'class' => "search-tool-input",
13219 'placeholder' => $langs->trans('Search'),
13220 'autocomplete' => 'off'
13221 );
13222
13223 // Optional data attr
13224 // 'autofocus' : will set auto focus on field ,
13225 // data-counter-target : will get count results
13226 // data-no-item-target : will be display if count results is 0
13227
13228 if ($dataTarget !== false) {
13229 $attr['data-search-tool-target'] = $dataTarget;
13230 }
13231
13232 // Override attr
13233 if (!empty($params['attr']) && is_array($params['attr'])) {
13234 foreach ($params['attr'] as $key => $value) {
13235 if ($key == 'class') {
13236 $attr['class'] .= ' '.$value;
13237 } elseif ($key == 'classOverride') {
13238 $attr['class'] = $value;
13239 } else {
13240 $attr[$key] = $value;
13241 }
13242 }
13243 }
13244
13245 // automatic add tooltip when title is detected
13246 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13247 $attr['class'] .= ' classfortooltip';
13248 }
13249
13250 $TCompiledAttr = [];
13251 foreach ($attr as $key => $value) {
13252 if (in_array($key, ['data-target'])
13253 || (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr']))) { // Not recommended
13254 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13255 } else {
13256 $value = dolPrintHTMLForAttribute($value);
13257 }
13258
13259 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13260 }
13261
13262 $compiledAttributes = implode(' ', $TCompiledAttr);
13263
13264
13265 return '<div class="search-tool-container"><input '.$compiledAttributes.'></div>';
13266 }
13267
13281 public function inputType($type, $name, $value = '', $id = '', $morecss = '', $moreparam = '', $label = '', $addInputLabel = '')
13282 {
13283 $out = '';
13284 if ($label != '') {
13285 $out .= '<label for="' . dolPrintHTMLForAttribute($id) . '">';
13286 }
13287 $out .= '<input type="' . dolPrintHTMLForAttribute($type) . '"';
13288 $out .= ' class="flat valignmiddle maxwidthonsmartphone ' . dolPrintHTMLForAttribute($morecss) . '"';
13289 if ($id != '') {
13290 $out .= ' id="' . dolPrintHTMLForAttribute($id) . '"';
13291 }
13292 $out .= ' name="' . dolPrintHTMLForAttribute($name) . '"';
13293 $out .= ' value="' . dolPrintHTMLForAttribute($value) . '" ';
13294 $out .= ($moreparam ? ' ' . $moreparam : '');
13295 $out .= ' />' . $addInputLabel;
13296 if ($label != '') {
13297 $out .= $label . '</label>';
13298 }
13299
13300 return $out;
13301 }
13302
13315 public function inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData = [], $morecss = 'minwidth75', $moreparam = '')
13316 {
13317 $out = "
13318 <script>
13319 $(document).ready(function () {
13320 $('#" . $htmlName . "').select2({
13321 ajax: {
13322 url: '" . $ajaxUrl . "',
13323 dataType: 'json',
13324 delay: 250, // wait 250 milliseconds before triggering the request
13325 data: function (params) {
13326 var query = {
13327 search: params.term,
13328 page: params.page || 1";
13329 if (!empty($ajaxData) && is_array($ajaxData)) {
13330 foreach ($ajaxData as $key => $value) {
13331 $out .= ", " . $key . ": '" . $value . "'";
13332 }
13333 }
13334 $out .= "
13335 }
13336 return query;
13337 }
13338 }
13339 })
13340 });
13341 </script>";
13342
13343 $out .= $this->selectarray($htmlName, $array, $id, 0, 0, 0, $moreparam, 0, 0, 0, '', $morecss);
13344
13345 return $out;
13346 }
13347
13357 public function inputHtml($htmlName, $value, $morecss = '', $moreparam = '')
13358 {
13359 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13360 $doleditor = new DolEditor($htmlName, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
13361
13362 return (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13363 }
13364
13375 public function inputText($htmlName, $value, $morecss = '', $moreparam = '', $options = array())
13376 {
13377 global $langs;
13378
13379 $out = '';
13380 if (!empty($options)) {
13381 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
13382 $out .= $this->selectarray($htmlName . "_multiinput", $options, '', 1, 0, 0, $moreparam, 0, 0, 0, '', "flat maxwidthonphone" . $morecss);
13383 $out .= '<input id="' . $htmlName . '_multiinputadd" type="button" class="button" value="' . $langs->trans("Add") . '">';
13384 $out .= "<script>";
13385 $out .= '
13386 function handlemultiinputdisabling(htmlname){
13387 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
13388 multiinput = $("#"+htmlname+"_multiinput");
13389 multiinput.find("option").each(function(){
13390 tmpval = $("#"+htmlname).val();
13391 tmpvalarray = tmpval.split("\n");
13392 valtotest = $(this).val();
13393 if(tmpvalarray.includes(valtotest)){
13394 $(this).prop("disabled",true);
13395 } else {
13396 if($(this).prop("disabled") == true){
13397 console.log(valtotest)
13398 $(this).prop("disabled", false);
13399 }
13400 }
13401 });
13402 }
13403
13404 $(document).ready(function () {
13405 $("#' . $htmlName . '_multiinputadd").on("click",function() {
13406 tmpval = $("#' . $htmlName . '").val();
13407 tmpvalarray = tmpval.split(",");
13408 valtotest = $("#' . $htmlName . '_multiinput").val();
13409 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
13410 console.log("We add the selected value to the text area ' . $htmlName . '");
13411 if(tmpval == ""){
13412 tmpval = valtotest;
13413 } else {
13414 tmpval = tmpval + "\n" + valtotest;
13415 }
13416 $("#' . $htmlName . '").val(tmpval);
13417 handlemultiinputdisabling("' . $htmlName . '");
13418 $("#' . $htmlName . '_multiinput").val(-1);
13419 } else {
13420 console.log("We add nothing the text area ' . $htmlName . '");
13421 }
13422 });
13423 $("#' . $htmlName . '").on("change",function(){
13424 handlemultiinputdisabling("' . $htmlName . '");
13425 });
13426 handlemultiinputdisabling("' . $htmlName . '");
13427 })';
13428 $out .= "</script>";
13429 $value = str_replace(',', "\n", $value);
13430 }
13431
13432 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13433 $doleditor = new DolEditor($htmlName, (string) $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
13434 $out .= (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13435
13436 return $out;
13437 }
13438
13449 public function inputRadio($htmlName, $options, $selectedValue, $morecss = '', $moreparam = '')
13450 {
13451 $out = '';
13452 foreach ($options as $optionKey => $optionLabel) {
13453 $selected = ((string) $selectedValue) === ((string) $optionKey) ? ' checked="checked"' : '';
13454 $optionId = $htmlName . '_' . $optionKey;
13455 $out .= '<input class="flat' . $morecss . '" type="radio" name="' . $htmlName . '" id="' . $optionId . '" value="' . dolPrintHTMLForAttribute((string) $optionKey) . '"' . $selected . $moreparam . '/><label for="' . $optionId . '">' . $optionLabel . '</label><br>';
13456 }
13457
13458 return $out;
13459 }
13460
13471 public function inputStars($htmlName, $size, $value, $morecss = '', $moreparam = '')
13472 {
13473 $out = '<input type="hidden" class="flat ' . $morecss . '" name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '"' . $moreparam . '>';
13474 $out .= '<div class="star-selection" id="' . $htmlName . '_selection">';
13475 for ($i = 1; $i <= $size; $i++) {
13476 $out .= '<span class="star" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13477 }
13478 $out .= '</div>';
13479 $out .= '<script>
13480 jQuery(function($) { /* commonobject.class.php 1 */
13481 let container = $("#' . $htmlName . '_selection");
13482 let selectedStars = parseInt($("#' . $htmlName . '").val()) || 0;
13483 container.find(".star").each(function() {
13484 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13485 });
13486 container.find(".star").on("mouseover", function() {
13487 let selectedStar = $(this).data("value");
13488 container.find(".star").each(function() {
13489 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
13490 });
13491 });
13492 container.on("mouseout", function() {
13493 container.find(".star").each(function() {
13494 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13495 });
13496 });
13497 container.find(".star").off("click").on("click", function() {
13498 selectedStars = $(this).data("value");
13499 if (selectedStars === 1 && $("#' . $htmlName . '").val() == 1) {
13500 selectedStars = 0;
13501 }
13502 $("#' . $htmlName . '").val(selectedStars);
13503 container.find(".star").each(function() {
13504 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13505 });
13506 });
13507 });
13508 </script>';
13509
13510 return $out;
13511 }
13512
13522 public function inputIcon($htmlName, $value, $morecss = '', $moreparam = '')
13523 {
13524 global $langs;
13525
13526 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
13527 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
13528 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
13529 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
13530 */
13531 $out = '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat ' . $morecss . ' maxwidthonsmartphone"';
13532 $out .= ' name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '" ' . ((string) $moreparam) . '>';
13533 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
13534 $out .= '<script>';
13535 $options = "{ title: '<b>" . $langs->trans("IconFieldSelector") . "</b>', placement: 'right', showFooter: false, templates: {";
13536 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
13537 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
13538 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
13539 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
13540 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
13541 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"" . $langs->trans("TypeToFilter") . "\" />',";
13542 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
13543 $options .= " <div class=\"arrow\" ></div>";
13544 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
13545 $options .= " <div class=\"popover-content \" ></div>";
13546 $options .= "</div>'}}";
13547 $out .= "$('#" . $htmlName . "').iconpicker(" . $options . ");";
13548 $out .= '</script>';
13549 }
13550
13551 return $out;
13552 }
13553
13562 public function inputGeoPoint($htmlName, $value, $type = '')
13563 {
13564 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13565 require_once DOL_DOCUMENT_ROOT . '/core/class/geomapeditor.class.php';
13566 $dolgeophp = new DolGeoPHP($this->db);
13567 $geomapeditor = new GeoMapEditor();
13568
13569 $geojson = '{}';
13570 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
13571 if (!empty($value)) {
13572 $tmparray = $dolgeophp->parseGeoString($value);
13573 $geojson = $tmparray['geojson'];
13574 $centroidjson = $tmparray['centroidjson'];
13575 }
13576
13577 return $geomapeditor->getHtml($htmlName, $geojson, $centroidjson, $type);
13578 }
13579
13586 public function outputMultiValues($values)
13587 {
13588 $out = '';
13589 $toPrint = array();
13590 $values = is_array($values) ? $values : array();
13591
13592 foreach ($values as $value) {
13593 $toPrint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $value . '</li>';
13594 }
13595 if (!empty($toPrint)) {
13596 $out = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toPrint) . '</ul></div>';
13597 }
13598
13599 return $out;
13600 }
13601
13609 public function outputStars($size, $value)
13610 {
13611 $out = '<div class="star-selection" data-value="' . dolPrintHTMLForAttribute((string) $value) . '">';
13612 for ($i = 1; $i <= $size; $i++) {
13613 $out .= '<span class="star' . ($i <= $value ? ' active' : '') . '" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13614 }
13615 $out .= '</div>';
13616
13617 return $out;
13618 }
13619
13626 public function outputIcon($value)
13627 {
13628 $out = '<span class="' . dolPrintHTMLForAttribute((string) $value) . '"></span>';
13629
13630 return $out;
13631 }
13632
13640 public function outputGeoPoint($value, $type)
13641 {
13642 $out = '';
13643
13644 if (!empty($value)) {
13645 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13646 $dolgeophp = new DolGeoPHP($this->db);
13647 if ($type == 'point') {
13648 $out = $dolgeophp->getXYString($value);
13649 } else { // multipts, linestrg, polygon
13650 $out = $dolgeophp->getPointString($value);
13651 }
13652 }
13653
13654 return $out;
13655 }
13656
13671 public function getNomUrl(&$object, $withpicto = 0, $option = '', $maxlength = 0, $save_lastsearch_value = -1, $notooltip = 0, $morecss = '', $add_label = 0, $sep = ' - ')
13672 {
13673 if (is_object($object) && method_exists($object, 'getNomUrl')) {
13674 $out = $object->getNomUrl($withpicto, $option, $maxlength, $save_lastsearch_value, $notooltip, $morecss, $add_label, $sep);
13675 return $out;
13676 } else {
13677 return '';
13678 }
13679 }
13680}
$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:596
$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.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
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, ....
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