dolibarr 24.0.2
html.form.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
7 * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9 * Copyright (C) 2006 Marc Barilley/Ocebo <marc@ocebo.com>
10 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerker@telenet.be>
11 * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
12 * Copyright (C) 2010 Juanjo Menent <jmenent@2byte.es>
13 * Copyright (C) 2010-2021 Philippe Grand <philippe.grand@atoo-net.com>
14 * Copyright (C) 2011 Herve Prot <herve.prot@symeos.com>
15 * Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
16 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
17 * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
18 * Copyright (C) 2014-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
19 * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
20 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
21 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
22 * Copyright (C) 2018 Christophe Battarel <christophe@altairis.fr>
23 * Copyright (C) 2018 Josep Lluis Amador <joseplluis@lliuretic.cat>
24 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
25 * Copyright (C) 2023 Nick Fragoulis
26 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
27 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
28 * Copyright (C) 2026 Lenin Rivas <lenin.rivas777@gmail.com>
29 * Copyright (C) 2026 Open-Dsi <support@open-dsi.fr>
30 * Copyright (C) 2026 Jose MARTINEZ <jose.martinez@pichinov.com>
31 *
32 * This program is free software; you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation; either version 3 of the License, or
35 * (at your option) any later version.
36 *
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 *
42 * You should have received a copy of the GNU General Public License
43 * along with this program. If not, see <https://www.gnu.org/licenses/>.
44 */
45
59class Form
60{
64 public $db;
65
69 public $error = '';
70
74 public $errors = array();
75
76 // Some properties used to return data by some methods
78 public $result;
79
81 public $num;
82
83 // Cache arrays
85 public $cache_types_paiements = array();
87 public $cache_conditions_paiements = array();
89 public $cache_transport_mode = array();
91 public $cache_availability = array();
93 public $cache_demand_reason = array();
95 public $cache_types_fees = array();
97 public $cache_vatrates = array();
99 public $cache_invoice_subtype = array();
101 public $cache_rule_for_lines_dates = array();
102
106 private $phoneInputSharedJsLoaded = false;
107
108
114 public function __construct($db)
115 {
116 $this->db = $db;
117 }
118
127 public function getDurationTypes(Translate $langs, $plurial = true, $reverse = false)
128 {
129 if ($plurial) {
130 $arrayoftypes = [
131 'y' => $langs->trans('Years'),
132 'm' => $langs->trans('Month'),
133 'w' => $langs->trans('Weeks'),
134 'd' => $langs->trans('Days'),
135 'h' => $langs->trans('Hours'),
136 'i' => $langs->trans('Minutes'),
137 's' => $langs->trans('Seconds'),
138 ];
139 } else {
140 $arrayoftypes = [
141 "y" => $langs->trans("Year"),
142 "m" => $langs->trans("Month"),
143 "w" => $langs->trans("Week"),
144 "d" => $langs->trans("Day"),
145 "h" => $langs->trans("Hour"),
146 "i" => $langs->trans("Minute"),
147 's' => $langs->trans('Second'),
148 ];
149 }
150 if ($reverse) {
151 return array_reverse($arrayoftypes);
152 } else {
153 return $arrayoftypes;
154 }
155 }
156
173 public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
174 {
175 global $langs;
176
177 $ret = '';
178
179 // TODO change for compatibility
180 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;/', $typeofdata)) {
181 if ($perm) {
182 $tmp = explode(':', $typeofdata);
183 $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
184 if ($fieldrequired) {
185 $ret .= '<span class="fieldrequired">';
186 }
187 if ($help) {
188 $ret .= $this->textwithpicto($langs->trans($text), $help);
189 } else {
190 $ret .= $langs->trans($text);
191 }
192 if ($fieldrequired) {
193 $ret .= '</span>';
194 }
195 $ret .= '</div>' . "\n";
196 } else {
197 if ($fieldrequired) {
198 $ret .= '<span class="fieldrequired">';
199 }
200 if ($help) {
201 $ret .= $this->textwithpicto($langs->trans($text), $help);
202 } else {
203 $ret .= $langs->trans($text);
204 }
205 if ($fieldrequired) {
206 $ret .= '</span>';
207 }
208 }
209 } else {
210 if (empty($notabletag) && $perm) {
211 $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
212 }
213 if ($fieldrequired) {
214 $ret .= '<span class="fieldrequired">';
215 }
216 if ($help) {
217 $ret .= $this->textwithpicto($langs->trans($text), $help);
218 } else {
219 $ret .= $langs->trans($text);
220 }
221 if ($fieldrequired) {
222 $ret .= '</span>';
223 }
224 if (!empty($notabletag)) {
225 $ret .= ' ';
226 }
227 if (empty($notabletag) && $perm) {
228 $ret .= '</td>';
229 }
230 if (empty($notabletag) && $perm) {
231 $ret .= '<td class="right">';
232 }
233 if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm && is_object($object)) {
234 $ret .= '<a class="editfielda reposition" href="' . dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'edit' . $htmlname, $paramid => $object->id], true) . $moreparam . '">';
235 $ret .= img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1));
236 $ret .= '</a>';
237 }
238 if (!empty($notabletag) && $notabletag == 1) {
239 if ($text) {
240 $ret .= ' : ';
241 } else {
242 $ret .= ' ';
243 }
244 }
245 if (!empty($notabletag) && $notabletag == 3) {
246 $ret .= ' ';
247 }
248 if (empty($notabletag) && $perm) {
249 $ret .= '</td>';
250 }
251 if (empty($notabletag) && $perm) {
252 $ret .= '</tr></table>';
253 }
254 }
255
256 return $ret;
257 }
258
282 public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '')
283 {
284 global $conf, $langs;
285
286 $ret = '';
287
288 // Check parameters
289 if (empty($typeofdata)) {
290 return 'ErrorBadParameter typeofdata is empty';
291 }
292 // Clean parameter $typeofdata
293 if ($typeofdata == 'datetime') {
294 $typeofdata = 'dayhour';
295 }
296 if ($typeofdata == 'date') {
297 $typeofdata = 'day';
298 }
299 $reg = array();
300 if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
301 if ($reg[1] == 'varchar') {
302 $typeofdata = 'string';
303 } elseif ($reg[1] == 'int') {
304 $typeofdata = 'numeric';
305 } else {
306 return 'ErrorBadParameter ' . $typeofdata;
307 }
308 }
309
310 // When option to edit inline is activated
311 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
312 $ret .= $this->editInPlace($object, $value, $htmlname, ($perm ? 1 : 0), $typeofdata, $editvalue, $extObject, $custommsg);
313 } else {
314 if ($editaction == '') {
315 $editaction = GETPOST('action', 'aZ09');
316 }
317 $editmode = ($editaction == 'edit' . $htmlname);
318 if ($editmode) { // edit mode
319 $ret .= "<!-- formeditfieldval -->\n";
320 $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
321 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
322 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
323 $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
324 if (empty($notabletag)) {
325 $ret .= '<table class="nobordernopadding centpercent">';
326 }
327 if (empty($notabletag)) {
328 $ret .= '<tr><td>';
329 }
330 if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
331 $tmp = explode(':', $typeofdata);
332 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus spellcheck="false">';
333 } elseif (preg_match('/^(integer)/', $typeofdata)) {
334 $tmp = explode(':', $typeofdata);
335 $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
336 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
337 } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
338 $tmp = explode(':', $typeofdata);
339 $valuetoshow = price2num($editvalue ? $editvalue : $value);
340 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
341 } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
342 $tmp = explode(':', $typeofdata);
343 $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
344 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
345 $tmp = explode(':', $typeofdata);
346 $cols = (empty($tmp[2]) ? '' : $tmp[2]);
347 $morealt = '';
348 if (preg_match('/%/', $cols)) {
349 $morealt = ' style="width: ' . $cols . '"';
350 $cols = '';
351 }
352 $valuetoshow = ($editvalue ? $editvalue : $value);
353 $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
354 // textarea convert automatically entities chars into simple chars.
355 // So we convert & into &amp; so a string like 'a &lt; <b>b</b><br>é<br>&lt;script&gt;alert('X');&lt;script&gt;' stay a correct html and is not converted by textarea component when wysiwyg is off.
356 $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
357 $ret .= dol_htmlwithnojs(dol_string_neverthesehtmltags($valuetoshow, array('textarea')));
358 $ret .= '</textarea><div class="clearboth"></div>';
359 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
360 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
361 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
362 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
363 $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
364 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
365 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
366 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
367 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
368 $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
369 } elseif (preg_match('/^select;/', $typeofdata)) {
370 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
371 $arraylist = array();
372 foreach ($arraydata as $val) {
373 $tmp = explode(':', $val);
374 $tmpkey = str_replace('|', ':', $tmp[0]);
375 $arraylist[$tmpkey] = $tmp[1];
376 }
377 $ret .= $this->selectarray($htmlname, $arraylist, $value);
378 } elseif (preg_match('/^link/', $typeofdata)) {
379 // TODO Not yet implemented. See code for extrafields
380 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
381 $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
382 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
383 $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), (empty($tmp[2]) ? '' : $tmp[2]), (empty($tmp[3]) ? 100 : (int) $tmp[3]), (empty($tmp[1]) ? 'dolibarr_notes' : $tmp[1]), 'In', (empty($tmp[5]) ? false : (bool) $tmp[5]), (isset($tmp[8]) ? ($tmp[8] ? true : false) : true), true, (empty($tmp[6]) ? 20 : (int) $tmp[6]), (empty($tmp[7]) ? '100' : $tmp[7]));
384 $ret .= $doleditor->Create(1);
385 } elseif ($typeofdata == 'asis') {
386 $ret .= ($editvalue ? $editvalue : $value);
387 }
388 if (empty($notabletag)) {
389 $ret .= '</td>';
390 }
391
392 // Button save-cancel
393 if (empty($notabletag)) {
394 $ret .= '<td>';
395 }
396 //else $ret.='<div class="clearboth"></div>';
397 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Save") . '">';
398 if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
399 $ret .= '<br>' . "\n";
400 }
401 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
402 if (empty($notabletag)) {
403 $ret .= '</td>';
404 }
405
406 if (empty($notabletag)) {
407 $ret .= '</tr></table>' . "\n";
408 }
409 $ret .= '</form>' . "\n";
410 } else { // view mode
411 if (preg_match('/^email/', $typeofdata)) {
412 $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
413 } elseif (preg_match('/^phone/', $typeofdata)) {
414 $ret .= dol_print_phone($value, '_blank', 32, 1);
415 } elseif (preg_match('/^url/', $typeofdata)) {
416 $ret .= dol_print_url($value, '_blank', 32, 1);
417 } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
418 $ret .= ($value != '' ? price($value, 0, $langs, 0, -1, -1, $conf->currency) : '');
419 } elseif (preg_match('/^checkbox/', $typeofdata)) {
420 $tmp = explode(':', $typeofdata);
421 $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
422 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
424 } elseif (preg_match('/^(safehtmlstring|restricthtml)/', $typeofdata)) { // 'restricthtml' is not an allowed type for editfieldval. Value is 'safehtmlstring'
426 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
427 $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
428 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
429 $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
430 } elseif (preg_match('/^select;/', $typeofdata)) {
431 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
432 $arraylist = array();
433 foreach ($arraydata as $val) {
434 $tmp = explode(':', $val);
435 $arraylist[$tmp[0]] = $tmp[1];
436 }
437 $ret .= $arraylist[$value];
438 if ($htmlname == 'fk_product_type') {
439 if ($value == 0) {
440 $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
441 } else {
442 $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
443 }
444 }
445 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
446 $tmpcontent = dol_htmlentitiesbr($value);
447 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
448 $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
449 $firstline = preg_replace('/[\n\r].*/', '', $firstline);
450 $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
451 }
452 // We don't use dol_escape_htmltag to get the html formatting active, but this need we must also
453 // clean data from some dangerous html
455 } else {
456 if (empty($moreoptions['valuealreadyhtmlescaped'])) {
457 $ret .= dol_escape_htmltag($value);
458 } else {
459 $ret .= $value; // $value must be already html escaped.
460 }
461 }
462
463 // Custom format if parameter $formatfunc has been provided
464 if ($formatfunc && method_exists($object, $formatfunc)) {
465 $ret = $object->$formatfunc($ret);
466 }
467 }
468 }
469 return $ret;
470 }
471
483 public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
484 {
485 global $conf, $langs, $extralanguages;
486
487 $result = '';
488
489 // List of extra languages
490 $arrayoflangcode = array();
491 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
492 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
493 }
494
495 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
496 if (!is_object($extralanguages)) {
497 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
498 $extralanguages = new ExtraLanguages($this->db);
499 }
500 $extralanguages->fetch_name_extralanguages('societe');
501
502 // ExtraLanguages::fetch_name_extralanguages() leaves $this->attributes empty
503 // when MAIN_USE_ALTERNATE_TRANSLATION_FOR is not configured, so PHP 8 raises
504 // 'Undefined array key' on the read below if we do not guard it (issue #34596).
505 if (empty($extralanguages->attributes[$object->element]) || !is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
506 return ''; // No extralang field to show
507 }
508
509 $result .= '<!-- Widget for translation -->' . "\n";
510 $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
511 $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', 0, 0, 0, '', 'fa-15 editfieldlang');
512 $result .= $s;
513 $result .= '</div>';
514
515 $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
516
517 $resultforextrlang = '';
518 foreach ($arrayoflangcode as $langcode) {
519 $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
520 if (empty($valuetoshow)) {
521 $object->fetchValuesForExtraLanguages();
522 //var_dump($object->array_languages);
523 $valuetoshow = $object->array_languages[$fieldname][$langcode];
524 }
525
526 $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
527 $resultforextrlang .= $s;
528
529 // TODO Use the showInputField() method of ExtraLanguages object
530 if ($typeofdata == 'textarea') {
531 $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
532 $resultforextrlang .= $valuetoshow;
533 $resultforextrlang .= '</textarea>';
534 } else {
535 $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
536 }
537 }
538 $result .= $resultforextrlang;
539
540 $result .= '</div>';
541 $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
542 }
543
544 return $result;
545 }
546
560 protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
561 {
562 $out = '';
563
564 // Check parameters
565 if (preg_match('/^text/', $inputType)) {
566 $value = dol_nl2br($value);
567 } elseif (preg_match('/^numeric/', $inputType)) {
568 $value = price($value);
569 } elseif ($inputType == 'day' || $inputType == 'datepicker') {
570 $value = dol_print_date($value, 'day');
571 }
572
573 if ($condition) {
574 $element = false;
575 $table_element = false;
576 $fk_element = false;
577 $loadmethod = false;
578 $savemethod = false;
579 $ext_element = false;
580 $button_only = false;
581 $inputOption = '';
582 $rows = '';
583 $cols = '';
584
585 if (is_object($object)) {
586 $element = $object->element;
587 $table_element = $object->table_element;
588 $fk_element = $object->id;
589 }
590
591 if (is_object($extObject)) {
592 $ext_element = $extObject->element;
593 }
594
595 if (preg_match('/^(string|email|numeric)/', $inputType)) {
596 $tmp = explode(':', $inputType);
597 $inputType = $tmp[0];
598 if (!empty($tmp[1])) {
599 $inputOption = $tmp[1];
600 }
601 if (!empty($tmp[2])) {
602 $savemethod = $tmp[2];
603 }
604 $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
605 } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
606 $tmp = explode(':', $inputType);
607 $inputType = $tmp[0];
608 if (!empty($tmp[1])) {
609 $inputOption = $tmp[1];
610 }
611 if (!empty($tmp[2])) {
612 $savemethod = $tmp[2];
613 }
614
615 $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
616 } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
617 $tmp = explode(':', $inputType);
618 $inputType = $tmp[0];
619 $loadmethod = $tmp[1];
620 if (!empty($tmp[2])) {
621 $savemethod = $tmp[2];
622 }
623 if (!empty($tmp[3])) {
624 $button_only = true;
625 }
626 } elseif (preg_match('/^textarea/', $inputType)) {
627 $tmp = explode(':', $inputType);
628 $inputType = $tmp[0];
629 $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
630 $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
631 } elseif (preg_match('/^ckeditor/', $inputType)) {
632 $tmp = explode(':', $inputType);
633 $inputType = $tmp[0];
634 $toolbar = $tmp[1];
635 if (!empty($tmp[2])) {
636 $width = $tmp[2];
637 }
638 if (!empty($tmp[3])) {
639 $height = $tmp[3];
640 }
641 if (!empty($tmp[4])) {
642 $savemethod = $tmp[4];
643 }
644
645 if (isModEnabled('fckeditor')) {
646 $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
647 } else {
648 $inputType = 'textarea';
649 }
650 }
651
652 $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
653 $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
654 $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
655 $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
656 if (!empty($savemethod)) {
657 $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
658 }
659 if (!empty($ext_element)) {
660 $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
661 }
662 if (!empty($custommsg)) {
663 if (is_array($custommsg)) {
664 if (!empty($custommsg['success'])) {
665 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
666 }
667 if (!empty($custommsg['error'])) {
668 $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
669 }
670 } else {
671 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
672 }
673 }
674 if ($inputType == 'textarea') {
675 $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
676 $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
677 }
678 $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
679 $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
680 } else {
681 $out = $value;
682 }
683
684 return $out;
685 }
686
705 public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
706 {
707 if ($incbefore) {
708 $text = $incbefore . $text;
709 }
710 if (!$htmltext) {
711 return $text;
712 }
713 $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
714
715 $tag = 'td';
716 if ($notabs == 2) {
717 $tag = 'div';
718 }
719 if ($notabs == 3) {
720 $tag = 'span';
721 }
722 // Sanitize tooltip
723 $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
724
725 $extrastyle = '';
726 if ($direction < 0) {
727 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
728 $extrastyle = 'padding: 0px; padding-left: 2px;';
729 }
730 if ($direction > 0) {
731 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
732 $extrastyle = 'padding: 0px; padding-right: 2px;';
733 }
734
735 $classfortooltip = 'classfortooltip';
736
737 $s = '';
738 $textfordialog = '';
739
740 if ($tooltiptrigger == '') {
741 $htmltext = str_replace('"', '&quot;', $htmltext);
742 } else {
743 $classfortooltip = 'classfortooltiponclick';
744 $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext"';
745 // Set default title of dialog
746 global $langs;
747 if ($langs instanceof Translate) {
748 $textfordialog .= ' title="'.$langs->trans("Note").'"';
749 }
750 $textfordialog .= '>' . $htmltext . '</div>';
751 }
752 if ($tooltipon == 2 || $tooltipon == 3) {
753 $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
754 if ($tooltiptrigger == '') {
755 $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on img tag to store tooltip
756 } else {
757 $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
758 }
759 } else {
760 $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
761 }
762 if ($tooltipon == 1 || $tooltipon == 3) {
763 $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ($tag != 'td' ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
764 if ($tooltiptrigger == '') {
765 $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on td tag to store tooltip
766 } else {
767 $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
768 }
769 } else {
770 $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
771 }
772 if (empty($notabs)) {
773 $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
774 } elseif ($notabs == 2) {
775 $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
776 }
777 // Define value if value is before
778 if ($direction < 0) {
779 $s .= '<' . $tag . $paramfortooltipimg;
780 if ($tag == 'td') {
781 $s .= ' class="valigntop" width="14"';
782 }
783 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
784 }
785 // Use another method to help avoid having a space in value in order to use this value with jquery
786 // Define label
787 if ((string) $text != '') {
788 $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
789 }
790 // Define value if value is after
791 if ($direction > 0) {
792 $s .= '<' . $tag . $paramfortooltipimg;
793 if ($tag == 'td') {
794 $s .= ' class="valignmiddle" width="14"';
795 }
796 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
797 }
798 if (empty($notabs)) {
799 $s .= '</tr></table>';
800 } elseif ($notabs == 2) {
801 $s .= '</div>';
802 }
803
804 return $s;
805 }
806
821 public function textwithpicto($text, $htmltooltip, $direction = 1, $type = 'help', $extracss = 'valignmiddle', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
822 {
823 global $conf, $langs;
824
825 //For backwards compatibility
826 if ($type == '0') {
827 $type = 'info';
828 } elseif ($type == '1') {
829 $type = 'help';
830 }
831 // Clean parameters
832 $tooltiptrigger = preg_replace('/[^a-z0-9]/i', '', $tooltiptrigger);
833
834 if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
835 $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
836 }
837 $alt = '';
838 if ($tooltiptrigger) {
839 $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
840 }
841
842 // If info or help with no javascript, show only text
843 if (empty($conf->use_javascript_ajax)) {
844 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
845 return $text;
846 } else {
847 $alt = $htmltooltip;
848 $htmltooltip = '';
849 }
850 }
851
852 // If info or help with smartphone, show only text (tooltip hover can't works)
853 if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
854 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
855 return $text;
856 }
857 }
858 // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
859 //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
860 //{
861 //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
862 //}
863
864 $img = '';
865 if ($type == 'info') {
866 $img = img_help(($tooltiptrigger != '' ? 2 : 0), $alt);
867 } elseif ($type == 'help') {
868 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
869 } elseif ($type == 'helpclickable') {
870 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
871 } elseif ($type == 'warning') {
872 $img = img_warning($alt);
873 } elseif ($type != 'none') {
874 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
875 $img = img_picto($alt, $type); // $type can be an image path
876 }
877
878 $tooltipon = ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2);
879
880 return $this->textwithtooltip($text, $htmltooltip, $tooltipon, $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
881 }
882
893 public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
894 {
895 global $conf, $langs, $hookmanager;
896
897 $disabled = 0;
898 $ret = '<div class="centpercent center">';
899 $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
900
901 // Complete list with data from external modules. THe module can use $_SERVER['PHP_SELF'] to know on which page we are, or use the $parameters['currentcontext'] completed by executeHooks.
902 $parameters = array();
903 $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
904 // check if there is a mass action
905
906 if (is_array($arrayofaction) && count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
907 return;
908 }
909 if (empty($reshook)) {
910 $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
911 if (is_array($arrayofaction)) {
912 foreach ($arrayofaction as $code => $label) {
913 $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
914 }
915 }
916 }
917 $ret .= $hookmanager->resPrint;
918
919 $ret .= '</select>';
920
921 if (empty($conf->dol_optimize_smallscreen)) {
922 $ret .= ajax_combobox('.' . $name . 'select');
923 }
924
925 // Warning: if you set submit button to disabled, post using 'Enter' will no more work if there is no another input submit. So we add a hidden button
926 $ret .= '<input type="submit" name="confirmmassactioninvisible" style="display: none" tabindex="-1">'; // Hidden button BEFORE so it is the one used when we submit with ENTER.
927 $ret .= '<input type="submit" disabled name="confirmmassaction"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display: none"') . ' class="reposition button smallpaddingimp' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'confirmed" value="' . dol_escape_htmltag($langs->trans("Confirm")) . '">';
928 $ret .= '</div>';
929
930 if (!empty($conf->use_javascript_ajax)) {
931 $ret .= '<!-- JS CODE TO ENABLE mass action select -->
932 <script nonce="' . getNonce() . '">
933 function initCheckForSelect(mode, name, cssclass) { /* mode is 0 during init of page or click all, 1 when we click on 1 checkboxi, "name" refers to the class of the massaction button, "cssclass" to the class of the check for select boxes */
934 atleastoneselected=0;
935 jQuery("."+cssclass).each(function( index ) {
936 /* console.log( index + ": " + $( this ).text() ); */
937 if ($(this).is(\':checked\')) atleastoneselected++;
938 });
939
940 console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
941
942 if (atleastoneselected || ' . ((int) $alwaysvisible) . ') {
943 jQuery("."+name).show();
944 ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
945 ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
946 } else {
947 jQuery("."+name).hide();
948 jQuery("."+name+"other").hide();
949 }
950 }
951
952 jQuery(document).ready(function () {
953 initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
954 jQuery(".' . $cssclass . '").change(function() {
955 console.log("A change was done on .' . $cssclass . '");
956 initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
957 });
958 jQuery(".' . $name . 'select").change(function() {
959 var massaction = $( this ).val();
960 var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
961 if (massaction == "builddoc") {
962 urlform = urlform + "#show_files";
963 }
964 $( this ).closest("form").attr("action", urlform);
965 console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
966 /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
967 if ($(this).val() != \'0\') {
968 jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
969 jQuery(".' . $name . 'other").hide(); /* To disable if another div was open */
970 jQuery(".' . $name . '"+massaction).show();
971 } else {
972 jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
973 jQuery(".' . $name . 'other").hide(); /* To disable any div open */
974 }
975 });
976 });
977 </script>
978 ';
979 }
980
981 return $ret;
982 }
983
984 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
985
1003 public function select_country($selected = '', $htmlname = 'country_id', $htmloption = '', $maxlength = 0, $morecss = 'minwidth300', $usecodeaskey = '', $showempty = 1, $disablefavorites = 0, $addspecialentries = 0, $exclude_country_code = array(), $hideflags = 0, $forcecombo = 0)
1004 {
1005 // phpcs:enable
1006 global $langs, $mysoc;
1007
1008 $langs->load("dict");
1009
1010 $selected = (string) $selected;
1011
1012 $out = '';
1014 $countryArray = array();
1015 $favorite = array();
1016 $label = array();
1017 $atleastonefavorite = 0;
1018
1019 $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
1020 $sql .= " FROM " . $this->db->prefix() . "c_country";
1021 $sql .= " WHERE active > 0";
1022 //$sql.= " ORDER BY code ASC";
1023
1024 dol_syslog(get_class($this) . "::select_country", LOG_DEBUG);
1025
1026 $resql = $this->db->query($sql);
1027 if ($resql) {
1028 $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
1029 $num = $this->db->num_rows($resql);
1030 $i = 0;
1031 if ($num) {
1032 while ($i < $num) {
1033 $obj = $this->db->fetch_object($resql);
1034
1035 $countryArray[$i]
1036 = array(
1037 'rowid' => (int) $obj->rowid,
1038 'code_iso' => (string) $obj->code_iso,
1039 'code_iso3' => (string) $obj->code_iso3,
1040 'label' => (string) ($obj->code_iso && $langs->transnoentitiesnoconv("Country" . $obj->code_iso) != "Country" . $obj->code_iso ? $langs->transnoentitiesnoconv("Country" . $obj->code_iso) : ($obj->label != '-' ? $obj->label : '')),
1041 'favorite' => (string) $obj->favorite,
1042 'eec' => (string) $obj->eec,
1043 );
1044 $favorite[$i] = $obj->favorite;
1045 $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
1046 $i++;
1047 }
1048
1049 if (empty($disablefavorites)) {
1050 $array1_sort_order = SORT_DESC;
1051 $array2_sort_order = SORT_ASC;
1052 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
1053 } else {
1054 $countryArray = dol_sort_array($countryArray, 'label');
1055 }
1056
1057 if ($showempty) {
1058 if (is_numeric($showempty)) {
1059 $out .= '<option value="">&nbsp;</option>' . "\n";
1060 } else {
1061 $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
1062 }
1063 }
1064
1065 if ($addspecialentries) { // Add dedicated entries for groups of countries
1066 //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
1067 $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1068 $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
1069 if ($mysoc->isInEEC()) {
1070 $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1071 }
1072 $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
1073 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1074 }
1075
1076 foreach ($countryArray as $row) {
1077 //if (empty($showempty) && empty($row['rowid'])) continue;
1078 if (empty($row['rowid'])) {
1079 continue;
1080 }
1081 if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1082 continue; // exclude some countries
1083 }
1084
1085 if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1086 $atleastonefavorite++;
1087 }
1088 if (empty($row['favorite']) && $atleastonefavorite) {
1089 $atleastonefavorite = 0;
1090 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1091 }
1092
1093 $labeltoshow = '';
1094 if ($row['label']) {
1095 $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1096 } else {
1097 $labeltoshow .= '&nbsp;';
1098 }
1099 if ($row['code_iso']) {
1100 $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1101 if (empty($hideflags)) {
1102 $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1103 $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1104 }
1105 }
1106
1107 if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1108 $out .= '<option value="' . ($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']) . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '" data-eec="' . ((int) $row['eec']) . '">';
1109 } else {
1110 $out .= '<option value="' . ($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']) . '" data-html="' . dol_escape_htmltag($labeltoshow) . '" data-eec="' . ((int) $row['eec']) . '">';
1111 }
1112 $out .= dol_string_nohtmltag($labeltoshow);
1113 $out .= '</option>' . "\n";
1114 }
1115 }
1116 $out .= '</select>';
1117 } else {
1118 dol_print_error($this->db);
1119 }
1120
1121 // Make select dynamic
1122 if (empty($forcecombo)) {
1123 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1124 $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1125 }
1126
1127 return $out;
1128 }
1129
1140 public function selectPhoneCode($selected = '', $htmlname = 'phone_code', $morecss = 'maxwidth150', $showempty = 0, $country_id_hint = 0)
1141 {
1142 global $langs;
1143
1144 $langs->load("dict");
1145
1146 $out = '';
1147 $codeArray = array();
1148 $favorite = array();
1149 $label = array();
1150 $atleastonefavorite = 0;
1151
1152 $sql = "SELECT rowid, code, label, phone_code, favorite, trunk_prefix";
1153 $sql .= " FROM ".$this->db->prefix()."c_country";
1154 // phone_code is an integer column, comparing it to an empty string fails on PostgreSQL
1155 $sql .= " WHERE active > 0 AND phone_code IS NOT NULL AND phone_code != 0";
1156
1157 dol_syslog(get_class($this)."::selectPhoneCode", LOG_DEBUG);
1158 $resql = $this->db->query($sql);
1159 if ($resql) {
1160 $num = $this->db->num_rows($resql);
1161 $i = 0;
1162 while ($i < $num) {
1163 $obj = $this->db->fetch_object($resql);
1164
1165 $translabel = ($obj->code && $langs->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langs->transnoentitiesnoconv("Country".$obj->code) : $obj->label;
1166
1167 $codeArray[$i]['rowid'] = $obj->rowid;
1168 $codeArray[$i]['code'] = $obj->code;
1169 $codeArray[$i]['label'] = $translabel;
1170 $codeArray[$i]['phone_code'] = '+'.$obj->phone_code;
1171 $codeArray[$i]['favorite'] = $obj->favorite;
1172 $codeArray[$i]['trunk_prefix'] = $obj->trunk_prefix;
1173 $favorite[$i] = $obj->favorite;
1174 $label[$i] = dol_string_unaccent($translabel);
1175 $i++;
1176 }
1177
1178 $array1_sort_order = SORT_DESC;
1179 $array2_sort_order = SORT_ASC;
1180 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $codeArray);
1181
1182 $out .= '<select id="select'.$htmlname.'" class="flat selectphonecode'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1183
1184 if ($showempty) {
1185 $out .= '<option value="">&nbsp;</option>'."\n";
1186 }
1187
1188 // Determine which row index to select: prefer country_id_hint match, fallback to first phone_code match
1189 $selectedIdx = -1;
1190 $firstMatchIdx = -1;
1191 if ($selected !== '') {
1192 foreach ($codeArray as $idx => $row) {
1193 if ($row['phone_code'] == $selected) {
1194 if ($firstMatchIdx < 0) {
1195 $firstMatchIdx = $idx;
1196 }
1197 if ($country_id_hint > 0 && $row['rowid'] == $country_id_hint) {
1198 $selectedIdx = $idx;
1199 break;
1200 }
1201 }
1202 }
1203 if ($selectedIdx < 0 && $firstMatchIdx >= 0) {
1204 $selectedIdx = $firstMatchIdx;
1205 }
1206 }
1207
1208 foreach ($codeArray as $idx => $row) {
1209 if (empty($row['code'])) {
1210 continue;
1211 }
1212
1213 if ($row['favorite']) {
1214 $atleastonefavorite++;
1215 }
1216 if (empty($row['favorite']) && $atleastonefavorite) {
1217 $atleastonefavorite = 0;
1218 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1219 }
1220
1221 $tmpflag = picto_from_langcode($row['code'], 'class="saturatemedium paddingrightonly"', 1);
1222
1223 // Short label for selected display: flag + country code
1224 $selectlabel = ($tmpflag ? $tmpflag.' ' : '').$row['code'];
1225
1226 // Detailed label for dropdown list: flag + country name + phone code
1227 $labeltoshow = ($tmpflag ? $tmpflag.' ' : '').$row['label'].' '.$row['phone_code'];
1228
1229 $out .= '<option value="'.dol_escape_htmltag($row['phone_code']).'"';
1230 if ($idx === $selectedIdx) {
1231 $out .= ' selected';
1232 }
1233 $out .= ' data-html="'.dol_escape_htmltag($labeltoshow).'"';
1234 $out .= ' data-select-html="'.dol_escape_htmltag($selectlabel).'"';
1235 $out .= ' data-country-id="'.((int) $row['rowid']).'"';
1236 $out .= ' data-trunk-prefix="'.dol_escape_htmltag((string) $row['trunk_prefix']).'"';
1237 $out .= '>';
1238 $out .= dol_string_nohtmltag($labeltoshow);
1239 $out .= '</option>'."\n";
1240 }
1241 $out .= '</select>';
1242 } else {
1243 dol_print_error($this->db);
1244 }
1245
1246 // Make select dynamic
1247 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1248 $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1249
1250 return $out;
1251 }
1252
1269 public function showPhoneInput($phoneValue, $htmlname, $country_id_hint = 0, $picto = 'object_phoning', $morecss = 'maxwidth150', $maxlength = 0, $countrySelectorId = 'selectcountry_id')
1270 {
1271 global $mysoc;
1272
1273 include_once DOL_DOCUMENT_ROOT.'/core/lib/phone.lib.php';
1274
1275 $codename = $htmlname.'_code';
1276
1277 // Fallback country_id: use caller hint, else main company country
1278 if (empty($country_id_hint) && !empty($mysoc->country_id)) {
1279 $country_id_hint = $mysoc->country_id;
1280 }
1281
1282 // On POST re-display, read the hidden field (which contains the full phone string)
1283 if (GETPOSTISSET($htmlname)) {
1284 $fullPhone = (string) GETPOST($htmlname);
1285 } else {
1286 $fullPhone = (string) $phoneValue;
1287 }
1288
1289 // Split into code + number
1290 $parsed = dol_parse_phone($fullPhone);
1291
1292 // Resolve default phone code: parsed code if set, else from country hint
1293 $phonecode = !empty($parsed['code']) ? $parsed['code'] : dol_get_phone_code_from_country($this->db, $country_id_hint);
1294
1295 $selectedCode = $phonecode;
1296 $numberValue = $parsed['number'];
1297
1298 // Add back trunk prefix for display (e.g. "644986885" → "0644986885" for France)
1299 if ($numberValue !== '' && $selectedCode !== '') {
1300 $trunkPrefix = dol_get_trunk_prefix($this->db, $selectedCode);
1301 if ($trunkPrefix !== '' && strpos($numberValue, $trunkPrefix) !== 0) {
1302 $numberValue = $trunkPrefix.$numberValue;
1303 }
1304 }
1305
1306 // Build output: hidden field (POSTed value)
1307 $out = '<input type="hidden" name="'.dol_escape_htmltag($htmlname).'" id="'.dol_escape_htmltag($htmlname).'" value="'.dol_escape_htmltag($fullPhone).'">';
1308
1309 // Picto
1310 $out .= img_picto('', $picto, 'class="pictofixedwidth"');
1311
1312 // Phone code select (display-only name, not submitted as separate POST param)
1313 $out .= $this->selectPhoneCode($selectedCode, $codename, 'maxwidth75 phone_code_select', 0, $country_id_hint);
1314
1315 // Visible number input (no name — not POSTed)
1316 $out .= '<input type="tel" inputmode="numeric" pattern="[0-9]*" id="'.dol_escape_htmltag($htmlname).'_input" class="'.dol_escape_htmltag($morecss).'"';
1317 if ($maxlength > 0) {
1318 $out .= ' maxlength="'.$maxlength.'"';
1319 }
1320 $out .= ' value="'.dol_escape_htmltag($numberValue).'">';
1321
1322 // Per-field JS to sync hidden field
1323 $out .= $this->getPhoneInputFieldJs($htmlname, $codename);
1324
1325 // Shared JS for country-sync (output once per page)
1326 $out .= $this->getPhoneInputSharedJs($countrySelectorId);
1327
1328 return $out;
1329 }
1330
1341 private function getPhoneInputFieldJs($htmlname, $codename)
1342 {
1343 $hiddenId = dol_escape_js($htmlname);
1344 $inputId = dol_escape_js($htmlname).'_input';
1345 $selectId = 'select'.dol_escape_js($codename);
1346
1347 $out = "\n".'<script type="text/javascript">'."\n";
1348 $out .= 'jQuery(document).ready(function() {'."\n";
1349 $out .= ' function syncPhoneField_'.$hiddenId.'() {'."\n";
1350 $out .= ' var selectEl = jQuery("#'.$selectId.'");'."\n";
1351 $out .= ' var code = selectEl.val() || "";'."\n";
1352 $out .= ' var number = (jQuery("#'.$inputId.'").val() || "").replace(/[^0-9]/g, "");'."\n";
1353 $out .= ' if (code && number) {'."\n";
1354 $out .= ' var selOpt = selectEl[0] && selectEl[0].selectedOptions && selectEl[0].selectedOptions[0];'."\n";
1355 $out .= ' var trunkPrefix = selOpt ? (selOpt.getAttribute("data-trunk-prefix") || "") : "";'."\n";
1356 $out .= ' if (trunkPrefix !== "" && number.indexOf(trunkPrefix) === 0) {'."\n";
1357 $out .= ' number = number.substring(trunkPrefix.length);'."\n";
1358 $out .= ' }'."\n";
1359 $out .= ' jQuery("#'.$hiddenId.'").val(code + " " + number);'."\n";
1360 $out .= ' } else if (number) {'."\n";
1361 $out .= ' jQuery("#'.$hiddenId.'").val(number);'."\n";
1362 $out .= ' } else {'."\n";
1363 $out .= ' jQuery("#'.$hiddenId.'").val("");'."\n";
1364 $out .= ' }'."\n";
1365 $out .= ' }'."\n";
1366 $out .= ' jQuery("#'.$selectId.'").on("change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1367 $out .= ' jQuery("#'.$inputId.'").on("input change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1368 $out .= '});'."\n";
1369 $out .= '</script>'."\n";
1370
1371 return $out;
1372 }
1373
1383 private function getPhoneInputSharedJs($countrySelectorId)
1384 {
1385 if ($this->phoneInputSharedJsLoaded) {
1386 return '';
1387 }
1388 $this->phoneInputSharedJsLoaded = true;
1389
1390 $out = "\n".'<script type="text/javascript">'."\n";
1391 $out .= 'jQuery(document).ready(function() {'."\n";
1392 $out .= ' jQuery("#'.dol_escape_js($countrySelectorId).'").on("change", function() {'."\n";
1393 $out .= ' var country_id = jQuery(this).val();'."\n";
1394 $out .= ' if (country_id) {'."\n";
1395 $out .= ' jQuery.getJSON("'.DOL_URL_ROOT.'/core/ajax/getphonecode.php", {country_id: country_id, token: "'.currentToken().'"}, function(data) {'."\n";
1396 $out .= ' if (data.phone_code) {'."\n";
1397 $out .= ' jQuery(".phone_code_select").each(function() {'."\n";
1398 $out .= ' jQuery(this).val(data.phone_code).trigger("change");'."\n";
1399 $out .= ' });'."\n";
1400 $out .= ' }'."\n";
1401 $out .= ' });'."\n";
1402 $out .= ' }'."\n";
1403 $out .= ' });'."\n";
1404 $out .= '});'."\n";
1405 $out .= '</script>'."\n";
1406
1407 return $out;
1408 }
1409
1423 private function makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
1424 {
1425 dol_syslog(__METHOD__, LOG_DEBUG);
1426 global $langs, $form;
1427 if (empty($form)) {
1428 $form = new Form($this->db);
1429 }
1430 $htmltoenteralink = '';
1431 $i = 0;
1432
1433 // headers
1434 $htmltoenteralink .= '<tr class="liste_titre">';
1435 $htmltoenteralink .= '<td class="nowrap"></td>';
1436 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1437 $htmltoenteralink .= '<td>' . $langs->trans("RefCustomer") . '</td>';
1438 $htmltoenteralink .= '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
1439 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1440 $htmltoenteralink .= '</tr>';
1441
1442 // rows with data
1443 while ($i < $num) {
1444 $objp = $this->db->fetch_object($resqllist);
1445 $alreadylinked = false;
1446 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1447 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1448 $alreadylinked = true;
1449 }
1450 }
1451 $htmltoenteralink .= '<tr class="oddeven">';
1452 $htmltoenteralink .= '<td>';
1453 if ($alreadylinked) {
1454 $htmltoenteralink .= img_picto('', 'link');
1455 } else {
1456 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1457 }
1458 $htmltoenteralink .= '</td>';
1459 $htmltoenteralink .= '<td>';
1460 if (!$alreadylinked) {
1461 $htmltoenteralink .= '<label for="' . $key . '_' . $objp->rowid . '">';
1462 }
1463 $htmltoenteralink .= $objp->ref;
1464 if (!$alreadylinked) {
1465 $htmltoenteralink .= '</label>';
1466 }
1467 $htmltoenteralink .= '</td>';
1468 $htmltoenteralink .= '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
1469 $htmltoenteralink .= '<td class="right">';
1470 if ($possiblelink['label'] == 'LinkToContract') {
1471 $htmltoenteralink .= $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
1472 }
1473 $htmltoenteralink .= '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
1474 $htmltoenteralink .= '</td>';
1475 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1476 $htmltoenteralink .= '</tr>';
1477 $i++;
1478 }
1479
1480 return $htmltoenteralink;
1481 }
1482
1497 private function makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
1498 {
1499 dol_syslog(__METHOD__, LOG_DEBUG);
1500 global $langs, $form;
1501 require_once DOL_DOCUMENT_ROOT . '/eventorganization/class/conferenceorboothattendee.class.php';
1502 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1503 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
1504 $attendeestatic = new ConferenceOrBoothAttendee($this->db);
1505 $companystatic = new Societe($this->db);
1506 $projectstatic = new Project($this->db);
1507 if (empty($form)) {
1508 $form = new Form($this->db);
1509 }
1510 $htmltoenteralink = '';
1511 $i = 0;
1512
1513 // headers
1514 $htmltoenteralink .= '<tr class="liste_titre">';
1515 $htmltoenteralink .= '<td class="nowrap"></td>';
1516 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1517 $htmltoenteralink .= '<td>' . $langs->trans("Name") . '</td>';
1518 $htmltoenteralink .= '<td>' . $langs->trans("Email") . '</td>';
1519 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1520 $htmltoenteralink .= '<td>' . $langs->trans("Project") . '</td>';
1521 $htmltoenteralink .= '<td>' . $langs->trans("DateOfRegistration") . '</td>';
1522 $htmltoenteralink .= '</tr>';
1523
1524 // rows with data
1525 while ($i < $num) {
1526 $objp = $this->db->fetch_object($resqllist);
1527 $alreadylinked = false;
1528 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1529 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1530 $alreadylinked = true;
1531 }
1532 }
1533 $htmltoenteralink .= '<tr class="oddeven">';
1534 $htmltoenteralink .= '<td>';
1535 if ($alreadylinked) {
1536 $htmltoenteralink .= img_picto('', 'link');
1537 } else {
1538 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1539 }
1540 $htmltoenteralink .= '</td>';
1541 $fetchattendee = $attendeestatic->fetch($objp->rowid);
1542 if ($fetchattendee) {
1543 $htmltoenteralink .= '<td>' . $attendeestatic->getNomUrl(0). '</td>';
1544 } else {
1545 $htmltoenteralink .= '<td><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
1546 }
1547 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1548 $htmltoenteralink .= '<td>' . $objp->email . '</td>';
1549 $fetchcompany = $companystatic->fetch($objp->socid);
1550 if ($fetchcompany) {
1551 $htmltoenteralink .= '<td>' . $companystatic->getNomUrl(0). '</td>';
1552 } else {
1553 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1554 }
1555 $fetchcproject = $projectstatic->fetch($objp->fk_project);
1556 if ($fetchcproject) {
1557 $htmltoenteralink .= '<td>' . $projectstatic->getNomUrl(0). '</td>';
1558 } else {
1559 $htmltoenteralink .= '<td>' . $objp->fk_project . '</td>';
1560 }
1561 $htmltoenteralink .= '<td>' . $objp->date_subscription . '</td>';
1562 $htmltoenteralink .= '</tr>';
1563 $i++;
1564 }
1565
1566 return $htmltoenteralink;
1567 }
1568
1569 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1570
1584 public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1585 {
1586 // phpcs:enable
1587 global $conf, $langs;
1588
1589 $langs->load("dict");
1590
1591 $out = '';
1592 //$moreattrib = '';
1593 $incotermArray = array();
1594
1595 $sql = "SELECT rowid, code";
1596 $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1597 $sql .= " WHERE active > 0";
1598 $sql .= " ORDER BY code ASC";
1599
1600 dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1601 $resql = $this->db->query($sql);
1602 if ($resql) {
1603 if ($conf->use_javascript_ajax && !$forcecombo) {
1604 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1605 $out .= ajax_combobox($htmlname, $events);
1606 }
1607
1608 if (!empty($page)) {
1609 $out .= '<form method="post" action="' . $page . '">';
1610 $out .= '<input type="hidden" name="action" value="set_incoterms">';
1611 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1612 }
1613
1614 $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1615 $out .= '<option value="0">&nbsp;</option>';
1616 $num = $this->db->num_rows($resql);
1617 $i = 0;
1618 if ($num) {
1619 while ($i < $num) {
1620 $obj = $this->db->fetch_object($resql);
1621 $incotermArray[$i]['rowid'] = $obj->rowid;
1622 $incotermArray[$i]['code'] = $obj->code;
1623 $i++;
1624 }
1625
1626 foreach ($incotermArray as $row) {
1627 if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1628 $out .= '<option value="' . $row['rowid'] . '" selected>';
1629 } else {
1630 $out .= '<option value="' . $row['rowid'] . '">';
1631 }
1632
1633 if ($row['code']) {
1634 $out .= $row['code'];
1635 }
1636
1637 $out .= '</option>';
1638 }
1639 }
1640 $out .= '</select>';
1641 $out .= ajax_combobox($htmlname);
1642
1643 if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1644 $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1645 //$moreattrib .= ' autocomplete="off"';
1646 }
1647 $out .= '<input id="location_incoterms" class="maxwidthonsmartphone heightofcombo" type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1648
1649 if (!empty($page)) {
1650 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1651 }
1652 } else {
1653 dol_print_error($this->db);
1654 }
1655
1656 return $out;
1657 }
1658
1659 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1660
1674 public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
1675 {
1676 // phpcs:enable
1677 global $langs;
1678
1679 // If product & services are enabled or both disabled.
1680 if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1681 || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1682 if (empty($hidetext)) {
1683 print $langs->trans("Type").'...';
1684 }
1685
1686 print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1687 if ($showempty) {
1688 print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
1689 if ($selected == -1) {
1690 print ' selected';
1691 }
1692 print '>';
1693 if (is_numeric($showempty)) {
1694 print '&nbsp;';
1695 } else {
1696 print $showempty;
1697 }
1698 print '</option>';
1699 }
1700
1701 print '<option value="0"';
1702 if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1703 print ' selected';
1704 }
1705 print '>' . $langs->trans("Product");
1706 print '</option>';
1707
1708 print '<option value="1"';
1709 if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1710 print ' selected';
1711 }
1712 print '>' . $langs->trans("Service");
1713 print '</option>';
1714
1715 print '</select>';
1716
1717 if ($useajaxcombo) {
1718 print ajax_combobox('select_' . $htmlname);
1719 }
1720 //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1721 }
1722 if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1723 print $langs->trans("Service");
1724 print '<input type="hidden" name="' . $htmlname . '" value="1">';
1725 }
1726 if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1727 print $langs->trans("Product");
1728 print '<input type="hidden" name="' . $htmlname . '" value="0">';
1729 }
1730 if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1731 print '<input type="hidden" name="' . $htmlname . '" value="1">'; // By default we set on service for contract. If CONTRACT_SUPPORT_PRODUCTS is set, forceall should be 1 not -1
1732 }
1733 }
1734
1735 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1736
1742 public function load_cache_types_fees()
1743 {
1744 // phpcs:enable
1745 global $langs;
1746
1747 $num = count($this->cache_types_fees);
1748 if ($num > 0) {
1749 return 0; // Cache already loaded
1750 }
1751
1752 dol_syslog(__METHOD__, LOG_DEBUG);
1753
1754 $langs->load("trips");
1755
1756 $sql = "SELECT c.code, c.label";
1757 $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1758 $sql .= " WHERE active > 0";
1759
1760 $resql = $this->db->query($sql);
1761 if ($resql) {
1762 $num = $this->db->num_rows($resql);
1763 $i = 0;
1764
1765 while ($i < $num) {
1766 $obj = $this->db->fetch_object($resql);
1767
1768 // If a translation exists, we use is, otherwise, we take the label by default
1769 $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1770 $this->cache_types_fees[$obj->code] = $label;
1771 $i++;
1772 }
1773
1774 asort($this->cache_types_fees);
1775
1776 return $num;
1777 } else {
1778 dol_print_error($this->db);
1779 return -1;
1780 }
1781 }
1782
1783 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1784
1793 public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1794 {
1795 // phpcs:enable
1796 global $user, $langs;
1797
1798 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1799
1800 $this->load_cache_types_fees();
1801
1802 print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1803 if ($showempty) {
1804 print '<option value="-1"';
1805 if ($selected == -1) {
1806 print ' selected';
1807 }
1808 print '>&nbsp;</option>';
1809 }
1810
1811 foreach ($this->cache_types_fees as $key => $value) {
1812 print '<option value="' . $key . '"';
1813 if ($key == $selected) {
1814 print ' selected';
1815 }
1816 print '>';
1817 print $value;
1818 print '</option>';
1819 }
1820
1821 print '</select>';
1822 if ($user->admin) {
1823 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1824 }
1825 }
1826
1827
1828 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1829
1852 public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false, $excludeids = array(), $showcode = 0)
1853 {
1854 // phpcs:enable
1855 global $conf, $langs;
1856
1857 $out = '';
1858
1859 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1860 if (is_null($ajaxoptions)) {
1861 $ajaxoptions = array();
1862 }
1863
1864 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1865
1866 // No immediate load of all database
1867 $placeholder = '';
1868 if ($selected && empty($selected_input_value)) {
1869 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1870 $societetmp = new Societe($this->db);
1871 $societetmp->fetch($selected);
1872 $selected_input_value = $societetmp->name;
1873 unset($societetmp);
1874 }
1875
1876 // mode 1
1877 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($excludeids) ? '' : '&excludeids=' . implode(',', $excludeids)) . ($showtype ? '&showtype=' . urlencode((string) ($showtype)) : '') . ($showcode ? '&showcode=' . urlencode((string) ($showcode)) : '') . ($limit ? '&limit='.$limit : '');
1878
1879 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1880 if (empty($hidelabel)) {
1881 $out .= $langs->trans("RefOrLabel") . ' : ';
1882 } elseif ($hidelabel == 1 && !is_numeric($showempty)) {
1883 $placeholder = $langs->trans($showempty);
1884 } elseif ($hidelabel > 1) {
1885 $placeholder = $langs->trans("RefOrLabel");
1886 if ($hidelabel == 2) {
1887 $out .= img_picto($langs->trans("Search"), 'search');
1888 }
1889 }
1890 $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('THIRDPARTY_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' spellcheck="false" />';
1891 if ($hidelabel == 3) {
1892 $out .= img_picto($langs->trans("Search"), 'search');
1893 }
1894
1895 $out .= ajax_event($htmlname, $events);
1896
1897 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, getDolGlobalInt('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1898 } else {
1899 // Immediate load of all database
1900 $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1901 }
1902
1903 return $out;
1904 }
1905
1906
1907 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1908
1934 public function select_contact($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $nokeyifsocid = true, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $selected_input_value = '', $filter = '')
1935 {
1936 // phpcs:enable
1937
1938 global $conf, $langs;
1939
1940 $out = '';
1941
1942 $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1943 if ($nokeyifsocid && $socid > 0) {
1944 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1945 }
1946
1947 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1948 $ajaxoptions = array();
1949
1950 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1951
1952 // No immediate load of all database
1953 $placeholder = '';
1954 if ($selected && empty($selected_input_value)) {
1955 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1956 $contacttmp = new Contact($this->db);
1957 $contacttmp->fetch($selected);
1958 $selected_input_value = $contacttmp->getFullName($langs);
1959 unset($contacttmp);
1960 }
1961 if (!is_numeric($showempty)) {
1962 $placeholder = $showempty;
1963 }
1964
1965 // mode 1
1966 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1967
1968 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1969
1970 $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('CONTACT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' spellcheck="false" />';
1971
1972 $out .= ajax_event($htmlname, $events);
1973
1974 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/contact/ajax/contact.php', $urloption, getDolGlobalInt('CONTACT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1975 } else {
1976 // Immediate load of all database
1977 $multiple = false;
1978 $disableifempty = 0;
1979 $options_only = 0;
1980 $limitto = '';
1981
1982 $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty);
1983 }
1984
1985 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1986
1987 return $out;
1988 }
1989
1990
1991 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1992
2016 public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false, $excludeids = array(), $showcode = 0)
2017 {
2018 // phpcs:enable
2019 global $user, $langs;
2020 global $hookmanager;
2021
2022 $langs->loadLangs(array("companies", "suppliers"));
2023
2024 $out = '';
2025 $num = 0;
2026 $outarray = array();
2027
2028 if ($selected === '') {
2029 $selected = array();
2030 } elseif (!is_array($selected)) {
2031 $selected = array($selected);
2032 }
2033
2034 // Clean $filter that may contains sql conditions so sql code
2035 if (function_exists('testSqlAndScriptInject')) {
2036 if (testSqlAndScriptInject($filter, 3) > 0) {
2037 $filter = '';
2038 return 'SQLInjectionTryDetected';
2039 }
2040 }
2041
2042 if ($filter != '') { // If a filter was provided
2043 $errormsg = '';
2044 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2045
2046 // Redo clean $filter that may contains sql conditions so sql code
2047 if (function_exists('testSqlAndScriptInject')) {
2048 if (testSqlAndScriptInject($filter, 3) > 0) {
2049 $filter = '';
2050 return 'SQLInjectionTryDetected';
2051 }
2052 }
2053 }
2054
2055 // We search companies
2056 $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
2057 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2058 $sql .= ", s.address, s.zip, s.town";
2059 $sql .= ", dictp.code as country_code";
2060 }
2061 $sql .= " FROM " . $this->db->prefix() . "societe as s";
2062 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2063 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
2064 }
2065 if (!$user->hasRight('societe', 'client', 'voir')) {
2066 $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
2067 }
2068 $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
2069 if (!empty($user->socid)) {
2070 $sql .= " AND s.rowid = " . ((int) $user->socid);
2071 }
2072 if ($filter) {
2073 // $filter is safe because, it has been tested by testSqlAndScriptInject() and sanitized by forgeSQLFromUniversalSearchCriteria()
2074 $sqlwhere = $filter; // @phan-suppress-current-line SqlInjection
2075 $sql .= " AND (" . $sqlwhere . ")";
2076 }
2077 if (!$user->hasRight('societe', 'client', 'voir')) {
2078 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
2079 }
2080 if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
2081 $sql .= " AND s.status <> 0";
2082 }
2083 if (!empty($excludeids)) {
2084 $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
2085 }
2086 // Add where from hooks
2087 $parameters = array();
2088 $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
2089 $sql .= $hookmanager->resPrint;
2090 // Add criteria
2091 if ($filterkey && $filterkey != '') {
2092 $sql .= " AND (";
2093 $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
2094 // For natural search
2095 $search_crit = explode(' ', $filterkey);
2096 $i = 0;
2097 if (count($search_crit) > 1) {
2098 $sql .= "(";
2099 }
2100 foreach ($search_crit as $crit) {
2101 if ($i > 0) {
2102 $sql .= " AND ";
2103 }
2104 $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
2105 $i++;
2106 }
2107 if (count($search_crit) > 1) {
2108 $sql .= ")";
2109 }
2110 if (isModEnabled('barcode')) {
2111 $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2112 }
2113 $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2114 $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2115 $sql .= ")";
2116 }
2117 $sql .= $this->db->order("nom", "ASC");
2118 $sql .= $this->db->plimit($limit, 0);
2119
2120 // Build output string
2121 dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
2122 $resql = $this->db->query($sql);
2123 if ($resql) {
2124 // Construct $out and $outarray
2125 $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple' : '') . '>' . "\n";
2126
2127 $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
2128 if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
2129 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2130 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2131 if ($showempty && !is_numeric($showempty)) {
2132 $textifempty = $langs->trans($showempty);
2133 } else {
2134 $textifempty .= $langs->trans("All");
2135 }
2136 }
2137 if ($showempty) {
2138 $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
2139 }
2140
2141 $companytemp = new Societe($this->db);
2142
2143 $num = $this->db->num_rows($resql);
2144 $i = 0;
2145 if ($num) {
2146 while ($i < $num) {
2147 $obj = $this->db->fetch_object($resql);
2148 $label = '';
2149 if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
2150 if (($obj->client) && (!empty($obj->code_client))) {
2151 $label = $obj->code_client . ' - ';
2152 }
2153 if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
2154 $label .= $obj->code_fournisseur . ' - ';
2155 }
2156 $label .= ' ' . $obj->name;
2157 } else {
2158 $label = $obj->name;
2159 }
2160
2161 if (!empty($obj->name_alias)) {
2162 $label .= ' (' . $obj->name_alias . ')';
2163 }
2164
2165 if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
2166 $label .= ' - '.$obj->tva_intra;
2167 }
2168
2169 $labelhtml = $label;
2170
2171 if ($showtype) {
2172 $companytemp->id = $obj->rowid;
2173 $companytemp->client = $obj->client;
2174 $companytemp->fournisseur = $obj->fournisseur;
2175 $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
2176 if ($tmptype) {
2177 $labelhtml .= ' ' . $tmptype;
2178 }
2179
2180 if ($obj->client || $obj->fournisseur) {
2181 $label .= ' (';
2182 }
2183 if ($obj->client == 1 || $obj->client == 3) {
2184 $label .= $langs->trans("Customer");
2185 }
2186 if ($obj->client == 2 || $obj->client == 3) {
2187 $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
2188 }
2189 if ($obj->fournisseur) {
2190 $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
2191 }
2192 if ($obj->client || $obj->fournisseur) {
2193 $label .= ')';
2194 }
2195 }
2196
2197 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2198 $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
2199 if (!empty($obj->country_code)) {
2200 $s .= ', ' . $langs->trans('Country' . $obj->country_code);
2201 }
2202 $label .= $s;
2203 $labelhtml .= $s;
2204 }
2205
2206 if (empty($outputmode)) {
2207 if (in_array($obj->rowid, $selected)) {
2208 $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2209 } else {
2210 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2211 }
2212 } else {
2213 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
2214 }
2215
2216 $i++;
2217 if (($i % 10) == 0) {
2218 $out .= "\n";
2219 }
2220 }
2221 }
2222 $out .= '</select>' . "\n";
2223 if (!$forcecombo) {
2224 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2225 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
2226 }
2227 } else {
2228 dol_print_error($this->db);
2229 }
2230
2231 $this->result = array('nbofthirdparties' => $num);
2232
2233 if ($outputmode) {
2234 return $outarray;
2235 }
2236 return $out;
2237 }
2238
2239
2265 public function selectcontacts($socid, $selected = array(), $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = 0, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0, $filter = '')
2266 {
2267 global $conf, $user, $langs, $hookmanager, $action;
2268
2269 $langs->load('companies');
2270
2271 if (empty($htmlid)) {
2272 $htmlid = $htmlname;
2273 }
2274 $num = 0;
2275 $out = '';
2276 $outarray = array();
2277
2278 if ($selected === '') {
2279 $selected = array();
2280 } elseif (!is_array($selected)) {
2281 $selected = array((int) $selected);
2282 }
2283
2284 // Clean $filter that may contains sql conditions so sql code
2285 if (function_exists('testSqlAndScriptInject')) {
2286 if (testSqlAndScriptInject($filter, 3) > 0) {
2287 $filter = '';
2288 return 'SQLInjectionTryDetected';
2289 }
2290 }
2291
2292 if ($filter != '') { // If a filter was provided
2293 if (preg_match('/[\‍(\‍)]/', $filter)) {
2294 // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
2295 $errormsg = '';
2296 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2297
2298 // Redo clean $filter that may contains sql conditions so sql code
2299 if (function_exists('testSqlAndScriptInject')) {
2300 if (testSqlAndScriptInject($filter, 3) > 0) {
2301 $filter = '';
2302 return 'SQLInjectionTryDetected';
2303 }
2304 }
2305 } else {
2306 // If not, we do nothing. We already know that there is no parenthesis
2307 // TODO Disallow this case in a future by returning an error here.
2308 dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Filter Syntax.", LOG_WARNING);
2309 }
2310 }
2311
2312 if (!is_object($hookmanager)) {
2313 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2314 $hookmanager = new HookManager($this->db);
2315 }
2316
2317 // We search third parties
2318 $sql = "SELECT sp.rowid, sp.lastname, sp.statut, sp.firstname, sp.poste, sp.email, sp.phone, sp.phone_perso, sp.phone_mobile, sp.town AS contact_town";
2319 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2320 $sql .= ", s.nom as company, s.town AS company_town";
2321 }
2322 $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
2323 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2324 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = sp.fk_soc";
2325 }
2326 $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
2327 $sql .= " AND ((sp.fk_user_creat = ".((int) $user->id)." AND sp.priv = 1) OR sp.priv = 0)"; // check if this is a private contact
2328 if ($socid > 0 || $socid == -1) {
2329 $sql .= " AND sp.fk_soc = " . ((int) $socid);
2330 }
2331 if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
2332 $sql .= " AND sp.statut <> 0";
2333 }
2334 // filter user access
2335 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
2336 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = sp.fk_soc AND sc.fk_user = ".(int) $user->id .")";
2337 }
2338 if ($user->socid > 0) {
2339 $sql .= " AND sp.fk_soc = ".((int) $user->socid);
2340 }
2341 if ($filter) {
2342 // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
2343 // if not, by testSqlAndScriptInject() only.
2344 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2345 $sql .= " AND (" . $sanitizedfilter . ")";
2346 }
2347 // Add where from hooks
2348 $parameters = array();
2349 $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
2350 $sql .= $hookmanager->resPrint;
2351 $sql .= " ORDER BY sp.lastname ASC";
2352
2353 dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
2354 $resql = $this->db->query($sql);
2355 if ($resql) {
2356 $num = $this->db->num_rows($resql);
2357
2358 if ($htmlname != 'none' && !$options_only) {
2359 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
2360 }
2361
2362 if ($showempty && !is_numeric($showempty)) {
2363 $textforempty = $showempty;
2364 $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
2365 } else {
2366 if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
2367 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
2368 }
2369 if ($showempty == 2) {
2370 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
2371 }
2372 }
2373
2374 $i = 0;
2375 if ($num) {
2376 include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2377 $contactstatic = new Contact($this->db);
2378
2379 while ($i < $num) {
2380 $obj = $this->db->fetch_object($resql);
2381
2382 // Set email (or phones) and town extended infos
2383 $extendedInfos = '';
2384 if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2385 $extendedInfos = array();
2386 $email = trim($obj->email);
2387 if (!empty($email)) {
2388 $extendedInfos[] = $email;
2389 } else {
2390 $phone = trim($obj->phone);
2391 $phone_perso = trim($obj->phone_perso);
2392 $phone_mobile = trim($obj->phone_mobile);
2393 if (!empty($phone)) {
2394 $extendedInfos[] = $phone;
2395 }
2396 if (!empty($phone_perso)) {
2397 $extendedInfos[] = $phone_perso;
2398 }
2399 if (!empty($phone_mobile)) {
2400 $extendedInfos[] = $phone_mobile;
2401 }
2402 }
2403 $contact_town = trim($obj->contact_town);
2404 $company_town = trim($obj->company_town);
2405 if (!empty($contact_town)) {
2406 $extendedInfos[] = $contact_town;
2407 } elseif (!empty($company_town)) {
2408 $extendedInfos[] = $company_town;
2409 }
2410 $extendedInfos = implode(' - ', $extendedInfos);
2411 if (!empty($extendedInfos)) {
2412 $extendedInfos = ' - ' . $extendedInfos;
2413 }
2414 }
2415
2416 $contactstatic->id = $obj->rowid;
2417 $contactstatic->lastname = $obj->lastname;
2418 $contactstatic->firstname = $obj->firstname;
2419 if ($obj->statut == 1) {
2420 $tmplabel = '';
2421 if ($htmlname != 'none') {
2422 $disabled = 0;
2423 if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
2424 $disabled = 1;
2425 }
2426 if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
2427 $disabled = 1;
2428 }
2429 if (!empty($selected) && in_array($obj->rowid, $selected)) {
2430 $out .= '<option value="' . $obj->rowid . '"';
2431 if ($disabled) {
2432 $out .= ' disabled';
2433 }
2434 $out .= ' selected>';
2435
2436 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2437 if ($showfunction && $obj->poste) {
2438 $tmplabel .= ' (' . $obj->poste . ')';
2439 }
2440 if (($showsoc > 0) && $obj->company) {
2441 $tmplabel .= ' - (' . $obj->company . ')';
2442 }
2443
2444 $out .= $tmplabel;
2445 $out .= '</option>';
2446 } else {
2447 $out .= '<option value="' . $obj->rowid . '"';
2448 if ($disabled) {
2449 $out .= ' disabled';
2450 }
2451 $out .= '>';
2452
2453 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2454 if ($showfunction && $obj->poste) {
2455 $tmplabel .= ' (' . $obj->poste . ')';
2456 }
2457 if (($showsoc > 0) && $obj->company) {
2458 $tmplabel .= ' - (' . $obj->company . ')';
2459 }
2460
2461 $out .= $tmplabel;
2462 $out .= '</option>';
2463 }
2464 } else {
2465 if (in_array($obj->rowid, $selected)) {
2466 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2467 if ($showfunction && $obj->poste) {
2468 $tmplabel .= ' (' . $obj->poste . ')';
2469 }
2470 if (($showsoc > 0) && $obj->company) {
2471 $tmplabel .= ' - (' . $obj->company . ')';
2472 }
2473
2474 $out .= $tmplabel;
2475 }
2476 }
2477
2478 if ($tmplabel != '') {
2479 array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
2480 }
2481 }
2482 $i++;
2483 }
2484 } else {
2485 $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
2486 $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
2487 $out .= $labeltoshow;
2488 $out .= '</option>';
2489 }
2490
2491 $parameters = array(
2492 'socid' => $socid,
2493 'htmlname' => $htmlname,
2494 'resql' => $resql,
2495 'out' => &$out,
2496 'showfunction' => $showfunction,
2497 'showsoc' => $showsoc,
2498 );
2499
2500 $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2501
2502 if ($htmlname != 'none' && !$options_only) {
2503 $out .= '</select>';
2504 }
2505
2506 if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
2507 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2508 $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
2509 }
2510
2511 $this->num = $num;
2512
2513 if ($options_only === 2) {
2514 // Return array of options
2515 return $outarray;
2516 } else {
2517 return $out;
2518 }
2519 } else {
2520 dol_print_error($this->db);
2521 return -1;
2522 }
2523 }
2524
2525
2526 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2527
2538 public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2539 {
2540 // phpcs:enable
2541 global $langs, $conf;
2542
2543 // On recherche les remises
2544 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2545 $sql .= " re.description, re.fk_facture_source";
2546 $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2547 $sql .= " WHERE re.fk_soc = " . (int) $socid;
2548 $sql .= " AND re.entity = " . ((int) $conf->entity);
2549 if ($filter) {
2550 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2551 $sql .= " AND " . $sanitizedfilter;
2552 }
2553 $sql .= " ORDER BY re.description ASC";
2554
2555 dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
2556 $resql = $this->db->query($sql);
2557 if ($resql) {
2558 print '<select id="select_' . $htmlname . '" class="flat maxwidth200onsmartphone" name="' . $htmlname . '">';
2559 $num = $this->db->num_rows($resql);
2560
2561 $qualifiedlines = $num;
2562
2563 $i = 0;
2564 if ($num) {
2565 print '<option value="0">&nbsp;</option>';
2566 while ($i < $num) {
2567 $obj = $this->db->fetch_object($resql);
2568 $desc = dol_trunc($obj->description, 40);
2569 if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
2570 $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
2571 }
2572 if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
2573 $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
2574 }
2575 if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
2576 $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
2577 }
2578 if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
2579 $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
2580 }
2581
2582 $selectstring = '';
2583 if ($selected > 0 && $selected == $obj->rowid) {
2584 $selectstring = ' selected';
2585 }
2586
2587 $disabled = '';
2588 if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2589 $qualifiedlines--;
2590 $disabled = ' disabled';
2591 }
2592
2593 if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2594 $tmpfac = new Facture($this->db);
2595 if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2596 $desc = $desc . ' - ' . $tmpfac->ref;
2597 }
2598 }
2599
2600 print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2601 $i++;
2602 }
2603 }
2604 print '</select>';
2605 print ajax_combobox('select_' . $htmlname);
2606
2607 return $qualifiedlines;
2608 } else {
2609 dol_print_error($this->db);
2610 return -1;
2611 }
2612 }
2613
2614
2615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2616
2632 public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2633 {
2634 // phpcs:enable
2635 print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2636 }
2637
2638 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2639
2664 public function select_dolusers($userselected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '', $maxlength = 0, $showstatus = 0, $morefilter = '', $showalso = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
2665 {
2666 // phpcs:enable
2667 global $conf, $user, $langs, $hookmanager;
2668 global $action;
2669
2670 // Convert $selected into an int (in case it is an object)
2671 if (is_object($userselected)) {
2672 $selected = (int) $userselected->id;
2673 } elseif (is_numeric($userselected)) {
2674 $selected = (int) $userselected;
2675 } elseif (is_array($userselected)) {
2676 $selected = $userselected;
2677 } else {
2678 $selected = -1;
2679 }
2680
2681 // If no preselected user defined, we take current user
2682 if ((is_numeric($selected) && ((int) $selected < -4 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2683 $selected = $user->id;
2684 }
2685
2686 // Convert selected int into an array
2687 if (!is_array($selected)) {
2688 if ($selected === -1 || $selected === '') {
2689 $selected = array();
2690 } else {
2691 $selected = array($selected);
2692 }
2693 }
2694
2695 // Exclude some users in $excludeUsers string
2696 $excludeUsers = null;
2697 if (is_array($exclude)) {
2698 $excludeUsers = implode(",", $exclude);
2699 }
2700
2701 // Include some users in $includeUsers string
2702 $includeUsers = null;
2703 $includeUsersArray = array();
2704 if (is_array($include)) {
2705 $includeUsersArray = $include;
2706 } elseif ($include == 'hierarchy') {
2707 // Build list includeUsersArray to have only hierarchy
2708 $includeUsersArray = $user->getAllChildIds(0);
2709 } elseif ($include == 'hierarchyme') {
2710 // Build list includeUsersArray to have only hierarchy and current user
2711 $includeUsersArray = $user->getAllChildIds(1);
2712 }
2713 // Get list of allowed users
2714 /* We do not limit list of users. Because we should limit this only for combo list into HR features where we may be allowed to
2715 * see all other users and element in other. For example in agenda, we can have permission to read all event of otherusers.
2716 * So we disable this.
2717 if (!$user->hasRight('user', 'user', 'lire')) {
2718 if (empty($includeUsersArray)) {
2719 $includeUsers = implode(",", $user->getAllChildIds(1));
2720 } else {
2721 $includeUsers = implode(",", array_intersect($includeUsersArray, $user->getAllChildIds(1)));
2722 }
2723 } else {
2724 $includeUsers = implode(",", $includeUsersArray);
2725 } */
2726 $includeUsers = implode(",", $includeUsersArray);
2727
2728 $num = 0;
2729
2730 $out = '';
2731 $outarray = array();
2732 $outarray2 = array();
2733
2734 // Do we want to show the label of entity into the combo list ?
2735 $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity) && !preg_match('/^search_/', $htmlname);
2736 $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2737
2738 // Forge request to select users
2739 $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.gender, u.photo";
2740 if ($showlabelofentity) {
2741 $sql .= ", e.label";
2742 }
2743 $sql .= " FROM " . $this->db->prefix() . "user as u";
2744 if ($showlabelofentity) {
2745 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2746 }
2747 // Condition here should be the same than into societe->getSalesRepresentatives().
2748 if ($userissuperadminentityone && $force_entity !== 'default') {
2749 if (!empty($force_entity)) {
2750 $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2751 } else {
2752 $sql .= " WHERE u.entity IS NOT NULL";
2753 }
2754 } else {
2755 if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2756 $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2757 } else {
2758 $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2759 }
2760 }
2761
2762 if (!empty($user->socid)) {
2763 $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2764 }
2765 if (is_array($exclude) && $excludeUsers) {
2766 $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2767 }
2768 if ($includeUsers) {
2769 $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2770 }
2771 if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2772 $sql .= " AND (u.statut <> 0";
2773 if (!empty($selected)) {
2774 $sql .= " OR u.rowid IN (".$this->db->sanitize(implode(',', $selected)).")"; // We must always keep the selected users to avoid to loose it/them when updating
2775 }
2776 $sql .= ")";
2777 }
2778 if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX')) {
2779 $sql .= " AND u.employee <> 0";
2780 }
2781 if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX')) {
2782 $sql .= " AND u.fk_soc IS NULL";
2783 }
2784 if (!empty($morefilter)) {
2785 $errormessage = '';
2786 $sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
2787 if ($errormessage) {
2788 $this->errors[] = $errormessage;
2789 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2790 if ($outputmode == 0) {
2791 return 'Error bad param $morefilter';
2792 } else {
2793 return array();
2794 }
2795 }
2796 }
2797
2798 //Add hook to filter on user (for example on usergroup define in custom modules)
2799 $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2800 if (!empty($reshook)) {
2801 $sql .= $hookmanager->resPrint;
2802 }
2803
2804 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2805 $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2806 } else {
2807 $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2808 }
2809
2810 dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2811
2812 $resql = $this->db->query($sql);
2813 if ($resql) {
2814 $num = $this->db->num_rows($resql);
2815 $i = 0;
2816 if ($num) {
2817 // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2818 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2819 if ($show_empty && !$multiple) {
2820 $textforempty = ' ';
2821 if (!empty($conf->use_javascript_ajax)) {
2822 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2823 }
2824 if (!is_numeric($show_empty)) {
2825 $textforempty = $show_empty;
2826 }
2827 $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2828
2829 $outarray[($show_empty < 0 ? $show_empty : -1)] = $textforempty;
2830 $outarray2[($show_empty < 0 ? $show_empty : -1)] = array(
2831 'id' => ($show_empty < 0 ? $show_empty : -1),
2832 'label' => $textforempty,
2833 'labelhtml' => $textforempty,
2834 'color' => '',
2835 'picto' => ''
2836 );
2837 }
2838 if ($showalso == 2 || $showalso == 3) {
2839 $out .= '<option value="-3"' . ((in_array(-3, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("MyTeam") . ' --</option>' . "\n";
2840
2841 $hasAtLeastOneSubordinate = (count($user->getAllChildIds(1)) > 1);
2842 if ($hasAtLeastOneSubordinate) {
2843 //$sql = "SELECT rowid FROM".MAIN_DB_PREFIX."user "
2844 $outarray[-3] = '-- ' . $langs->trans("MyTeam") . ' --';
2845 $outarray2[-3] = array(
2846 'id' => -3,
2847 'label' => '-- ' . $langs->trans("MyTeam") . ' --',
2848 'labelhtml' => '-- ' . $langs->trans("MyTeam") . ' --',
2849 'color' => '',
2850 'picto' => ''
2851 );
2852 }
2853 }
2854 if ($showalso == 1 || $showalso == 3) {
2855 $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2856
2857 $outarray[-2] = '-- ' . $langs->trans("Everybody") . ' --';
2858 $outarray2[-2] = array(
2859 'id' => -2,
2860 'label' => '-- ' . $langs->trans("Everybody") . ' --',
2861 'labelhtml' => '-- ' . $langs->trans("Everybody") . ' --',
2862 'color' => '',
2863 'picto' => ''
2864 );
2865 }
2866 if ($showalso == 4) {
2867 $out .= '<option value="-4"' . ((in_array(-4, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("AllProjectContacts") . ' --</option>' . "\n";
2868
2869 $outarray[-4] = '-- ' . $langs->trans("AllProjectContacts") . ' --';
2870 $outarray2[-4] = array(
2871 'id' => -4,
2872 'label' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2873 'labelhtml' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2874 'color' => '',
2875 'picto' => ''
2876 );
2877 }
2878
2879 $userstatic = new User($this->db);
2880
2881 while ($i < $num) {
2882 $obj = $this->db->fetch_object($resql);
2883
2884 $userstatic->id = $obj->rowid;
2885 $userstatic->lastname = $obj->lastname;
2886 $userstatic->firstname = $obj->firstname;
2887 $userstatic->photo = $obj->photo;
2888 $userstatic->status = $obj->status;
2889 $userstatic->entity = $obj->entity;
2890 $userstatic->admin = $obj->admin;
2891 $userstatic->gender = $obj->gender;
2892
2893 $disableline = '';
2894 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2895 $disableline = ($enableonlytext ? $enableonlytext : '1');
2896 }
2897
2898 $labeltoshow = '';
2899 $labeltoshowhtml = '';
2900
2901 // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2902 $fullNameMode = 0;
2903 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2904 $fullNameMode = 1; //Firstname+lastname
2905 }
2906 $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2907 $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2908 if (empty($obj->firstname) && empty($obj->lastname)) {
2909 $labeltoshow .= $obj->login;
2910 $labeltoshowhtml .= $obj->login;
2911 }
2912
2913 // Complete name with a more info string like: ' (info1 - info2 - ...)'
2914 $moreinfo = '';
2915 $moreinfohtml = '';
2916 if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2917 $moreinfo .= ($moreinfo ? ' - ' : ' (');
2918 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2919 $moreinfo .= $obj->login;
2920 $moreinfohtml .= $obj->login;
2921 }
2922 if ($showstatus >= 0) {
2923 if ($obj->status == 1 && $showstatus == 1) {
2924 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2925 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2926 }
2927 if ($obj->status == 0 && $showstatus == 1) {
2928 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2929 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2930 }
2931 }
2932 if ($showlabelofentity) {
2933 if (empty($obj->entity)) {
2934 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2935 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2936 } else {
2937 if ($obj->entity != $conf->entity) {
2938 $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2939 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2940 }
2941 }
2942 }
2943 $moreinfo .= (!empty($moreinfo) ? ')' : '');
2944 $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2945 if (!empty($disableline) && $disableline != '1') {
2946 // Add text from $enableonlytext parameter
2947 $moreinfo .= ' - ' . $disableline;
2948 $moreinfohtml .= ' - ' . $disableline;
2949 }
2950 $labeltoshow .= $moreinfo;
2951 $labeltoshowhtml .= $moreinfohtml;
2952
2953 $out .= '<option value="' . $obj->rowid . '"';
2954 if (!empty($disableline)) {
2955 $out .= ' disabled';
2956 }
2957 if (in_array($obj->rowid, $selected)) {
2958 $out .= ' selected';
2959 }
2960 $out .= ' data-html="';
2961
2962 $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2963 if ($showstatus >= 0 && $obj->status == 0) {
2964 $outhtml .= '<strike class="opacitymediumxxx">';
2965 }
2966 $outhtml .= $labeltoshowhtml;
2967 if ($showstatus >= 0 && $obj->status == 0) {
2968 $outhtml .= '</strike>';
2969 }
2970 $labeltoshowhtml = $outhtml;
2971
2972 $out .= dol_escape_htmltag($outhtml);
2973 $out .= '">';
2974 $out .= $labeltoshow;
2975 $out .= '</option>';
2976
2977 $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2978 $outarray2[$userstatic->id] = array(
2979 'id' => $userstatic->id,
2980 'label' => $labeltoshow,
2981 'labelhtml' => $labeltoshowhtml,
2982 'color' => '',
2983 'picto' => ''
2984 );
2985
2986 $i++;
2987 }
2988 } else {
2989 $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2990 $out .= '<option value="">' . $langs->trans("None") . '</option>';
2991 }
2992 $out .= '</select>';
2993
2994 if ($num && !$forcecombo) {
2995 // Enhance with select2
2996 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2997 $out .= ajax_combobox($htmlname);
2998 }
2999 } else {
3000 dol_print_error($this->db);
3001 }
3002
3003 $this->num = $num;
3004
3005 if ($outputmode == 2) {
3006 return $outarray2;
3007 } elseif ($outputmode) {
3008 return $outarray;
3009 }
3010
3011 return $out;
3012 }
3013
3014
3015 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3039 public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array(), $canremoveowner = 1)
3040 {
3041 // phpcs:enable
3042 global $langs, $user;
3043
3044 $userstatic = new User($this->db);
3045 $out = '';
3046
3047 // The list of selected users is provided by the caller through $listofuserid (owner first).
3048 // Fall back to the legacy global $_SESSION['assignedtouser'] only when no list is provided
3049 // (comm/action/card.php now scopes that session bucket per event id and no longer feeds this key).
3050 if (!empty($listofuserid)) {
3051 $assignedtouser = $listofuserid;
3052 } elseif (!empty($_SESSION['assignedtouser'])) {
3053 $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
3054 if (!is_array($assignedtouser)) {
3055 $assignedtouser = array();
3056 }
3057 } else {
3058 $assignedtouser = array();
3059 }
3060 $nbassignetouser = count($assignedtouser);
3061
3062 //if ($nbassignetouser && $action != 'view') $out .= '<br>';
3063 if ($nbassignetouser) {
3064 $out .= '<ul class="attendees">';
3065 }
3066 $i = 0;
3067 $ownerid = 0;
3068 foreach ($assignedtouser as $key => $value) {
3069 if ($value['id'] == $ownerid) {
3070 continue;
3071 }
3072
3073 $out .= '<li>';
3074
3075 $userstatic->fetch($value['id']);
3076 $out .= $userstatic->getNomUrl(-4);
3077
3078 if ($i == 0) {
3079 $ownerid = $value['id'];
3080 $out .= ' (' . $langs->trans("Owner") . ')';
3081 }
3082 // Add picto to delete owner/assignee
3083 if ($nbassignetouser > 1 && $action != 'view') {
3084 $canremoveassignee = 1;
3085 if ($i == 0) {
3086 // We are on the owner of the event
3087 if (!$canremoveowner) {
3088 $canremoveassignee = 0;
3089 }
3090 if (!$user->hasRight('agenda', 'allactions', 'create')) {
3091 $canremoveassignee = 0; // Can't remove the owner
3092 }
3093 } else {
3094 // We are not on the owner of the event but on a secondary assignee
3095 }
3096 if ($canremoveassignee) {
3097 // If user has all permission, he should be ableto remove a assignee.
3098 // If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
3099 $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="noborderfocus removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
3100 }
3101 }
3102 // Show my availability
3103 if ($showproperties) {
3104 if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
3105 $out .= '<div class="myavailability inline-block">';
3106 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3107 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ':</span>';
3108 $out .= '</span>';
3109 $out .= ' <input title="'.$langs->trans("Availability").'" id="transparency" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofuserid[$ownerid]['transparency'] ? ' checked' : '') . '><label for="transparency">' . $langs->trans("Busy") . '</label>';
3110 $out .= '</div>';
3111 }
3112 }
3113 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3114 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3115
3116 $out .= '</li>';
3117 $i++;
3118 }
3119 if ($nbassignetouser) {
3120 $out .= '</ul>';
3121 }
3122
3123 // Method with no ajax
3124 if ($action != 'view') {
3125 // Section to add another user
3126 $out .= '<div class="divadduser'.$htmlname.'">';
3127 $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
3128 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3129 $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
3130 $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
3131 $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
3132 $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
3133 $out .= '});';
3134 $out .= '})</script>';
3135 $out .= img_picto('', 'user', 'class="pictofixedwidth"');
3136 $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter, 0, '', 'minwidth200');
3137 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3138 $out .= $langs->trans("Add").'</button>';
3139 $out .= '</div>';
3140 //$out .= '<br>';
3141 }
3142
3143 return $out;
3144 }
3145
3146 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3166 public function select_dolresources_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofresourceid = array())
3167 {
3168 // phpcs:enable
3169 global $langs;
3170
3171 require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php';
3172 require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php';
3173 $formresources = new FormResource($this->db);
3174 $resourcestatic = new Dolresource($this->db);
3175
3176 $out = '';
3177 // The list of selected resources is provided by the caller through $listofresourceid.
3178 // Fall back to the legacy global $_SESSION['assignedtoresource'] only when no list is provided
3179 // (comm/action/card.php now scopes that session bucket per event id and no longer feeds this key).
3180 if (!empty($listofresourceid)) {
3181 $assignedtoresource = $listofresourceid;
3182 } elseif (!empty($_SESSION['assignedtoresource'])) {
3183 $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
3184 if (!is_array($assignedtoresource)) {
3185 $assignedtoresource = array();
3186 }
3187 } else {
3188 $assignedtoresource = array();
3189 }
3190 $nbassignetoresource = count($assignedtoresource);
3191
3192 //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
3193 if ($nbassignetoresource) {
3194 $out .= '<ul class="attendees">';
3195 }
3196 $i = 0;
3197
3198 foreach ($assignedtoresource as $key => $value) {
3199 $out .= '<li>';
3200 $resourcestatic->fetch($value['id']);
3201 $out .= $resourcestatic->getNomUrl(-1);
3202 if ($nbassignetoresource >= 1 && $action != 'view') {
3203 $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 . '">';
3204 }
3205 // Show my availability
3206 if ($showproperties) {
3207 if (is_array($listofresourceid) && count($listofresourceid)) {
3208 $out .= '<div class="myavailability inline-block">';
3209 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3210 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ': </span>';
3211 $out .= '</span>';
3212 $out .= ' <input title="'.$langs->trans("Availability").'" id="transparencyresource'.$value['id'].'" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . (!empty($listofresourceid[$value['id']]['transparency']) ? ' checked' : '') . '><label for="transparencyresource'.$value['id'].'">' . $langs->trans("Busy") . '</label>';
3213 $out .= '</div>';
3214 }
3215 }
3216 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3217 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3218
3219 $out .= '</li>';
3220 $i++;
3221 }
3222 if ($nbassignetoresource) {
3223 $out .= '</ul>';
3224 }
3225
3226 // Method with no ajax
3227 if ($action != 'view') {
3228 $out .= '<input type="hidden" class="removedassignedresourcehidden" name="removedassignedresource" value="">';
3229 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3230 $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
3231 $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
3232 $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
3233 $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
3234 $out .= '});';
3235 $out .= '})</script>';
3236
3237 $events = array();
3238 if ($nbassignetoresource) {
3239 //$out .= img_picto('', 'add', 'class="pictofixedwidth"');
3240 } else {
3241 $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
3242 }
3243 $out .= $formresources->select_resource_list(0, $htmlname, '', 1, 1, 0, $events, '', 2, 0, 'minwidth200');
3244 //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
3245 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3246 $out .= $langs->trans("Add");
3247 $out .= '</button>';
3248 $out .= '<br>';
3249 }
3250
3251 return $out;
3252 }
3253
3254 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3255
3285 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)
3286 {
3287 // phpcs:enable
3288 global $langs, $conf;
3289
3290 $out = '';
3291
3292 // check parameters
3293 $price_level = (!empty($price_level) ? $price_level : 0);
3294 if (is_null($ajaxoptions)) {
3295 $ajaxoptions = array();
3296 }
3297
3298 if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
3299 if (isModEnabled("product") && !isModEnabled('service')) {
3300 $filtertype = '0';
3301 } elseif (!isModEnabled('product') && isModEnabled("service")) {
3302 $filtertype = '1';
3303 }
3304 }
3305
3306 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3307 $placeholder = (is_numeric($showempty) ? '' : 'placeholder="'.dolPrintHTML($showempty).'"');
3308
3309 if ($selected && empty($selected_input_value)) {
3310 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3311 $producttmpselect = new Product($this->db);
3312 $producttmpselect->fetch($selected);
3313 $selected_input_value = $producttmpselect->ref;
3314 unset($producttmpselect);
3315 }
3316 // handle case where product or service module is disabled + no filter specified
3317 if ($filtertype == '') {
3318 if (!isModEnabled('product')) { // when product module is disabled, show services only
3319 $filtertype = 1;
3320 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3321 $filtertype = 0;
3322 }
3323 }
3324 // mode=1 means customers products
3325 $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;
3326 if ((int) $warehouseId > 0) {
3327 $urloption .= '&warehouseid=' . (int) $warehouseId;
3328 }
3329
3330 if (isModEnabled('variants') && is_array($selected_combinations)) {
3331 // Code to automatically insert with javascript the select of attributes under the select of product
3332 // when a parent of variant has been selected.
3333 // Note: Samecode than for product input using select
3334 $htmltag = 'input';
3335 $out .= '
3336 <!-- script to auto show attributes select tags if a variant was selected -->
3337 <script nonce="' . getNonce() . '">
3338 // auto show attributes fields
3339 selected = ' . json_encode($selected_combinations) . ';
3340 combvalues = {};
3341
3342 jQuery(document).ready(function () {
3343
3344 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3345 if (jQuery(this).val() == \'free\') {
3346 jQuery(\'div#attributes_box\').empty();
3347 }
3348 });
3349
3350 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3351
3352 if (!jQuery(this).val()) {
3353 jQuery(\'div#attributes_box\').empty();
3354 return;
3355 }
3356
3357 console.log("A change has started. We get variants fields to inject html select");
3358
3359 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3360 id: jQuery(this).val()
3361 }, function (data) {
3362 jQuery(\'div#attributes_box\').empty();
3363
3364 jQuery.each(data, function (key, val) {
3365
3366 combvalues[val.id] = val.values;
3367
3368 var span = jQuery(document.createElement(\'div\')).css({
3369 \'display\': \'table-row\'
3370 });
3371
3372 span.append(
3373 jQuery(document.createElement(\'div\')).text(val.label).css({
3374 \'font-weight\': \'bold\',
3375 \'display\': \'table-cell\'
3376 })
3377 );
3378
3379 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3380 \'margin-left\': \'15px\',
3381 \'white-space\': \'pre\'
3382 }).append(
3383 jQuery(document.createElement(\'option\')).val(\'\')
3384 );
3385
3386 jQuery.each(combvalues[val.id], function (key, val) {
3387 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3388
3389 if (selected[val.fk_product_attribute] == val.id) {
3390 tag.attr(\'selected\', \'selected\');
3391 }
3392
3393 html.append(tag);
3394 });
3395
3396 span.append(html);
3397 jQuery(\'div#attributes_box\').append(span);
3398 });
3399 })
3400 });
3401
3402 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3403 });
3404 </script>
3405 ';
3406 }
3407
3408 if (empty($hidelabel)) {
3409 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3410 } elseif ($hidelabel > 1) {
3411 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3412 if ($hidelabel == 2) {
3413 $out .= img_picto($langs->trans("Search"), 'search');
3414 }
3415 }
3416
3417 $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" />';
3418 if ($hidelabel == 3) {
3419 $out .= img_picto($langs->trans("Search"), 'search');
3420 }
3421
3422 $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);
3423 } else {
3424 $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase, $warehouseId);
3425
3426 if (isModEnabled('variants') && is_array($selected_combinations)) {
3427 // Code to automatically insert with javascript the select of attributes under the select of product
3428 // when a parent of variant has been selected.
3429 // Note: Samecode than for product input using Ajax
3430 $htmltag = 'select';
3431 $out .= '
3432 <!-- script to auto show attributes select tags if a variant was selected -->
3433 <script nonce="' . getNonce() . '">
3434 // auto show attributes fields
3435 selected = ' . json_encode($selected_combinations) . ';
3436 combvalues = {};
3437
3438 jQuery(document).ready(function () {
3439
3440 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3441 if (jQuery(this).val() == \'free\') {
3442 jQuery(\'div#attributes_box\').empty();
3443 }
3444 });
3445
3446 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3447
3448 if (!jQuery(this).val()) {
3449 jQuery(\'div#attributes_box\').empty();
3450 return;
3451 }
3452
3453 console.log("A change has started. We get variants fields to inject html select");
3454
3455 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3456 id: jQuery(this).val()
3457 }, function (data) {
3458 jQuery(\'div#attributes_box\').empty();
3459
3460 jQuery.each(data, function (key, val) {
3461
3462 combvalues[val.id] = val.values;
3463
3464 var span = jQuery(document.createElement(\'div\')).css({
3465 \'display\': \'table-row\'
3466 });
3467
3468 span.append(
3469 jQuery(document.createElement(\'div\')).text(val.label).css({
3470 \'font-weight\': \'bold\',
3471 \'display\': \'table-cell\'
3472 })
3473 );
3474
3475 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3476 \'margin-left\': \'15px\',
3477 \'white-space\': \'pre\'
3478 }).append(
3479 jQuery(document.createElement(\'option\')).val(\'\')
3480 );
3481
3482 jQuery.each(combvalues[val.id], function (key, val) {
3483 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3484
3485 if (selected[val.fk_product_attribute] == val.id) {
3486 tag.attr(\'selected\', \'selected\');
3487 }
3488
3489 html.append(tag);
3490 });
3491
3492 span.append(html);
3493 jQuery(\'div#attributes_box\').append(span);
3494 });
3495 })
3496 });
3497
3498 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3499 });
3500 </script>
3501 ';
3502 }
3503 }
3504
3505 if (empty($nooutput)) {
3506 print $out;
3507 } else {
3508 return $out;
3509 }
3510 }
3511
3512 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3513
3529 public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
3530 {
3531 // phpcs:enable
3532
3533 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3534
3535 $error = 0;
3536 $out = '';
3537
3538 if (!$forcecombo) {
3539 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3540 $events = array();
3541 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("BOM_USE_SEARCH_TO_SELECT"));
3542 }
3543
3544 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3545
3546 $sql = 'SELECT b.rowid, b.ref, b.label as bomLabel, p.label as productLabel';
3547 $sql .= ' FROM ' . $this->db->prefix() . 'bom_bom as b';
3548 $sql .= ' INNER JOIN ' . $this->db->prefix() . 'product as p ON b.fk_product = p.rowid';
3549 $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
3550 if (!empty($status)) {
3551 $sql .= ' AND status = ' . (int) $status;
3552 }
3553 if (!empty($type)) {
3554 $sql .= ' AND bomtype = ' . (int) $type;
3555 }
3556 if (!empty($TProducts)) {
3557 $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
3558 }
3559 if (!empty($limit)) {
3560 $sql .= ' LIMIT ' . (int) $limit;
3561 }
3562 $resql = $this->db->query($sql);
3563 if ($resql) {
3564 if ($showempty) {
3565 $out .= '<option value="-1"';
3566 if (empty($selected)) {
3567 $out .= ' selected';
3568 }
3569 $out .= '>&nbsp;</option>';
3570 }
3571 while ($obj = $this->db->fetch_object($resql)) {
3572 $out .= '<option value="' . $obj->rowid . '"';
3573 if ($obj->rowid == $selected) {
3574 $out .= 'selected';
3575 }
3576 $out .= '>' . $obj->ref . ' - ' . $obj->productLabel . ' - ' . $obj->bomLabel . '</option>';
3577 }
3578 } else {
3579 $error++;
3580 dol_print_error($this->db);
3581 }
3582 $out .= '</select>';
3583 if (empty($nooutput)) {
3584 print $out;
3585 } else {
3586 return $out;
3587 }
3588 }
3589
3590 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3591
3618 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)
3619 {
3620 // phpcs:enable
3621 global $langs;
3622 global $hookmanager;
3623
3624 $out = '';
3625 $outarray = array();
3626
3627 // Units
3628 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3629 $langs->load('other');
3630 }
3631
3632 $warehouseStatusArray = array();
3633 if (!empty($warehouseStatus)) {
3634 require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
3635 if (preg_match('/warehouseclosed/', $warehouseStatus)) {
3636 $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
3637 }
3638 if (preg_match('/warehouseopen/', $warehouseStatus)) {
3639 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
3640 }
3641 if (preg_match('/warehouseinternal/', $warehouseStatus)) {
3642 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
3643 }
3644 }
3645
3646 $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";
3647 if (count($warehouseStatusArray)) {
3648 $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
3649 } else {
3650 $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", '0', "p.stock") . " AS stock";
3651 }
3652
3653 $sql = "SELECT ";
3654
3655 // Add select from hooks
3656 $parameters = array();
3657 $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
3658 if (empty($reshook)) {
3659 $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
3660 } else {
3661 $sql .= $hookmanager->resPrint;
3662 }
3663
3664 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3665 // Take randomly the first category of product to allow a sort on it. Bugged feature !
3666 $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
3667 FROM " . $this->db->prefix() . "categorie_product
3668 WHERE " . $this->db->prefix() . "categorie_product.fk_product = p.rowid
3669 LIMIT 1
3670 ) AS categorie_product_id";
3671 }
3672
3673 // Price by customer
3674 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3675 $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
3676 $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';
3677 $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref, custdiscount_percent";
3678 }
3679 // Units
3680 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3681 $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";
3682 $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';
3683 }
3684
3685 // Multilang : we add translation
3686 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3687 $sql .= ", pl.label as label_translated";
3688 $sql .= ", pl.description as description_translated";
3689 $selectFields .= ", label_translated";
3690 $selectFields .= ", description_translated";
3691 }
3692 // Price by quantity
3693 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3694 $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
3695 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3696 $sql .= " AND price_level = " . ((int) $price_level);
3697 }
3698 $sql .= " ORDER BY date_price";
3699 $sql .= " DESC LIMIT 1) as price_rowid";
3700 $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
3701 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3702 $sql .= " AND price_level = " . ((int) $price_level);
3703 }
3704 $sql .= " ORDER BY date_price";
3705 $sql .= " DESC LIMIT 1) as price_by_qty";
3706 $selectFields .= ", price_rowid, price_by_qty";
3707 }
3708
3709 //$sqlfields = $sql; // $sql fields to remove for count total
3710
3711 $sql .= " FROM ".$this->db->prefix()."product as p";
3712
3713 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
3714 $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
3715 }
3716
3717 // Add from (left join) from hooks
3718 $parameters = array(
3719 'socid' => $socid,
3720 );
3721 $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
3722 $sql .= $hookmanager->resPrint;
3723
3724 if (count($warehouseStatusArray)) {
3725 // Return line if product is inside the selected stock. If not, e.* and p.* will be null so we will count 0.
3726 // Replace this with a AND EXISTS ? Not possible as we need the ps.reel field for the SUM or 0 if no link.
3727 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps ON ps.fk_product = p.rowid";
3728 $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e ON ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
3729 $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')';
3730 }
3731
3732 // Price by customer (Add field pcp for the older price for couple product/thirdparty.
3733 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3734 $now = dol_now();
3735 $sql .= " LEFT JOIN (";
3736 $sql .= " SELECT pcp1.*";
3737 $sql .= " FROM " . $this->db->prefix() . "product_customer_price AS pcp1";
3738 $sql .= " LEFT JOIN (";
3739 $sql .= " SELECT fk_soc, fk_product, MIN(date_begin) AS date_begin";
3740 $sql .= " FROM " . $this->db->prefix() . "product_customer_price";
3741 $sql .= " WHERE fk_soc = " . ((int) $socid);
3742 $sql .= " AND date_begin <= '" . $this->db->idate($now) . "'";
3743 $sql .= " AND (date_end IS NULL OR '" . $this->db->idate($now) . "' <= date_end)";
3744 $sql .= " GROUP BY fk_soc, fk_product";
3745 $sql .= " ) AS pcp2 ON pcp1.fk_soc = pcp2.fk_soc AND pcp1.fk_product = pcp2.fk_product AND pcp1.date_begin = pcp2.date_begin";
3746 $sql .= " WHERE pcp2.fk_soc IS NOT NULL";
3747 $sql .= " ) AS pcp ON pcp.fk_soc = " . ((int) $socid) . " AND pcp.fk_product = p.rowid";
3748 }
3749 // Units : we add unit properties with a link on the primary key of unit
3750 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3751 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units as u ON u.rowid = p.fk_unit";
3752 }
3753 // Multilang : we add translation fields with a link on unique key fk_product/lang.
3754 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3755 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid";
3756 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3757 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3758 $soc = new Societe($this->db);
3759 $result = $soc->fetch($socid);
3760 if ($result > 0 && !empty($soc->default_lang)) {
3761 $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3762 } else {
3763 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3764 }
3765 } else {
3766 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3767 }
3768 }
3769
3770 // Add WHERE conditions
3771 $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3772 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3773 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD_BUT_ALLOW_SEARCH_IN_EAN13')) {
3774 if (strlen($filterkey) != 13) {
3775 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3776 }
3777 } else {
3778 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3779 }
3780 }
3781 if ($finished == 0) {
3782 $sql .= " AND p.finished = " . ((int) $finished);
3783 } elseif ($finished == 1) {
3784 $sql .= " AND p.finished = ".((int) $finished);
3785 }
3786 if ($status >= 0) {
3787 $sql .= " AND p.tosell = ".((int) $status);
3788 }
3789 if ($status_purchase >= 0) {
3790 $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3791 }
3792 // Filter by product type
3793 if (strval($filtertype) != '') {
3794 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3795 } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3796 $sql .= " AND p.fk_product_type = 1";
3797 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3798 $sql .= " AND p.fk_product_type = 0";
3799 }
3800
3801 if ((int) $warehouseId > 0) {
3802 $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)";
3803 }
3804
3805 // Add where from hooks
3806 $parameters = array(
3807 'filterkey' => &$filterkey,
3808 'socid' => $socid,
3809 );
3810 $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3811 $sql .= $hookmanager->resPrint;
3812 // Add criteria on ref/label
3813 if ($filterkey != '') {
3814 $sqlSupplierSearch = '';
3815
3816 $sql .= ' AND (';
3817 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3818 // For natural search
3819 $search_crit = explode(' ', $filterkey);
3820 $i = 0;
3821 if (count($search_crit) > 1) {
3822 $sql .= "(";
3823 }
3824 foreach ($search_crit as $crit) {
3825 if ($i > 0) {
3826 $sql .= " AND ";
3827 }
3828 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3829 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3830 $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3831 }
3832 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3833 $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3834 }
3835 if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3836 $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3837 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3838 $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3839 }
3840 }
3841
3842 // include search in supplier ref
3843 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3844 $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' AND ' : '';
3845 $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3846 }
3847 $sql .= ")";
3848 $i++;
3849 }
3850 if (count($search_crit) > 1) {
3851 $sql .= ")";
3852 }
3853 if (isModEnabled('barcode')) {
3854 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3855 }
3856
3857 // include search in supplier ref
3858 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3859 $sql .= " OR EXISTS (SELECT pfp.fk_product FROM " . $this->db->prefix() . "product_fournisseur_price as pfp WHERE p.rowid = pfp.fk_product";
3860 $sql .= " AND (";
3861 $sql .= $sqlSupplierSearch;
3862 $sql .= "))";
3863 }
3864
3865 $sql .= ')';
3866 }
3867 if (count($warehouseStatusArray)) {
3868 $sql .= " GROUP BY " . $this->db->sanitize($selectFields, 0, 0, 1); // To have the SUM on ps.reel working in the select.
3869 }
3870
3871 // Sort by category
3872 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3873 $sql .= " ORDER BY categorie_product_id ".(getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1 ? "ASC" : "DESC");
3874 } else {
3875 $sql .= $this->db->order("p.ref");
3876 }
3877
3878 $limit = getDolGlobalInt('SEARCH_LIMIT_AJAX') ?: $limit; // SEARCH_LIMIT_AJAX is a hidden option that has priority on visible option PRODUIT_LIMIT_SIZE if set.
3879 $sql .= $this->db->plimit($limit, 0);
3880
3881 /* The fast and low memory method to get and count full list converts the sql into a sql count */
3882 /*
3883 $nbtotalofrecords = 0;
3884 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
3885 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
3886
3887 $resql = $this->db->query($sqlforcount);
3888 if ($resql) {
3889 $objforcount = $this->db->fetch_object($resql);
3890 $nbtotalofrecords = $objforcount->nbtotalofrecords;
3891 } else {
3892 dol_print_error($this->db);
3893 }
3894 */
3895
3896 // Build output string
3897 dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
3898
3899 // If we have no $limit parameter, this request may hang dur to high number of lines returned.
3900 // This should not happen because this method should not be called directly, iIt is called by select_produit() that always add a $limit parameter.
3901 $result = $this->db->query($sql);
3902
3903 if ($result) {
3904 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3905 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3906 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3907
3908 $num = $this->db->num_rows($result);
3909
3910 $events = array();
3911
3912 if (!$forcecombo) {
3913 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3914 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3915 }
3916
3917 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3918
3919 $textifempty = '';
3920 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3921 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3922 if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3923 if ($showempty && !is_numeric($showempty)) {
3924 $textifempty = $langs->trans($showempty);
3925 } else {
3926 $textifempty .= $langs->trans("All");
3927 }
3928 } else {
3929 if ($showempty && !is_numeric($showempty)) {
3930 $textifempty = $langs->trans($showempty);
3931 }
3932 }
3933 if ($showempty) {
3934 $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3935 }
3936
3937 $i = 0;
3938 while ($num && $i < $num) {
3939 $opt = '';
3940 $optJson = array();
3941 $objp = $this->db->fetch_object($result);
3942
3943 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
3944 $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3945 $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3946 $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3947 $sql .= " ORDER BY quantity ASC";
3948
3949 dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3950 $result2 = $this->db->query($sql);
3951 if ($result2) {
3952 $nb_prices = $this->db->num_rows($result2);
3953 $j = 0;
3954 while ($nb_prices && $j < $nb_prices) {
3955 $objp2 = $this->db->fetch_object($result2);
3956
3957 $objp->price_by_qty_rowid = $objp2->rowid;
3958 $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3959 $objp->price_by_qty_quantity = $objp2->quantity;
3960 $objp->price_by_qty_unitprice = $objp2->unitprice;
3961 $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3962 // For backward compatibility
3963 $objp->quantity = $objp2->quantity;
3964 $objp->price = $objp2->price;
3965 $objp->unitprice = $objp2->unitprice;
3966 $objp->remise_percent = $objp2->remise_percent;
3967
3968 //$objp->tva_tx is not overwritten by $objp2 value
3969 //$objp->default_vat_code is not overwritten by $objp2 value
3970
3971 $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3972 '@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';
3973 $j++;
3974
3975 // Add new entry
3976 // "key" value of json key array is used by jQuery automatically as selected value
3977 // "label" value of json key array is used by jQuery automatically as text for combo box
3978 $out .= $opt;
3979 array_push($outarray, $optJson);
3980 }
3981 }
3982 } else {
3983 if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3984 $price_product = new Product($this->db);
3985 $price_product->fetch($objp->rowid, '', '', '1');
3986
3987 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3988 $priceparser = new PriceParser($this->db);
3989 $price_result = $priceparser->parseProduct($price_product);
3990 if ($price_result >= 0) {
3991 $objp->price = $price_result;
3992 $objp->unitprice = $price_result;
3993 //Calculate the VAT
3994 $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3995 $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3996 }
3997 }
3998 if (getDolGlobalInt('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES') && !empty($objp->custprice)) {
3999 $price_level = '';
4000 }
4001 $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
4002 // Add new entry
4003 // "key" value of json key array is used by jQuery automatically as selected value
4004 // "label" value of json key array is used by jQuery automatically as text for combo box
4005 $out .= $opt;
4006 array_push($outarray, $optJson);
4007 }
4008
4009 $i++;
4010 }
4011
4012 $out .= '</select>';
4013
4014 $this->db->free($result);
4015
4016 if (empty($outputmode)) {
4017 return $out;
4018 }
4019
4020 return $outarray;
4021 } else {
4022 dol_print_error($this->db);
4023 }
4024
4025 return '';
4026 }
4027
4043 protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
4044 {
4045 global $langs, $conf, $user;
4046 global $hookmanager;
4047
4048 $outkey = '';
4049 $outval = '';
4050 $outref = '';
4051 $outlabel = '';
4052 $outlabel_translated = '';
4053 $outdesc = '';
4054 $outdesc_translated = '';
4055 $outbarcode = '';
4056 $outorigin = '';
4057 $outtype = '';
4058 $outprice_ht = '';
4059 $outprice_ttc = '';
4060 $outpricebasetype = '';
4061 $outtva_tx = '';
4062 $outdefault_vat_code = '';
4063 $outqty = 1;
4064 $outdiscount = '0';
4065
4066 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4067
4068 $productlabel = $objp->label;
4069 if (!empty($objp->label_translated)) {
4070 $productlabel = $objp->label_translated;
4071 }
4072 $label = $productlabel;
4073 if (!empty($filterkey) && $filterkey != '') {
4074 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4075 }
4076
4077 $outkey = $objp->rowid;
4078 $outref = $objp->ref;
4079 $outrefcust = empty($objp->custref) ? '' : $objp->custref;
4080 $outlabel = $objp->label;
4081 $outdesc = $objp->description;
4082 if (getDolGlobalInt('MAIN_MULTILANGS')) {
4083 $outlabel_translated = $objp->label_translated;
4084 $outdesc_translated = $objp->description_translated;
4085 }
4086 $outbarcode = $objp->barcode;
4087 $outorigin = $objp->fk_country;
4088 $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
4089
4090 $outtype = $objp->fk_product_type;
4091 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4092 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4093
4094 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4095 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
4096 }
4097
4098 // Units
4099 $outvalUnits = '';
4100 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4101 if (!empty($objp->unit_short)) {
4102 $outvalUnits .= ' - ' . $objp->unit_short;
4103 }
4104 }
4105 if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
4106 if (!empty($objp->weight) && $objp->weight_units !== null) {
4107 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4108 $outvalUnits .= ' - ' . $unitToShow;
4109 }
4110 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4111 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4112 $outvalUnits .= ' - ' . $unitToShow;
4113 }
4114 if (!empty($objp->surface) && $objp->surface_units !== null) {
4115 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4116 $outvalUnits .= ' - ' . $unitToShow;
4117 }
4118 if (!empty($objp->volume) && $objp->volume_units !== null) {
4119 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4120 $outvalUnits .= ' - ' . $unitToShow;
4121 }
4122 }
4123 if ($outdurationvalue && $outdurationunit) {
4124 $da = array(
4125 'h' => $langs->trans('Hour'),
4126 'd' => $langs->trans('Day'),
4127 'w' => $langs->trans('Week'),
4128 'm' => $langs->trans('Month'),
4129 'y' => $langs->trans('Year')
4130 );
4131 if (isset($da[$outdurationunit])) {
4132 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4133 }
4134 }
4135
4136 // Set stocktag (stock too low or not or unknown)
4137 $stocktag = 0;
4138 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4139 if ($user->hasRight('stock', 'lire')) {
4140 if ($objp->stock > 0) {
4141 $stocktag = 1;
4142 } elseif ($objp->stock <= 0) {
4143 $stocktag = -1;
4144 }
4145 }
4146 }
4147
4148 // Set full plain label for the native <option> text. Select2 uses this text
4149 // as its search corpus, while data-html below keeps the visible label short.
4150 $labeltosearch = '';
4151 $labeltosearch .= $objp->ref;
4152 if (!empty($objp->custref)) {
4153 $labeltosearch .= ' (' . $objp->custref . ')';
4154 }
4155 if ($outbarcode) {
4156 $labeltosearch .= ' (' . $outbarcode . ')';
4157 }
4158 $labeltosearch .= ' - ' . $productlabel;
4159 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4160 $labeltosearch .= ' (' . getCountry($outorigin, '1') . ')';
4161 }
4162
4163 // Set $labltoshowhtml
4164 $labeltoshowhtml = '';
4165 $labeltoshowhtml .= $objp->ref;
4166 if (!empty($objp->custref)) {
4167 $labeltoshowhtml .= ' (' . $objp->custref . ')';
4168 }
4169 if (!empty($filterkey) && $filterkey != '') {
4170 $labeltoshowhtml = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $labeltoshowhtml, 1);
4171 }
4172 if ($outbarcode) {
4173 $labeltoshowhtml .= ' (' . $outbarcode . ')';
4174 }
4175 $labeltoshowhtml .= ' - ' . dol_trunc($label, $maxlengtharticle);
4176 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4177 $labeltoshowhtml .= ' (' . getCountry($outorigin, '1') . ')';
4178 }
4179
4180 // Stock
4181 $labeltoshowstock = '';
4182 $labeltoshowhtmlstock = '';
4183 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4184 if ($user->hasRight('stock', 'lire')) {
4185 $labeltoshowstock .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4186
4187 if ($objp->stock > 0) {
4188 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_ok">';
4189 } elseif ($objp->stock <= 0) {
4190 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_too_low">';
4191 }
4192 $labeltoshowhtmlstock .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4193 $labeltoshowhtmlstock .= '</span>';
4194
4195 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4196 $langs->load("stocks");
4197
4198 $tmpproduct = new Product($this->db);
4199 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4200 $tmpproduct->load_virtual_stock();
4201 $virtualstock = $tmpproduct->stock_theorique;
4202
4203 $labeltoshowstock .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4204
4205 $labeltoshowhtmlstock .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4206 if ($virtualstock > 0) {
4207 $labeltoshowhtmlstock .= '<span class="product_line_stock_ok">';
4208 } elseif ($virtualstock <= 0) {
4209 $labeltoshowhtmlstock .= '<span class="product_line_stock_too_low">';
4210 }
4211 $labeltoshowhtmlstock .= $virtualstock;
4212 $labeltoshowhtmlstock .= '</span>';
4213
4214 unset($tmpproduct);
4215 }
4216 }
4217 }
4218
4219 // Price
4220 $found = 0;
4221 $labeltoshowprice = '';
4222 $labeltoshowhtmlprice = '';
4223 // If we need a particular price level (from 1 to n)
4224 if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4225 $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
4226 $sql .= " FROM " . $this->db->prefix() . "product_price";
4227 $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
4228 $sql .= " AND entity IN (" . getEntity('productprice') . ")";
4229 $sql .= " AND price_level = " . ((int) $price_level);
4230 $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
4231 $sql .= " LIMIT 1";
4232
4233 dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
4234 $result2 = $this->db->query($sql);
4235 if ($result2) {
4236 $objp2 = $this->db->fetch_object($result2);
4237 if ($objp2) {
4238 $found = 1;
4239 if ($objp2->price_base_type == 'HT') {
4240 $labeltoshowprice .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4241 $labeltoshowhtmlprice .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4242 } else {
4243 $labeltoshowprice .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4244 $labeltoshowhtmlprice .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4245 }
4246 $outprice_ht = price($objp2->price);
4247 $outprice_ttc = price($objp2->price_ttc);
4248 $outpricebasetype = $objp2->price_base_type;
4249 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
4250 $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
4251 $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
4252 } else {
4253 $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
4254 $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
4255 }
4256 }
4257 } else {
4258 dol_print_error($this->db);
4259 }
4260 }
4261
4262 // Price by quantity
4263 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
4264 $found = 1;
4265 $outqty = $objp->quantity;
4266 $outdiscount = $objp->remise_percent;
4267 if ($objp->quantity == 1) {
4268 $labeltoshowprice .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
4269 $labeltoshowhtmlprice .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
4270 $labeltoshowprice .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4271 $labeltoshowhtmlprice .= $langs->transnoentities("Unit");
4272 } else {
4273 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4274 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4275 $labeltoshowprice .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4276 $labeltoshowhtmlprice .= $langs->transnoentities("Units");
4277 }
4278
4279 $outprice_ht = price($objp->unitprice);
4280 $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
4281 $outpricebasetype = $objp->price_base_type;
4282 $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
4283 $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
4284 }
4285 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
4286 $labeltoshowprice .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4287 $labeltoshowhtmlprice .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4288 }
4289 if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
4290 $labeltoshowprice .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4291 $labeltoshowhtmlprice .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4292 }
4293
4294 // Price by customer
4295 if (empty($hidepriceinlabel) && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4296 if (!empty($objp->idprodcustprice)) {
4297 $found = 1;
4298
4299 if ($objp->custprice_base_type == 'HT') {
4300 $labeltoshowprice .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4301 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4302 } else {
4303 $labeltoshowprice .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4304 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4305 }
4306
4307 $outprice_ht = price($objp->custprice);
4308 $outprice_ttc = price($objp->custprice_ttc);
4309 $outpricebasetype = $objp->custprice_base_type;
4310 $outtva_tx = $objp->custtva_tx;
4311 $outdefault_vat_code = $objp->custdefault_vat_code;
4312 $outdiscount = $objp->custdiscount_percent;
4313 }
4314 }
4315
4316 // If level no defined or multiprice not found, we used the default price
4317 if (empty($hidepriceinlabel) && !$found) {
4318 if ($objp->price_base_type == 'HT') {
4319 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4320 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4321 } else {
4322 $labeltoshowprice .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4323 $labeltoshowhtmlprice .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4324 }
4325 $outprice_ht = price($objp->price);
4326 $outprice_ttc = price($objp->price_ttc);
4327 $outpricebasetype = $objp->price_base_type;
4328 $outtva_tx = $objp->tva_tx;
4329 $outdefault_vat_code = $objp->default_vat_code;
4330 }
4331
4332 $optiontext = $labeltosearch.$outvalUnits.$labeltoshowprice.$labeltoshowstock;
4333 $optionhtml = $labeltoshowhtml.$outvalUnits.$labeltoshowhtmlprice.$labeltoshowhtmlstock;
4334 $optionhtmlforattribute = dol_escape_htmltag($optionhtml, 0, 0, '', 0, 1);
4335
4336 // Build options
4337 $opt = '<option value="' . $objp->rowid . '"';
4338 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
4339 if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
4340 $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 . '"';
4341 }
4342 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
4343 $opt .= ' data-labeltrans="' . dol_escape_htmltag($outlabel_translated, 0, 0, '', 0, 1) . '"';
4344 $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
4345 }
4346
4347 if ($stocktag == 1) {
4348 $opt .= ' class="product_line_stock_ok" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4349 //$opt .= ' class="product_line_stock_ok"';
4350 }
4351 if ($stocktag == -1) {
4352 $opt .= ' class="product_line_stock_too_low" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4353 //$opt .= ' class="product_line_stock_too_low"';
4354 }
4355 $opt .= ' data-html="'.$optionhtmlforattribute.'" data-select-html="'.$optionhtmlforattribute.'"';
4356
4357 $opt .= '>';
4358
4359 // Ref, barcode, country
4360 $opt .= dol_escape_htmltag($optiontext, 0, 0, '', 0, 1);
4361 $outval .= $labeltoshowhtml;
4362
4363 // Units
4364 $outval .= $outvalUnits;
4365
4366 // Price
4367 $outval .= $labeltoshowhtmlprice;
4368
4369 // Stock
4370 $outval .= $labeltoshowhtmlstock;
4371
4372
4373 $parameters = array('objp' => $objp);
4374 $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
4375 if (empty($reshook)) {
4376 $opt .= $hookmanager->resPrint;
4377 } else {
4378 $opt = $hookmanager->resPrint;
4379 }
4380
4381 $opt .= "</option>\n";
4382 $optJson = array(
4383 'key' => $outkey,
4384 'value' => $outref,
4385 'label' => $outval,
4386 'label2' => $outlabel,
4387 'desc' => $outdesc,
4388 'type' => $outtype,
4389 'price_ht' => price2num($outprice_ht),
4390 'price_ttc' => price2num($outprice_ttc),
4391 'price_ht_locale' => price(price2num($outprice_ht)),
4392 'price_ttc_locale' => price(price2num($outprice_ttc)),
4393 'pricebasetype' => $outpricebasetype,
4394 'tva_tx' => $outtva_tx,
4395 'default_vat_code' => $outdefault_vat_code,
4396 'qty' => $outqty,
4397 'discount' => $outdiscount,
4398 'duration_value' => $outdurationvalue,
4399 'duration_unit' => $outdurationunit,
4400 'pbq' => $outpbq,
4401 'labeltrans' => $outlabel_translated,
4402 'desctrans' => $outdesc_translated,
4403 'ref_customer' => $outrefcust
4404 );
4405 }
4406
4407 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4408
4425 public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $notused = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '', $nooutput = 0)
4426 {
4427 // phpcs:enable
4428 global $langs, $conf;
4429 global $price_level, $status, $finished;
4430
4431 if (!isset($status)) {
4432 $status = 1;
4433 }
4434
4435 $selected_input_value = '';
4436 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
4437 if ((int) $selected > 0) {
4438 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4439 $producttmpselect = new Product($this->db);
4440 $producttmpselect->fetch((int) $selected);
4441 $selected_input_value = $producttmpselect->ref;
4442 unset($producttmpselect);
4443 } elseif (preg_match('/^idprod_([0-9]+)$/', (string) $selected, $regtmpsel)) {
4444 // Preselect when a product without supplier price was just created ('idprod_ID' value, used by backtopage of creation popup)
4445 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4446 $producttmpselect = new Product($this->db);
4447 $producttmpselect->fetch((int) $regtmpsel[1]);
4448 $selected_input_value = $producttmpselect->ref;
4449 unset($producttmpselect);
4450 }
4451
4452 // mode=2 means suppliers products
4453 $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
4454
4455 $s = ($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="'.$morecss.'" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
4456
4457 $s .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalInt('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
4458 } else {
4459 $s = $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $notused, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, getDolGlobalInt('SUPPLIER_SHOW_STOCK_IN_PRODUCTS_COMBO'), $placeholder);
4460 }
4461
4462 if ($nooutput) {
4463 return $s;
4464 } else {
4465 print $s;
4466 }
4467 }
4468
4469 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4470
4489 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 = '')
4490 {
4491 // phpcs:enable
4492 global $langs, $conf, $user;
4493 global $hookmanager;
4494
4495 $out = '';
4496 $outarray = array();
4497
4498 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4499
4500 $langs->load('stocks');
4501 // Units
4502 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4503 $langs->load('other');
4504 }
4505
4506 $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,";
4507 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
4508 $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
4509 $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
4510 $sql .= ", pfp.supplier_reputation";
4511 // if we use supplier description of the products
4512 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4513 $sql .= ", pfp.desc_fourn as description";
4514 } else {
4515 $sql .= ", p.description";
4516 }
4517 // Units
4518 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4519 $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";
4520 }
4521
4522 // Add select from hooks
4523 $parameters = [];
4524 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
4525 $sql .= $hookmanager->resPrint;
4526
4527 $sql .= " FROM " . $this->db->prefix() . "product as p";
4528
4529 // Add join from hooks
4530 $parameters = [];
4531 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
4532 $sql .= $hookmanager->resPrint;
4533
4534 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
4535 if ($socid > 0) {
4536 $sql .= " AND pfp.fk_soc = " . ((int) $socid);
4537 }
4538 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4539 // Units
4540 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4541 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
4542 }
4543 $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
4544 if ($statut != -1) {
4545 $sql .= " AND p.tobuy = " . ((int) $statut);
4546 }
4547 if (strval($filtertype) != '') {
4548 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
4549 }
4550
4551 // Add where from hooks
4552 $parameters = array();
4553 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
4554 $sql .= $hookmanager->resPrint;
4555 // Add criteria on ref/label
4556 if ($filterkey != '') {
4557 $sql .= ' AND (';
4558 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
4559 // For natural search
4560 $search_crit = explode(' ', $filterkey);
4561 $i = 0;
4562 if (count($search_crit) > 1) {
4563 $sql .= "(";
4564 }
4565 foreach ($search_crit as $crit) {
4566 if ($i > 0) {
4567 $sql .= " AND ";
4568 }
4569 $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) . "%'";
4570 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4571 $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
4572 }
4573 $sql .= ")";
4574 $i++;
4575 }
4576 if (count($search_crit) > 1) {
4577 $sql .= ")";
4578 }
4579 if (isModEnabled('barcode')) {
4580 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4581 $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4582 }
4583 $sql .= ')';
4584 }
4585 $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
4586 $sql .= $this->db->plimit($limit, 0);
4587
4588 // Build output string
4589
4590 dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
4591 $result = $this->db->query($sql);
4592 if ($result) {
4593 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4594 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
4595
4596 $num = $this->db->num_rows($result);
4597
4598 //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
4599 $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
4600 if (!$selected) {
4601 $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4602 } else {
4603 $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4604 }
4605
4606 $i = 0;
4607 while ($i < $num) {
4608 $objp = $this->db->fetch_object($result);
4609
4610 if (is_null($objp->idprodfournprice)) {
4611 // There is no supplier price found, we will use the vat rate for sale
4612 $objp->tva_tx = $objp->tva_tx_sale;
4613 $objp->default_vat_code = $objp->default_vat_code_sale;
4614 }
4615
4616 $outkey = $objp->idprodfournprice; // id in table of price
4617 if (!$outkey && $alsoproductwithnosupplierprice) {
4618 $outkey = 'idprod_' . $objp->rowid; // id of product
4619 }
4620
4621 $outref = $objp->ref;
4622 $outbarcode = $objp->barcode;
4623 $outqty = 1;
4624 $outdiscount = 0;
4625 $outtype = $objp->fk_product_type;
4626 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4627 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4628
4629 // Units
4630 $outvalUnits = '';
4631 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4632 if (!empty($objp->unit_short)) {
4633 $outvalUnits .= ' - ' . $objp->unit_short;
4634 }
4635 if (!empty($objp->weight) && $objp->weight_units !== null) {
4636 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4637 $outvalUnits .= ' - ' . $unitToShow;
4638 }
4639 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4640 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4641 $outvalUnits .= ' - ' . $unitToShow;
4642 }
4643 if (!empty($objp->surface) && $objp->surface_units !== null) {
4644 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4645 $outvalUnits .= ' - ' . $unitToShow;
4646 }
4647 if (!empty($objp->volume) && $objp->volume_units !== null) {
4648 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4649 $outvalUnits .= ' - ' . $unitToShow;
4650 }
4651 if ($outdurationvalue && $outdurationunit) {
4652 $da = array(
4653 'h' => $langs->trans('Hour'),
4654 'd' => $langs->trans('Day'),
4655 'w' => $langs->trans('Week'),
4656 'm' => $langs->trans('Month'),
4657 'y' => $langs->trans('Year')
4658 );
4659 if (isset($da[$outdurationunit])) {
4660 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4661 }
4662 }
4663 }
4664
4665 $objRef = $objp->ref;
4666 if ($filterkey && $filterkey != '') {
4667 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
4668 }
4669 $objRefFourn = $objp->ref_fourn;
4670 if ($filterkey && $filterkey != '') {
4671 $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
4672 }
4673 $label = $objp->label;
4674 if ($filterkey && $filterkey != '') {
4675 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4676 }
4677
4678 switch ($objp->fk_product_type) {
4680 $picto = 'product';
4681 break;
4683 $picto = 'service';
4684 break;
4685 default:
4686 $picto = '';
4687 break;
4688 }
4689
4690 if (empty($picto)) {
4691 $optlabel = '';
4692 } else {
4693 $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
4694 }
4695
4696 $optlabel .= $objp->ref;
4697 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4698 $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
4699 }
4700 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4701 $optlabel .= ' (' . $outbarcode . ')';
4702 }
4703 $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4704
4705 $outvallabel = $objRef;
4706 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4707 $outvallabel .= ' (' . $objRefFourn . ')';
4708 }
4709 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4710 $outvallabel .= ' (' . $outbarcode . ')';
4711 }
4712 $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4713
4714 $outsearchlabel = implode(' ', array_filter(array(
4715 (string) $objp->ref,
4716 (string) $objp->ref_fourn,
4717 (string) $objp->barcode,
4718 (string) $objp->label,
4719 dol_string_nohtmltag((string) $objp->description)
4720 ), function (string $value): bool {
4721 return $value !== '';
4722 }));
4723
4724 // Units
4725 $optlabel .= $outvalUnits;
4726 $outvallabel .= $outvalUnits;
4727
4728 if (!empty($objp->idprodfournprice)) {
4729 $outqty = $objp->quantity;
4730 $outdiscount = $objp->remise_percent;
4731 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4732 $prod_supplier = new ProductFournisseur($this->db);
4733 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4734 $prod_supplier->id = $objp->fk_product;
4735 $prod_supplier->fourn_qty = $objp->quantity;
4736 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4737 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4738
4739 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4740 $priceparser = new PriceParser($this->db);
4741 $price_result = $priceparser->parseProductSupplier($prod_supplier);
4742 if ($price_result >= 0) {
4743 $objp->fprice = $price_result;
4744 if ($objp->quantity >= 1) {
4745 $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
4746 }
4747 }
4748 }
4749 if ($objp->quantity == 1) {
4750 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4751 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
4752 $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4753 $outvallabel .= $langs->transnoentities("Unit");
4754 } else {
4755 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4756 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4757 $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4758 $outvallabel .= ' ' . $langs->transnoentities("Units");
4759 }
4760
4761 if ($objp->quantity != 1) {
4762 $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
4763 $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
4764 }
4765 if ($objp->remise_percent >= 1) {
4766 $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4767 $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4768 }
4769 if ($objp->duration) {
4770 $optlabel .= " - " . $objp->duration;
4771 $outvallabel .= " - " . $objp->duration;
4772 }
4773 if (!$socid) {
4774 $optlabel .= " - " . dol_trunc($objp->name, 8);
4775 $outvallabel .= " - " . dol_trunc($objp->name, 8);
4776 }
4777 if ($objp->supplier_reputation) {
4778 //TODO dictionary
4779 $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
4780
4781 $optlabel .= " - " . $reputations[$objp->supplier_reputation];
4782 $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
4783 }
4784 } else {
4785 $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
4786 $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
4787 }
4788
4789 if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4790 $novirtualstock = ($showstockinlist == 2);
4791
4792 if ($user->hasRight('stock', 'lire')) {
4793 $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4794
4795 if ($objp->stock > 0) {
4796 $optlabel .= ' - <span class="product_line_stock_ok">';
4797 } elseif ($objp->stock <= 0) {
4798 $optlabel .= ' - <span class="product_line_stock_too_low">';
4799 }
4800 $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
4801 $optlabel .= '</span>';
4802 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4803 $langs->load("stocks");
4804
4805 $tmpproduct = new Product($this->db);
4806 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4807 $tmpproduct->load_virtual_stock();
4808 $virtualstock = $tmpproduct->stock_theorique;
4809
4810 $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4811
4812 $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4813 if ($virtualstock > 0) {
4814 $optlabel .= '<span class="product_line_stock_ok">';
4815 } elseif ($virtualstock <= 0) {
4816 $optlabel .= '<span class="product_line_stock_too_low">';
4817 }
4818 $optlabel .= $virtualstock;
4819 $optlabel .= '</span>';
4820
4821 unset($tmpproduct);
4822 }
4823 }
4824 }
4825
4826 $optstart = '<option value="' . $outkey . '"';
4827 if ($selected && preg_match('/^idprod_/', (string) $selected) && (string) $selected == 'idprod_'.$objp->rowid) {
4828 $optstart .= ' selected';
4829 } elseif ($selected && (string) $selected == (string) $objp->idprodfournprice) {
4830 $optstart .= ' selected';
4831 }
4832
4833 if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
4834 $optstart .= ' disabled';
4835 }
4836
4837 if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
4838 $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
4839 $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
4840 $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
4841 $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"'; // the price with numeric international format
4842 $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"'; // the price formatted in user language
4843 $optstart .= ' data-discount="' . dol_escape_htmltag((string) $outdiscount) . '"';
4844 $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"'; // the rate with numeric international format
4845 $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"'; // the rate formatted in user language
4846 $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
4847 $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
4848 if (isModEnabled('multicurrency')) {
4849 $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
4850 $optstart .= ' data-multicurrency-unitprice="' . dol_escape_htmltag(price2num($objp->multicurrency_unitprice)) . '"'; // the price with numeric international format
4851 }
4852 }
4853 $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
4854 $optstart .= ' data-search="' . dol_escape_htmltag($outsearchlabel) . '"';
4855
4856 // set $parameters to call hook
4857 $outarrayentry = array(
4858 'key' => $outkey,
4859 'value' => $outref,
4860 'label' => $outvallabel,
4861 'labelhtml' => $optlabel,
4862 'qty' => $outqty,
4863 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4864 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4865 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4866 'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
4867 'tva_tx' => price2num($objp->tva_tx),
4868 'default_vat_code' => $objp->default_vat_code,
4869 'supplier_ref' => $objp->ref_fourn,
4870 'discount' => $outdiscount,
4871 'type' => $outtype,
4872 'duration_value' => $outdurationvalue,
4873 'duration_unit' => $outdurationunit,
4874 'disabled' => empty($objp->idprodfournprice),
4875 'description' => $objp->description
4876 );
4877 if (isModEnabled('multicurrency')) {
4878 $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4879 $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4880 }
4881 $parameters = array(
4882 'objp' => &$objp,
4883 'optstart' => &$optstart,
4884 'optlabel' => &$optlabel,
4885 'outvallabel' => &$outvallabel,
4886 'outarrayentry' => &$outarrayentry,
4887 'fk_soc' => $socid
4888 );
4889 $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4890
4891
4892 // Add new entry
4893 // "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
4894 // "label" value of json key array is used by jQuery automatically as text for combo box
4895 $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4896 $outarraypush = array(
4897 'key' => $outkey,
4898 'value' => $outref,
4899 'label' => $outvallabel,
4900 'labelhtml' => $optlabel,
4901 'qty' => $outqty,
4902 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4903 'price_qty_ht_locale' => price($objp->fprice),
4904 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4905 'price_unit_ht_locale' => price($objp->unitprice),
4906 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4907 'tva_tx_formated' => price($objp->tva_tx),
4908 'tva_tx' => price2num($objp->tva_tx),
4909 'default_vat_code' => $objp->default_vat_code,
4910 'supplier_ref' => $objp->ref_fourn,
4911 'discount' => $outdiscount,
4912 'type' => $outtype,
4913 'duration_value' => $outdurationvalue,
4914 'duration_unit' => $outdurationunit,
4915 'disabled' => empty($objp->idprodfournprice),
4916 'description' => $objp->description
4917 );
4918 if (isModEnabled('multicurrency')) {
4919 $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4920 $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4921 }
4922 array_push($outarray, $outarraypush);
4923
4924 // Example of var_dump $outarray
4925 // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4926 // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/unit (20,00 Euros/unit)"
4927 // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4928 //}
4929 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4930 //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unit (20,00 Euros/unit)');
4931 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4932
4933 $i++;
4934 }
4935 $out .= '</select>';
4936
4937 $this->db->free($result);
4938
4939 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4940 $out .= ajax_combobox($htmlname);
4941 } else {
4942 dol_print_error($this->db);
4943 }
4944
4945 if (empty($outputmode)) {
4946 return $out;
4947 }
4948 return $outarray;
4949 }
4950
4951 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4952
4961 public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4962 {
4963 // phpcs:enable
4964 global $langs, $conf;
4965
4966 $langs->load('stocks');
4967
4968 $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4969 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4970 $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4971 $sql .= " FROM " . $this->db->prefix() . "product as p";
4972 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4973 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4974 $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4975 $sql .= " AND p.tobuy = 1";
4976 $sql .= " AND s.fournisseur = 1";
4977 $sql .= " AND p.rowid = " . ((int) $productid);
4978 if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4979 $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4980 } else {
4981 $sql .= " ORDER BY pfp.unitprice - pfp.unitprice * pfp.remise_percent / 100 ASC";
4982 }
4983
4984 dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4985 $result = $this->db->query($sql);
4986
4987 if ($result) {
4988 $num = $this->db->num_rows($result);
4989
4990 $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4991
4992 if (!$num) {
4993 $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4994 } else {
4995 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4996 $form .= '<option value="0">&nbsp;</option>';
4997
4998 $i = 0;
4999 while ($i < $num) {
5000 $objp = $this->db->fetch_object($result);
5001
5002 $opt = '<option value="' . $objp->idprodfournprice . '"';
5003 //if there is only one supplier, preselect it
5004 if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
5005 $opt .= ' selected';
5006 }
5007 $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
5008
5009 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
5010 $prod_supplier = new ProductFournisseur($this->db);
5011 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
5012 $prod_supplier->id = $productid;
5013 $prod_supplier->fourn_qty = $objp->quantity;
5014 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
5015 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
5016
5017 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
5018 $priceparser = new PriceParser($this->db);
5019 $price_result = $priceparser->parseProductSupplier($prod_supplier);
5020 if ($price_result >= 0) {
5021 $objp->fprice = $price_result;
5022 if ($objp->quantity >= 1) {
5023 $objp->unitprice = $objp->fprice / $objp->quantity;
5024 }
5025 }
5026 }
5027 if ($objp->quantity == 1) {
5028 $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
5029 }
5030
5031 $opt .= $objp->quantity . ' ';
5032
5033 if ($objp->quantity == 1) {
5034 $opt .= $langs->trans("Unit");
5035 } else {
5036 $opt .= $langs->trans("Units");
5037 }
5038 if ($objp->quantity > 1) {
5039 $opt .= " - ";
5040 $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");
5041 }
5042 if ($objp->duration) {
5043 $opt .= " - " . $objp->duration;
5044 }
5045 $opt .= "</option>\n";
5046
5047 $form .= $opt;
5048 $i++;
5049 }
5050 }
5051
5052 $form .= '</select>';
5053 $this->db->free($result);
5054 return $form;
5055 } else {
5056 dol_print_error($this->db);
5057 return '';
5058 }
5059 }
5060
5061
5062 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5069 {
5070 // phpcs:enable
5071 global $langs, $hookmanager;
5072
5073 $num = count($this->cache_conditions_paiements);
5074 if ($num > 0) {
5075 return 0; // Cache already loaded
5076 }
5077
5078 dol_syslog(__METHOD__, LOG_DEBUG);
5079
5080 $this->cache_conditions_paiements = array();
5081
5082 $sql = "SELECT rowid, code, libelle as label, deposit_percent, entity";
5083 $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
5084 $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
5085 $sql .= " AND active > 0";
5086 $sql .= " ORDER BY sortorder";
5087
5088 $resql = $this->db->query($sql);
5089 if ($resql) {
5090 $num = $this->db->num_rows($resql);
5091 $i = 0;
5092 while ($i < $num) {
5093 $obj = $this->db->fetch_object($resql);
5094
5095 // If a translation exists, we use it, otherwise, we take the label by default
5096 $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5097
5098 $this->cache_conditions_paiements[$obj->rowid]['code'] = (string) $obj->code;
5099 $this->cache_conditions_paiements[$obj->rowid]['label'] = (string) $label;
5100 $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = (string) $obj->deposit_percent;
5101 $this->cache_conditions_paiements[$obj->rowid]['entity'] = (int) $obj->entity;
5102
5103 $i++;
5104 }
5105
5106 $parameters = array('context' => 'paymentterm');
5107 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5108 if (empty($reshook)) {
5109 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5110 $this->cache_conditions_paiements = array_merge($this->cache_conditions_paiements, $hookmanager->resArray);
5111 }
5112 } else {
5113 $this->cache_conditions_paiements = $hookmanager->resArray;
5114 }
5115
5116 //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
5117
5118 return $num;
5119 } else {
5120 dol_print_error($this->db);
5121 return -1;
5122 }
5123 }
5124
5125 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5126
5133 {
5134 // phpcs:enable
5135 $factureRec = new FactureRec($this->db);
5136
5137 $this->cache_rule_for_lines_dates = $factureRec->fields['rule_for_lines_dates']['arrayofkeyval'];
5138
5139 if (empty($this->cache_rule_for_lines_dates)) {
5140 return -1;
5141 }
5142
5143 return 1;
5144 }
5145
5146 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5147
5153 public function load_cache_availability()
5154 {
5155 // phpcs:enable
5156 global $langs;
5157
5158 $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
5159 if ($num > 0) {
5160 return 0; // Cache already loaded
5161 }
5162
5163 dol_syslog(__METHOD__, LOG_DEBUG);
5164
5165 $this->cache_availability = array();
5166
5167 $langs->load('propal');
5168
5169 $sql = "SELECT rowid, code, label, position";
5170 $sql .= " FROM " . $this->db->prefix() . 'c_availability';
5171 $sql .= " WHERE active > 0";
5172
5173 $resql = $this->db->query($sql);
5174 if ($resql) {
5175 $num = $this->db->num_rows($resql);
5176 $i = 0;
5177 while ($i < $num) {
5178 $obj = $this->db->fetch_object($resql);
5179
5180 // If a translation exists, we use is, otherwise, we take the label by default
5181 $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5182 $this->cache_availability[$obj->rowid]['code'] = (string) $obj->code;
5183 $this->cache_availability[$obj->rowid]['label'] = (string) $label;
5184 $this->cache_availability[$obj->rowid]['position'] = (int) $obj->position;
5185 $i++;
5186 }
5187
5188 // @phan-suppress-next-line PhanTypeMismatchProperty PhanTypeMismatchDimFetch
5189 $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
5190
5191 return $num;
5192 } else {
5193 dol_print_error($this->db);
5194 return -1;
5195 }
5196 }
5197
5209 public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '', $noouput = 0)
5210 {
5211 global $langs, $user;
5212
5213 $this->load_cache_availability();
5214
5215 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5216
5217 $out = '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5218 if ($addempty) {
5219 $out .= '<option value="-1">'.(is_numeric($addempty) ? '&nbsp;' : $langs->trans($addempty)).'</option>';
5220 }
5221 foreach ($this->cache_availability as $id => $arrayavailability) {
5222 if ($selected == $id) {
5223 $out .= '<option value="' . $id . '" selected>';
5224 } else {
5225 $out .= '<option value="' . $id . '">';
5226 }
5227 $out .= dol_escape_htmltag($arrayavailability['label']);
5228 $out .= '</option>';
5229 }
5230 $out .= '</select>';
5231 if ($user->admin) {
5232 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5233 }
5234 $out .= ajax_combobox($htmlname);
5235
5236 if ($noouput) {
5237 return $out;
5238 } else {
5239 print $out;
5240 return '';
5241 }
5242 }
5243
5249 public function loadCacheInputReason()
5250 {
5251 global $langs;
5252
5253 $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
5254 if ($num > 0) {
5255 return 0; // Cache already loaded
5256 }
5257
5258 $sql = "SELECT rowid, code, label";
5259 $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
5260 $sql .= " WHERE active > 0";
5261
5262 $resql = $this->db->query($sql);
5263 if ($resql) {
5264 $num = $this->db->num_rows($resql);
5265 $i = 0;
5267 $tmparray = array();
5268 while ($i < $num) {
5269 $obj = $this->db->fetch_object($resql);
5270
5271 // If a translation exists, we use is, otherwise, we take the label by default
5272 $label = ($obj->label != '-' ? (string) $obj->label : '');
5273 if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
5274 $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
5275 }
5276 if ($langs->trans($obj->code) != $obj->code) {
5277 $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
5278 }
5279
5280 $tmparray[(int) $obj->rowid]
5281 = array(
5282 'id' => (int) $obj->rowid,
5283 'code' => (string) $obj->code,
5284 'label' => $label,
5285 );
5286 $i++;
5287 }
5288
5289 $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
5290
5291 unset($tmparray);
5292 return $num;
5293 } else {
5294 dol_print_error($this->db);
5295 return -1;
5296 }
5297 }
5298
5311 public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
5312 {
5313 global $langs, $user;
5314
5315 $this->loadCacheInputReason();
5316
5317 print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5318 if ($addempty) {
5319 print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
5320 }
5321 foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
5322 if ($arraydemandreason['code'] == $exclude) {
5323 continue;
5324 }
5325
5326 if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
5327 print '<option value="' . $arraydemandreason['id'] . '" selected>';
5328 } else {
5329 print '<option value="' . $arraydemandreason['id'] . '">';
5330 }
5331 $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
5332 print $langs->trans($label);
5333 print '</option>';
5334 }
5335 print '</select>';
5336 if ($user->admin && empty($notooltip)) {
5337 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5338 }
5339 print ajax_combobox('select_' . $htmlname);
5340 }
5341
5342 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5343
5350 {
5351 // phpcs:enable
5352 global $langs, $hookmanager;
5353
5354 $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
5355 if ($num > 0) {
5356 return $num; // Cache already loaded
5357 }
5358
5359 dol_syslog(__METHOD__, LOG_DEBUG);
5360
5361 $this->cache_types_paiements = array();
5362
5363 $sql = "SELECT id, code, libelle as label, type, entity, active";
5364 $sql .= " FROM " . $this->db->prefix() . "c_paiement";
5365 $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
5366
5367 $resql = $this->db->query($sql);
5368 if ($resql) {
5369 $num = $this->db->num_rows($resql);
5370 $i = 0;
5371 while ($i < $num) {
5372 $obj = $this->db->fetch_object($resql);
5373
5374 // If a translation exists, we use is, otherwise, we take the label by default
5375 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5376 $this->cache_types_paiements[(int) $obj->id]['id'] = (int) $obj->id;
5377 $this->cache_types_paiements[(int) $obj->id]['code'] = (string) $obj->code;
5378 $this->cache_types_paiements[(int) $obj->id]['label'] = (string) $label;
5379 $this->cache_types_paiements[(int) $obj->id]['type'] = (int) $obj->type;
5380 $this->cache_types_paiements[(int) $obj->id]['entity'] = (int) $obj->entity;
5381 $this->cache_types_paiements[(int) $obj->id]['active'] = (int) $obj->active;
5382 $i++;
5383 }
5384
5385 $parameters = array('context' => 'paymenttype');
5386 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5387 if (empty($reshook)) {
5388 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5389 $this->cache_types_paiements = array_merge($this->cache_types_paiements, $hookmanager->resArray);
5390 }
5391 } else {
5392 $this->cache_types_paiements = $hookmanager->resArray;
5393 }
5394
5395 $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
5396
5397 return $num;
5398 } else {
5399 dol_print_error($this->db);
5400 return -1;
5401 }
5402 }
5403
5404
5405 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5406
5425 public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
5426 {
5427 // phpcs:enable
5428 $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
5429 if (empty($noprint)) {
5430 print $out;
5431 } else {
5432 return $out;
5433 }
5434 }
5435
5436
5453 public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
5454 {
5455 global $langs, $user;
5456
5457 $out = '';
5458 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5459
5461
5462 // Set default value if not already set by caller
5463 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
5464 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
5465 $selected = getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID');
5466 }
5467
5468 $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5469 if ($addempty) {
5470 $out .= '<option value="0">&nbsp;</option>';
5471 }
5472
5473 $selectedDepositPercent = null;
5474
5475 foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
5476 if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
5477 continue;
5478 }
5479
5480 if ($selected == $id) {
5481 $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
5482 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
5483 } else {
5484 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
5485 }
5486 $label = $arrayconditions['label'];
5487
5488 if (!empty($arrayconditions['deposit_percent'])) {
5489 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
5490 }
5491
5492 $out .= $label;
5493 $out .= '</option>';
5494 }
5495 $out .= '</select>';
5496 if ($user->admin && empty($noinfoadmin)) {
5497 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5498 }
5499 $out .= ajax_combobox($htmlname);
5500
5501 if ($deposit_percent >= 0) {
5502 $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
5503 $out .= $langs->trans('DepositPercent') . ' : ';
5504 $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
5505 $out .= '</span>';
5506 $out .= '
5507 <script nonce="' . getNonce() . '">
5508 $(document).ready(function () {
5509 $("#' . $htmlname . '").change(function () {
5510 let $selected = $(this).find("option:selected");
5511 let depositPercent = $selected.attr("data-deposit_percent");
5512
5513 if (depositPercent.length > 0) {
5514 $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
5515 } else {
5516 $("#' . $htmlname . '_deposit_percent_container").hide();
5517 }
5518
5519 return true;
5520 });
5521 });
5522 </script>';
5523 }
5524
5525 return $out;
5526 }
5527
5528
5537 public function getSelectRuleForLinesDates($selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0)
5538 {
5539 global $langs;
5540
5541 $out = '';
5542
5544
5545 $out .= '<select id="' . $htmlname . '" class="flat selectbillingterm" name="' . $htmlname . '">';
5546 if ($addempty) {
5547 $out .= '<option value="-1">&nbsp;</option>';
5548 }
5549
5550
5551 foreach ($this->cache_rule_for_lines_dates as $rule_for_lines_dates_key => $rule_for_lines_dates_name) {
5552 if ($selected == $rule_for_lines_dates_key) {
5553 $out .= '<option value="' . $rule_for_lines_dates_key . '" selected>';
5554 } else {
5555 $out .= '<option value="' . $rule_for_lines_dates_key . '">';
5556 }
5557
5558 $out .= $langs->trans($rule_for_lines_dates_name);
5559 $out .= '</option>';
5560 }
5561 $out .= '</select>';
5562
5563 $out .= ajax_combobox($htmlname);
5564
5565 return $out;
5566 }
5567
5568
5569 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5570
5587 public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
5588 {
5589 // phpcs:enable
5590 global $langs, $user;
5591
5592 $out = '';
5593
5594 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
5595
5596 $filterarray = array();
5597 if ($filtertype == 'CRDT') {
5598 $filterarray = array(0, 2, 3);
5599 } elseif ($filtertype == 'DBIT') {
5600 $filterarray = array(1, 2, 3);
5601 } elseif ($filtertype != '' && $filtertype != '-1') {
5602 $filterarray = explode(',', $filtertype);
5603 }
5604
5606
5607 // Set default value if not already set by caller
5608 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
5609 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
5610 $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
5611 }
5612
5613 $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5614 if ($empty) {
5615 $out .= '<option value="">&nbsp;</option>';
5616 }
5617 foreach ($this->cache_types_paiements as $id => $arraytypes) {
5618 // If not good status
5619 if ($active >= 0 && $arraytypes['active'] != $active) {
5620 continue;
5621 }
5622
5623 // We skip of the user requested to filter on specific payment methods
5624 if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
5625 continue;
5626 }
5627
5628 // We discard empty lines if showempty is on because an empty line has already been output.
5629 if ($empty && empty($arraytypes['code'])) {
5630 continue;
5631 }
5632
5633 if ($format == 0) {
5634 $out .= '<option value="' . $id . '" data-code="'.$arraytypes['code'].'"';
5635 } elseif ($format == 1) {
5636 $out .= '<option value="' . $arraytypes['code'] . '"';
5637 } elseif ($format == 2) {
5638 $out .= '<option value="' . $arraytypes['code'] . '"';
5639 } elseif ($format == 3) {
5640 $out .= '<option value="' . $id . '"';
5641 }
5642 // Print attribute selected or not
5643 if ($format == 1 || $format == 2) {
5644 if ($selected == $arraytypes['code']) {
5645 $out .= ' selected';
5646 }
5647 } else {
5648 if ($selected == $id) {
5649 $out .= ' selected';
5650 }
5651 }
5652 $out .= '>';
5653 $value = '';
5654 if ($format == 0) {
5655 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5656 } elseif ($format == 1) {
5657 $value = $arraytypes['code'];
5658 } elseif ($format == 2) {
5659 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5660 } elseif ($format == 3) {
5661 $value = $arraytypes['code'];
5662 }
5663 $out .= $value ? $value : '&nbsp;';
5664 $out .= '</option>';
5665 }
5666 $out .= '</select>';
5667 if ($user->admin && !$noadmininfo) {
5668 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5669 }
5670 $out .= ajax_combobox('select' . $htmlname);
5671
5672 if (empty($nooutput)) {
5673 print $out;
5674 } else {
5675 return $out;
5676 }
5677 }
5678
5679
5688 public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
5689 {
5690 global $langs;
5691
5692 $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5693 $options = array(
5694 'HT' => $langs->trans("HT"),
5695 'TTC' => $langs->trans("TTC")
5696 );
5697 foreach ($options as $id => $value) {
5698 if ($selected == $id) {
5699 $return .= '<option value="' . $id . '" selected>' . $value;
5700 } else {
5701 $return .= '<option value="' . $id . '">' . $value;
5702 }
5703 $return .= '</option>';
5704 }
5705 $return .= '</select>';
5706 if ($addjscombo) {
5707 $return .= ajax_combobox('select_' . $htmlname);
5708 }
5709
5710 return $return;
5711 }
5712
5713 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5714
5721 {
5722 // phpcs:enable
5723 global $langs;
5724
5725 $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
5726 if ($num > 0) {
5727 return $num; // Cache already loaded
5728 }
5729
5730 dol_syslog(__METHOD__, LOG_DEBUG);
5731
5732 $this->cache_transport_mode = array();
5733
5734 $sql = "SELECT rowid, code, label, active";
5735 $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
5736 $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
5737
5738 $resql = $this->db->query($sql);
5739 if ($resql) {
5740 $num = $this->db->num_rows($resql);
5741 $i = 0;
5742 while ($i < $num) {
5743 $obj = $this->db->fetch_object($resql);
5744
5745 // If traduction exist, we use it else we take the default label
5746 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5747 $this->cache_transport_mode[(int) $obj->rowid]
5748 = array(
5749 'rowid' => (int) $obj->rowid,
5750 'code' => (string) $obj->code,
5751 'label' => (string) $label,
5752 'active' => (int) $obj->active,
5753 );
5754 $i++;
5755 }
5756
5757 $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
5758
5759 return $num;
5760 } else {
5761 dol_print_error($this->db);
5762 return -1;
5763 }
5764 }
5765
5779 public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
5780 {
5781 global $langs, $user;
5782
5783 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
5784
5786
5787 print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5788 if ($empty) {
5789 print '<option value="">&nbsp;</option>';
5790 }
5791 foreach ($this->cache_transport_mode as $id => $arraytypes) {
5792 // If not good status
5793 if ($active >= 0 && $arraytypes['active'] != $active) {
5794 continue;
5795 }
5796
5797 // We discard empty line if showempty is on because an empty line has already been output.
5798 if ($empty && empty($arraytypes['code'])) {
5799 continue;
5800 }
5801
5802 if ($format == 0) {
5803 print '<option value="' . $id . '"';
5804 } elseif ($format == 1) {
5805 print '<option value="' . $arraytypes['code'] . '"';
5806 } elseif ($format == 2) {
5807 print '<option value="' . $arraytypes['code'] . '"';
5808 } elseif ($format == 3) {
5809 print '<option value="' . $id . '"';
5810 }
5811 // If text is selected, we compare with code, else with id
5812 if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
5813 print ' selected';
5814 } elseif ($selected == $id) {
5815 print ' selected';
5816 }
5817 print '>';
5818 $value = '';
5819 if ($format == 0) {
5820 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5821 } elseif ($format == 1) {
5822 $value = $arraytypes['code'];
5823 } elseif ($format == 2) {
5824 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5825 } elseif ($format == 3) {
5826 $value = $arraytypes['code'];
5827 }
5828 print $value ? $value : '&nbsp;';
5829 print '</option>';
5830 }
5831 print '</select>';
5832
5833 print ajax_combobox("select".$htmlname);
5834
5835 if ($user->admin && !$noadmininfo) {
5836 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5837 }
5838 }
5839
5852 public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
5853 {
5854 global $langs, $user;
5855
5856 $langs->loadLangs(array("admin", "sendings"));
5857
5858 $sql = "SELECT rowid, code, libelle as label";
5859 $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
5860 $sql .= " WHERE active > 0";
5861 if ($filtre) {
5862 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
5863 }
5864 $sql .= " ORDER BY libelle ASC";
5865
5866 dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
5867
5868 $result = $this->db->query($sql);
5869 if ($result) {
5870 $num = $this->db->num_rows($result);
5871 $i = 0;
5872 if ($num) {
5873 print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5874 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5875 print '<option value="-1">&nbsp;</option>';
5876 }
5877 while ($i < $num) {
5878 $obj = $this->db->fetch_object($result);
5879 if ($selected == $obj->rowid) {
5880 print '<option value="' . $obj->rowid . '" selected>';
5881 } else {
5882 print '<option value="' . $obj->rowid . '">';
5883 }
5884 print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
5885 print '</option>';
5886 $i++;
5887 }
5888 print "</select>";
5889 if ($user->admin && empty($noinfoadmin)) {
5890 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5891 }
5892
5893 print ajax_combobox('select' . $htmlname);
5894 } else {
5895 print $langs->trans("NoShippingMethodDefined");
5896 }
5897 } else {
5898 dol_print_error($this->db);
5899 }
5900 }
5901
5911 public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
5912 {
5913 global $langs;
5914
5915 $langs->load("sendings");
5916
5917 if ($htmlname != "none") {
5918 print '<form method="POST" action="' . $page . '">';
5919 print '<input type="hidden" name="action" value="setshippingmethod">';
5920 print '<input type="hidden" name="token" value="' . newToken() . '">';
5921 $this->selectShippingMethod($selected, $htmlname, '', $addempty);
5922 print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
5923 print '</form>';
5924 } else {
5925 if ($selected) {
5926 $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
5927 print $langs->trans("SendingMethod" . strtoupper($code));
5928 } else {
5929 print "&nbsp;";
5930 }
5931 }
5932 }
5933
5942 public function selectSituationInvoices($selected = '', $socid = 0)
5943 {
5944 global $langs;
5945
5946 $langs->load('bills');
5947
5948 $opt = '';
5949
5950 $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
5951 $sql .= ' FROM ' . $this->db->prefix() . 'facture';
5952 $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
5953 $sql .= ' AND situation_counter >= 1';
5954 $sql .= ' AND fk_soc = ' . (int) $socid;
5955 $sql .= ' AND type <> 2';
5956 $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
5957 $resql = $this->db->query($sql);
5958
5959 $nbSituationInvoiceForThirdparty = 0;
5960
5961 if ($resql && $this->db->num_rows($resql) > 0) {
5962 // Last seen cycle
5963 $ref = 0;
5964 while ($obj = $this->db->fetch_object($resql)) {
5965 //Same cycle ?
5966 if ($obj->situation_cycle_ref != $ref) {
5967 // Just seen this cycle
5968 $ref = $obj->situation_cycle_ref;
5969 //not final ?
5970 if ($obj->situation_final != 1) {
5971 //Not prov?
5972 if (substr($obj->ref, 1, 4) != 'PROV') {
5973 $nbSituationInvoiceForThirdparty++;
5974
5975 if ($selected == $obj->rowid) {
5976 $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
5977 } else {
5978 $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
5979 }
5980 }
5981 }
5982 }
5983 }
5984 } else {
5985 dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
5986 }
5987
5988 if ($nbSituationInvoiceForThirdparty > 0) {
5989 $opt = '<option class="minwidth100" value="" selected>&nbsp;</option>'.$opt;
5990 } else {
5991 $opt = '<option class="minwidth100" value="-1" selected>'.$langs->trans('NoSituations').'</option>';
5992 }
5993
5994 return $opt;
5995 }
5996
6006 public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
6007 {
6008 global $langs;
6009
6010 $langs->load('products');
6011
6012 $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
6013
6014 $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
6015 $sql .= ' WHERE active > 0';
6016 if (!empty($unit_type)) {
6017 $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
6018 }
6019 $sql .= " ORDER BY sortorder";
6020
6021 $resql = $this->db->query($sql);
6022 if ($resql && $this->db->num_rows($resql) > 0) {
6023 if ($showempty) {
6024 $return .= '<option value="-1"></option>';
6025 }
6026
6027 while ($res = $this->db->fetch_object($resql)) {
6028 $unitLabel = $res->label;
6029 if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
6030 $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
6031 }
6032
6033 if ($selected == $res->rowid) {
6034 $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
6035 } else {
6036 $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
6037 }
6038 }
6039 $return .= '</select>';
6040
6041 $return .= ajax_combobox($htmlname);
6042 }
6043 return $return;
6044 }
6045
6046 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6047
6063 public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0, $addentrynone = 0)
6064 {
6065 // phpcs:enable
6066 global $langs;
6067
6068 $out = '';
6069
6070 $langs->loadLangs(array("admin", "banks"));
6071 $num = 0;
6072
6073 $sql = "SELECT rowid, label, bank, clos as status, currency_code";
6074 $sql .= " FROM " . $this->db->prefix() . "bank_account";
6075 $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
6076 if ($status != 2) {
6077 $sql .= " AND clos = " . (int) $status;
6078 }
6079 if ($filtre) {
6080 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6081 }
6082 $sql .= " ORDER BY label";
6083
6084 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6085 $result = $this->db->query($sql);
6086 if ($result) {
6087 $num = $this->db->num_rows($result);
6088 $i = 0;
6089
6090 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6091
6092 if ($num == 0) {
6093 if ($status == 0) {
6094 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
6095 } else {
6096 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6097 }
6098 } else {
6099 if (!empty($useempty) && !is_numeric($useempty)) {
6100 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6101 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6102 $out .= '<option value="-1">&nbsp;</option>';
6103 }
6104 }
6105
6106 while ($i < $num) {
6107 $obj = $this->db->fetch_object($result);
6108
6109 $labeltoshow = trim($obj->label);
6110 $labeltoshowhtml = trim($obj->label);
6111 if ($showcurrency) {
6112 $labeltoshow .= ' (' . $obj->currency_code . ')';
6113 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $obj->currency_code . ')</span>';
6114 }
6115 if ($status == 2 && $obj->status == 1) {
6116 $labeltoshow .= ' (' . $langs->trans("Closed") . ')';
6117 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $langs->trans("Closed") . ')</span>';
6118 }
6119
6120 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6121 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'" selected>';
6122 } else {
6123 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'">';
6124 }
6125 $out .= $labeltoshow;
6126 $out .= '</option>';
6127 $i++;
6128 }
6129
6130 if (!empty($addentrynone)) {
6131 $out .= '<option value="-2"'.($selected == -2 ? ' selected="selected"' : '').' data-html="'.dolPrintHTMLForAttribute('<span class="opacitymedium">'.$langs->trans("None").'</span>').'">'.$langs->trans("None").'</option>';
6132 }
6133
6134 $out .= "</select>";
6135 $out .= ajax_combobox('select' . $htmlname);
6136 } else {
6137 dol_print_error($this->db);
6138 }
6139
6140 // Output or return
6141 if (empty($nooutput)) {
6142 print $out;
6143 } else {
6144 return $out;
6145 }
6146
6147 return $num;
6148 }
6149
6163 public function selectRib($selected = '', $htmlname = 'ribcompanyid', $filtre = '', $useempty = 0, $moreattrib = '', $showibanbic = 0, $morecss = '', $nooutput = 0)
6164 {
6165 // phpcs:enable
6166 global $langs;
6167
6168 $out = '';
6169
6170 $langs->loadLangs(array("admin", "banks"));
6171 $num = 0;
6172
6173 $sql = "SELECT rowid, label, bank, status, iban_prefix, bic, default_rib";
6174 $sql .= " FROM " . $this->db->prefix() . "societe_rib";
6175 $sql .= " WHERE type = 'ban'";
6176 if ($filtre) {
6177 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6178 }
6179 $sql .= " ORDER BY label";
6180 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6181 $result = $this->db->query($sql);
6182 if ($result) {
6183 $num = $this->db->num_rows($result);
6184 $i = 0;
6185
6186 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6187
6188 if ($num == 0) {
6189 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6190 } else {
6191 if (!empty($useempty) && !is_numeric($useempty)) {
6192 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6193 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6194 $out .= '<option value="-1">&nbsp;</option>';
6195 }
6196 }
6197
6198 while ($i < $num) {
6199 $obj = $this->db->fetch_object($result);
6200 $iban = dolDecrypt($obj->iban_prefix);
6201 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6202 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '" selected>';
6203 } else {
6204 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '">';
6205 }
6206 $out .= trim($obj->label);
6207 if ($showibanbic) {
6208 $out .= ' (' . $iban . '/' .$obj->bic. ')' . ($obj->default_rib ? ' ['.$langs->trans("ByDefault").']' : '');
6209 }
6210 $out .= '</option>';
6211 $i++;
6212 }
6213 $out .= "</select>";
6214 $out .= ajax_combobox('select' . $htmlname);
6215 } else {
6216 dol_print_error($this->db);
6217 }
6218
6219 // Output or return
6220 if (empty($nooutput)) {
6221 print $out;
6222 } else {
6223 return $out;
6224 }
6225
6226 return $num;
6227 }
6228
6240 public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
6241 {
6242 global $langs;
6243
6244 $langs->load("admin");
6245 $num = 0;
6246
6247 $sql = "SELECT rowid, name, fk_country, status, entity";
6248 $sql .= " FROM " . $this->db->prefix() . "establishment";
6249 $sql .= " WHERE 1=1";
6250 if ($status != 2) {
6251 $sql .= " AND status = " . (int) $status;
6252 }
6253 if ($filtre) {
6254 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6255 }
6256 $sql .= " ORDER BY name";
6257
6258 dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
6259 $result = $this->db->query($sql);
6260 if ($result) {
6261 $num = $this->db->num_rows($result);
6262 $i = 0;
6263 if ($num) {
6264 print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6265 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6266 print '<option value="-1">&nbsp;</option>';
6267 }
6268
6269 while ($i < $num) {
6270 $obj = $this->db->fetch_object($result);
6271 if ($selected == $obj->rowid) {
6272 print '<option value="' . $obj->rowid . '" selected>';
6273 } else {
6274 print '<option value="' . $obj->rowid . '">';
6275 }
6276 print trim($obj->name);
6277 if ($status == 2 && $obj->status == 1) {
6278 print ' (' . $langs->trans("Closed") . ')';
6279 }
6280 print '</option>';
6281 $i++;
6282 }
6283 print "</select>";
6284 } else {
6285 if ($status == 0) {
6286 print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
6287 } else {
6288 print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
6289 }
6290 }
6291
6292 return $num;
6293 } else {
6294 dol_print_error($this->db);
6295 return -1;
6296 }
6297 }
6298
6308 public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
6309 {
6310 global $langs;
6311 if ($htmlname != "none") {
6312 print '<form method="POST" action="' . $page . '">';
6313 print '<input type="hidden" name="action" value="setbankaccount">';
6314 print '<input type="hidden" name="token" value="' . newToken() . '">';
6315 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
6316 $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
6317 if ($nbaccountfound > 0) {
6318 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6319 }
6320 print '</form>';
6321 } else {
6322 $langs->load('banks');
6323
6324 if ($selected) {
6325 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
6326 $bankstatic = new Account($this->db);
6327 $result = $bankstatic->fetch((int) $selected);
6328 if ($result) {
6329 print $bankstatic->getNomUrl(1);
6330 }
6331 } else {
6332 print "&nbsp;";
6333 }
6334 }
6335 }
6336
6348 public function formRib($page, $selected = '', $htmlname = 'ribcompanyid', $filtre = '', $addempty = 0, $showibanbic = 0)
6349 {
6350 global $langs;
6351 if ($htmlname != "none") {
6352 print '<form method="POST" action="' . $page . '">';
6353 print '<input type="hidden" name="action" value="setbankaccountcustomer">';
6354 print '<input type="hidden" name="token" value="' . newToken() . '">';
6355 $nbaccountfound = $this->selectRib($selected, $htmlname, $filtre, $addempty, '', $showibanbic);
6356 if ($nbaccountfound > 0) {
6357 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6358 }
6359 print '</form>';
6360 } else {
6361 $langs->load('banks');
6362
6363 if ($selected) {
6364 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
6365 $bankstatic = new CompanyBankAccount($this->db);
6366 $result = $bankstatic->fetch((int) $selected);
6367 if ($result) {
6368 print $bankstatic->label;
6369 if ($showibanbic) {
6370 print ' (' . $bankstatic->iban . '/' .$bankstatic->bic. ')';
6371 }
6372 }
6373 } else {
6374 print "&nbsp;";
6375 }
6376 }
6377 }
6378
6388 public function selectCategories($categtype, $htmlname, $object = null)
6389 {
6390 global $langs;
6391
6392 $out = '';
6393
6394 $cate_arbo = $this->select_all_categories($categtype, '', '', 64, 0, 3);
6395
6396 $arrayselected = array();
6397 if (GETPOSTISARRAY($htmlname)) {
6398 $arrayselected = GETPOST($htmlname, 'array:int');
6399 } elseif (is_object($object) && $object->id > 0) {
6400 $c = new Categorie($this->db);
6401 $cats = $c->containing($object->id, $categtype);
6402 $arrayselected = array();
6403 foreach ($cats as $cat) {
6404 $arrayselected[] = $cat->id;
6405 }
6406 }
6407
6408 $out .= img_picto('', 'category', 'class="pictofixedwidth"');
6409 $out .= $this->multiselectarray($htmlname, $cate_arbo, $arrayselected, 0, 0, 'minwidth100 widthcentpercentminusxx', 0, 0);
6410
6411 if (!getDolGlobalString('CATEGORY_EDIT_IN_MENU_NOT_IN_POPUP')) {
6412 // Add html code to add the edit button and go back
6413 $jsonclose = 'doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'()';
6414 $urltoopen = '/categories/categorie_list.php?type='.urlencode($categtype).'&nosearch=1';
6415
6416 $s = dolButtonToOpenUrlInDialogPopup($htmlname, $langs->transnoentitiesnoconv("Categories"), img_picto('', 'add', 'class="editfielda"'), $urltoopen, '', '', '', $jsonclose);
6417 $out .= $s;
6418 // Add js code to add the edit button and go back
6419 $out .= '<!-- Add js code to open the popup for category/edit/add -->'."\n";
6420 $out .= '<script>function doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'() {
6421 console.log("doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).' has been called, we refresh the combo content + refresh select2...");
6422
6423 // Call an ajax to reload values and update the select
6424
6425 $.ajax({
6426 url: \''.DOL_URL_ROOT.'/core/ajax/fetchCategories.php\',
6427 data: {
6428 action: \'getCategories\',
6429 type: \''.dol_escape_htmltag($categtype).'\'
6430 },
6431 type: \'GET\',
6432 dataType: \'json\',
6433 success: function (data) {
6434 var $select = $(\'#'.dol_sanitizeKeyCode($htmlname).'\');
6435 var selectedValues = $select.val(); // This is an array of selected values
6436 console.log(selectedValues);
6437 $select.empty();
6438 $.each(data, function (index, item) {
6439 $select.append(\'<option value="\' + item.id + \'" data-html="\' + item.htmlforattribute + \'">\' + item.htmlforoption + \'</option>\');
6440 });
6441 $select.val(selectedValues);
6442 },
6443 error: function (xhr, status, error) {
6444 console.log("Error when loading ajax page : " + error);
6445 }
6446 });
6447
6448 // Refresh select2 to take account of new values (enough for small change)
6449 $("#'.dol_sanitizeKeyCode($htmlname).'").trigger("change");
6450
6451 // Alternative if change in select is complex
6452 /*
6453 $("#'.dol_sanitizeKeyCode($htmlname).'").select2("destroy");
6454 $("#'.dol_sanitizeKeyCode($htmlname).'").select2();
6455 */
6456 }</script>';
6457 }
6458
6459 return $out;
6460 }
6461
6462
6463 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6483 public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
6484 {
6485 // phpcs:enable
6486 global $langs;
6487
6488 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
6489
6490 $cat = new Categorie($this->db);
6491
6492 if (is_numeric($type)) {
6493 $type = array_search($type, $cat->MAP_ID); // For backward compatibility
6494 }
6495
6496 $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
6497
6498 $outarray = array();
6499 $outarrayrichhtml = array();
6500
6501
6502 $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6503 $num = 0;
6504 if (is_array($cate_arbo)) {
6505 $num = count($cate_arbo);
6506
6507 if (!$num) {
6508 $langs->load("categories");
6509 $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
6510 } else {
6511 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6512 $output .= '<option value="-1">&nbsp;</option>';
6513 }
6514 foreach ($cate_arbo as $key => $value) {
6515 if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
6516 $add = 'selected ';
6517 } else {
6518 $add = '';
6519 }
6520
6521 $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth"'.(empty($cate_arbo[$key]['color']) ? '' : ' style="color: #' . $cate_arbo[$key]['color'] . '"'));
6522 $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
6523
6524 $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
6525
6526 $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
6527
6528 $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
6529 $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
6530 $output .= '>';
6531 // The visible (truncated) label is rendered via data-html in
6532 // templateResult of the select2 combobox; the bare option text
6533 // must keep the full label so that the select2 search matcher
6534 // (ajax_combobox in core/lib/ajax.lib.php) can find a hit on
6535 // characters that lie outside the truncated portion.
6536 $output .= dol_escape_htmltag($cate_arbo[$key]['fulllabel']);
6537 $output .= '</option>';
6538
6539 $cate_arbo[$key]['data-html'] = $labeltoshow;
6540 }
6541 }
6542 }
6543 $output .= '</select>';
6544 $output .= "\n";
6545
6546 $this->num = $num;
6547
6548 if ($outputmode == 2) {
6549 // TODO: handle error when $cate_arbo is not an array
6550 return $cate_arbo;
6551 } elseif ($outputmode == 1) {
6552 return $outarray;
6553 } elseif ($outputmode == 3) {
6554 return $outarrayrichhtml;
6555 }
6556 return $output;
6557 }
6558
6567 public function getHelpBlock($content, $icon = 'fa-question-circle')
6568 {
6569 global $langs;
6570
6571 // Sanitize content (assuming it might contain HTML, but escaping text nodes if needed)
6572 // We trust the caller to pass safe HTML or translated strings.
6573
6574 $html = '<details class="dolibarr-help-block" style="margin-top:8px;">';
6575 $html .= '<summary style="cursor:pointer; color:#0056b3; font-weight:normal; list-style:none; font-size:0.9em; display:flex; align-items:center;">';
6576 $html .= '<span class="fa ' . $icon . '" style="margin-right:6px;"></span>';
6577 $html .= $langs->trans("Help"); // Standardized title
6578 $html .= '</summary>';
6579 $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;">';
6580 $html .= $content;
6581 $html .= '</div>';
6582 $html .= '</details>';
6583
6584 return $html;
6585 }
6586
6587 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6588
6607 public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
6608 {
6609 // phpcs:enable
6610 dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
6611 print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
6612 }
6613
6643 public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 600, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No', $helpContent = '')
6644 {
6645 global $langs, $conf;
6646
6647 $more = '';
6648 $formconfirm = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
6649
6650 $inputok = array();
6651 $inputko = array();
6652
6653 // Clean parameters
6654 $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
6655 if ($conf->browser->layout == 'phone') {
6656 $width = '95%';
6657 }
6658
6659 // Set height automatically if not defined
6660 if (empty($height)) {
6661 $height = 185;
6662 if (is_array($formquestion)) {
6663 $height += (count($formquestion) * 40);
6664 }
6665 if ($question) {
6666 $height += dol_nboflines_bis($question, 80) * 40;
6667 }
6668 }
6669
6670 if (is_array($formquestion) && !empty($formquestion)) {
6671 // First add hidden fields and value
6672 foreach ($formquestion as $key => $input) {
6673 if (is_array($input) && !empty($input)) {
6674 if ($input['type'] == 'hidden') {
6675 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6676 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6677
6678 $more .= '<input type="hidden" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . dol_escape_htmltag(isset($input['value']) ? $input['value'] : '') . '" class="' . $morecss . '"' . $moreattr . '>' . "\n"; // 'value' non fourni par tous les appelants
6679 }
6680 }
6681 }
6682
6683 // Now add questions
6684 $moreonecolumn = '';
6685 $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
6686 foreach ($formquestion as $key => $input) {
6687 if (is_array($input) && !empty($input)) {
6688 $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
6689 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6690 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6691
6692 if ($input['type'] == 'text' || $input['type'] == 'input') { // traditional input
6693 $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";
6694 } elseif ($input['type'] == 'password') {
6695 $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";
6696 } elseif ($input['type'] == 'textarea') {
6697 $moreonecolumn .= '<div class="margintoponly">';
6698 $moreonecolumn .= $input['label'] . '<br>';
6699 $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
6700 $moreonecolumn .= $input['value'];
6701 $moreonecolumn .= '</textarea>';
6702 $moreonecolumn .= '</div>';
6703 } elseif (in_array($input['type'], ['select', 'multiselect'])) {
6704 if (empty($morecss)) {
6705 $morecss = 'minwidth100';
6706 }
6707
6708 $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
6709 $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
6710 $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
6711 $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
6712 $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
6713 $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
6714 $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
6715
6716 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
6717 if (!empty($input['label'])) {
6718 $more .= $input['label'] . '</div><div class="tagtd left">';
6719 }
6720 if ($input['type'] == 'select') {
6721 $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);
6722 } else {
6723 $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);
6724 }
6725 $more .= '</div></div>' . "\n";
6726 } elseif ($input['type'] == 'checkbox') {
6727 $more .= '<div class="tagtr">';
6728 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
6729 $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
6730 if (isset($input['value']) && !is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
6731 $more .= ' checked';
6732 }
6733 if (isset($input['value']) && is_bool($input['value']) && $input['value']) {
6734 $more .= ' checked';
6735 }
6736 if (isset($input['disabled'])) {
6737 $more .= ' disabled';
6738 }
6739 $more .= ' /></div>';
6740 $more .= '</div>' . "\n";
6741 } elseif ($input['type'] == 'radio') {
6742 $i = 0;
6743 foreach ($input['values'] as $selkey => $selval) {
6744 $more .= '<div class="tagtr">';
6745 if (isset($input['label'])) {
6746 if ($i == 0) {
6747 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6748 } else {
6749 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
6750 }
6751 }
6752 $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;
6753 if (!empty($input['disabled'])) {
6754 $more .= ' disabled';
6755 }
6756 if (isset($input['default']) && $input['default'] === $selkey) {
6757 $more .= ' checked="checked"';
6758 }
6759 $more .= ' /> ';
6760 $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
6761 $more .= '</div></div>' . "\n";
6762 $i++;
6763 }
6764 } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
6765 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6766 $more .= '<div class="tagtd">';
6767 $addnowlink = (empty($input['datenow']) ? 0 : 1);
6768 $h = $m = 0;
6769 if ($input['type'] == 'datetime') {
6770 $h = isset($input['hours']) ? $input['hours'] : 1;
6771 $m = isset($input['minutes']) ? $input['minutes'] : 1;
6772 }
6773 $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
6774 $more .= '</div></div>'."\n";
6775 $formquestion[] = array('name' => $input['name'].'day');
6776 $formquestion[] = array('name' => $input['name'].'month');
6777 $formquestion[] = array('name' => $input['name'].'year');
6778 $formquestion[] = array('name' => $input['name'].'hour');
6779 $formquestion[] = array('name' => $input['name'].'min');
6780 } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
6781 $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
6782 if (!empty($input['label'])) {
6783 $more .= $input['label'] . '</div><div class="tagtd">';
6784 }
6785 if (!empty($input['value'])) {
6786 $more .= $input['value'];
6787 }
6788 $more .= '</div></div>' . "\n";
6789 } elseif ($input['type'] == 'onecolumn') {
6790 $moreonecolumn .= '<div class="margintoponly">';
6791 $moreonecolumn .= $input['value'];
6792 $moreonecolumn .= '</div>' . "\n";
6793 } elseif ($input['type'] == 'hidden') {
6794 // Do nothing more, already added by a previous loop
6795 } elseif ($input['type'] == 'separator') {
6796 $more .= '<br>';
6797 } else {
6798 $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
6799 }
6800 }
6801 }
6802 $more .= '</div>' . "\n";
6803 $more .= $moreonecolumn;
6804 }
6805
6806 // JQUERY method dialog is broken with smartphone, we use standard HTML.
6807 // 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
6808 // See page product/card.php for example
6809 if (!empty($conf->dol_use_jmobile)) {
6810 $useajax = 0;
6811 }
6812 if (empty($conf->use_javascript_ajax)) {
6813 $useajax = 0;
6814 }
6815
6816 if ($useajax) {
6817 $autoOpen = true;
6818 $dialogconfirm = 'dialog-confirm';
6819 $button = '';
6820 if (!is_numeric($useajax)) {
6821 $button = $useajax;
6822 $useajax = 1;
6823 $autoOpen = false;
6824 $dialogconfirm .= '-' . $button;
6825 }
6826 $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
6827 $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
6828
6829 // Add input fields into list of fields to read during submit (inputok and inputko)
6830 if (is_array($formquestion)) {
6831 foreach ($formquestion as $key => $input) {
6832 //print "xx ".$key." rr ".is_array($input)."<br>\n";
6833 // Add name of fields to propagate with the GET when submitting the form with button OK.
6834 if (is_array($input) && isset($input['name'])) {
6835 if (strpos($input['name'], ',') > 0) {
6836 $inputok = array_merge($inputok, explode(',', $input['name']));
6837 } else {
6838 array_push($inputok, $input['name']);
6839 }
6840 }
6841 // Add name of fields to propagate with the GET when submitting the form with button KO.
6842 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
6843 if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
6844 array_push($inputko, $input['name']);
6845 }
6846 }
6847 }
6848
6849 // Show JQuery confirm box.
6850 // Add 'flex-direction: column' and 'justify-content: space-between' to push content to top and buttons to bottom
6851 $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
6852 $formconfirm .= '<div style="display: flex; flex-direction: column; height: 100%;">';
6853 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6854 $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
6855 }
6856 if (!empty($more)) {
6857 $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
6858 }
6859 // NEW: Add help block if content provided
6860 if (!empty($helpContent)) {
6861 $formconfirm .= '<div style="text-align:left; margin-top:12px; padding-top:8px; border-top:1px solid #eee; clear:both;">';
6862 $formconfirm .= $this->getHelpBlock($helpContent);
6863 $formconfirm .= '</div>';
6864 }
6865 if (!empty($question)) {
6866 $formconfirm .= '<div class="confirmmessage" style="padding-top: 15px;">';
6867 $formconfirm .= img_help(0, '') . ' ' . $question;
6868 $formconfirm .= '</div>';
6869 }
6870 $formconfirm .= '</div>';
6871 $formconfirm .= '</div>' . "\n";
6872
6873 $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
6874 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
6875 $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
6876 $formconfirm .= 'jQuery(document).ready(function() {
6877 $(function() {
6878 $( "#' . $dialogconfirm . '" ).dialog({
6879 autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
6880 if ($newselectedchoice == 'no') {
6881 $formconfirm .= '
6882 open: function() {
6883 $(this).parent().find("button.ui-button:eq(2)").focus();
6884 },';
6885 }
6886
6887 $jsforcursor = '';
6888 if ($useajax == 1) {
6889 $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
6890 $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
6891 }
6892
6893 $postconfirmas = 'GET';
6894
6895 $formconfirm .= '
6896 resizable: false,
6897 height: \'' . dol_escape_js($height) . '\',
6898 width: \'' . dol_escape_js($width) . '\',
6899 modal: true,
6900 closeOnEscape: false,
6901 buttons: {
6902 "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
6903 var options = "token=' . urlencode(newToken()) . '";
6904 var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
6905 var page = \'' . dol_escape_js(!empty($page) ? $page : '') . '\';
6906 var pageyes = \'' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '\';
6907
6908 if (inputok.length > 0) {
6909 $.each(inputok, function(i, inputname) {
6910 var more = "";
6911 var inputvalue;
6912 if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
6913 inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
6914 } else {
6915 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6916 inputvalue = $("#" + inputname + more).val();
6917 }
6918 if (typeof inputvalue == "undefined") { inputvalue=""; }
6919 console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
6920 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6921 });
6922 }
6923 var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
6924 if (pageyes.length > 0) {';
6925 if ($postconfirmas == 'GET') {
6926 $formconfirm .= 'location.href = urljump;';
6927 } else {
6928 $formconfirm .= $jsforcursor;
6929 $formconfirm .= 'var post = $.post(
6930 pageyes,
6931 options,
6932 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6933 );';
6934 }
6935 $formconfirm .= '
6936 console.log("after post ok");
6937 }
6938 $(this).dialog("close");
6939 },
6940 "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
6941 var options = "token=' . urlencode(newToken()) . '";
6942 var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
6943 var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
6944 var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
6945 if (inputko.length > 0) {
6946 $.each(inputko, function(i, inputname) {
6947 var more = "";
6948 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6949 var inputvalue = $("#" + inputname + more).val();
6950 if (typeof inputvalue == "undefined") { inputvalue=""; }
6951 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6952 });
6953 }
6954 var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
6955 //alert(urljump);
6956 if (pageno.length > 0) {';
6957 if ($postconfirmas == 'GET') {
6958 $formconfirm .= 'location.href = urljump;';
6959 } else {
6960 $formconfirm .= $jsforcursor;
6961 $formconfirm .= 'var post = $.post(
6962 pageno,
6963 options,
6964 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6965 );';
6966 }
6967 $formconfirm .= '
6968 console.log("after post ko");
6969 }
6970 $(this).dialog("close");
6971 }
6972 }
6973 }
6974 );
6975
6976 var button = "' . $button . '";
6977 if (button.length > 0) {
6978 $( "#" + button ).click(function() {
6979 $("#' . $dialogconfirm . '").dialog("open");
6980 });
6981 }
6982 });
6983 });
6984 </script>';
6985 $formconfirm .= "<!-- end ajax formconfirm -->\n";
6986 } else {
6987 $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
6988
6989 if (empty($disableformtag)) {
6990 $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftnoright">' . "\n";
6991 }
6992
6993 $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
6994 $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
6995
6996 $formconfirm .= '<div class="valid">' . "\n";
6997
6998 // Line title
6999 $formconfirm .= '<div class="validtitre">';
7000 $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
7001 $formconfirm .= '</div>' . "\n";
7002
7003 // Line text
7004 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
7005 $formconfirm .= '<div class="valid">' . $formquestion['text'] . '</div>' . "\n";
7006 }
7007
7008 // Line form fields
7009 if ($more) {
7010 $formconfirm .= '<div>' . "\n";
7011 $formconfirm .= $more;
7012 $formconfirm .= '</div>' . "\n";
7013 }
7014
7015 // NEW: Help block row (between form fields and question)
7016 if (!empty($helpContent)) {
7017 $formconfirm .= '<div style="padding-top:8px; border-top:1px solid #888;">';
7018 $formconfirm .= $this->getHelpBlock($helpContent);
7019 $formconfirm .= '</div>' . "\n";
7020 }
7021
7022 // Let's add a row that acts as a spacer.
7023 $formconfirm .= '<div style="padding-top: 20px;"></div>' . "\n";
7024
7025 // Question row
7026 $formconfirm .= '<div class="inline-block">' . $question . '</div>';
7027
7028 $formconfirm .= '<div class="inline-block">';
7029 $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
7030 $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
7031 $formconfirm .= '</div>';
7032
7033 $formconfirm .= '</div>';
7034
7035 if (empty($disableformtag)) {
7036 $formconfirm .= "</form>\n";
7037 }
7038 $formconfirm .= '<br>';
7039
7040 if (!empty($conf->use_javascript_ajax)) {
7041 $formconfirm .= '<!-- code to disable button to avoid double clic -->';
7042 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
7043 $formconfirm .= '
7044 $(document).ready(function () {
7045 $(".confirmvalidatebutton").on("click", function() {
7046 console.log("We click on button confirmvalidatebutton");
7047 $(this).attr("disabled", "disabled");
7048 setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
7049 //console.log($(this).closest("form"));
7050 $(this).closest("form").submit();
7051 });
7052 });
7053 ';
7054 $formconfirm .= '</script>' . "\n";
7055 }
7056
7057 $formconfirm .= "<!-- end formconfirm -->\n";
7058 }
7059
7060 return $formconfirm;
7061 }
7062
7063 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7064
7081 public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '', $option = '')
7082 {
7083 // phpcs:enable
7084 global $langs;
7085
7086 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
7087 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
7088
7089 $out = '';
7090
7091 $formproject = new FormProjets($this->db);
7092
7093 $langs->load("project");
7094 if ($htmlname != "none") {
7095 $out .= '<form method="post" action="' . $page . '">';
7096 $out .= '<input type="hidden" name="action" value="classin">';
7097 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7098 $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
7099 $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7100 $out .= '</form>';
7101 } else {
7102 $out .= '<span class="project_head_block">';
7103 if ($selected instanceof Project) {
7104 $out .= $selected->getNomUrl(0, $option, 1);
7105 } elseif (is_numeric($selected)) {
7106 $projet = new Project($this->db);
7107 $projet->fetch((int) $selected);
7108 $out .= $projet->getNomUrl(0, $option, 1);
7109 } else {
7110 $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
7111 }
7112 $out .= '</span>';
7113 }
7114
7115 if (empty($nooutput)) {
7116 print $out;
7117 return '';
7118 }
7119 return $out;
7120 }
7121
7122 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7123
7139 public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
7140 {
7141 // phpcs:enable
7142 global $langs;
7143
7144 $selected = (int) $selected;
7145
7146 $out = '';
7147
7148 if ($htmlname != "none") {
7149 $out .= '<form method="POST" action="' . $page . '">';
7150 $out .= '<input type="hidden" name="action" value="setconditions">';
7151 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7152 if ($type) {
7153 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7154 }
7155 $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
7156 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7157 $out .= '</form>';
7158 } else {
7159 if ($selected) {
7160 $this->load_cache_conditions_paiements();
7161
7162 if (isset($this->cache_conditions_paiements[$selected])) {
7163 $label = $this->cache_conditions_paiements[$selected]['label'];
7164
7165 if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
7166 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
7167 }
7168
7169 $out .= $label;
7170 } else {
7171 $langs->load('errors');
7172 $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions', $selected);
7173 }
7174 } else {
7175 $out .= '&nbsp;';
7176 }
7177 }
7178
7179 if (empty($nooutput)) {
7180 print $out;
7181 return '';
7182 }
7183 return $out;
7184 }
7185
7186 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7187
7198 public function form_rule_for_lines_dates($page, $selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0, $nooutput = 0): string
7199 {
7200 // phpcs:enable
7201 global $langs;
7202
7203 $out = '';
7204
7205 if ($htmlname != 'none') {
7206 $out .= '<form method="POST" action="' . $page . '">';
7207 $out .= '<input type="hidden" name="action" value="setruleforlinesdates">';
7208 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7209 $out .= $this->getSelectRuleForLinesDates($selected, $htmlname, $addempty);
7210 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7211 $out .= '</form>';
7212 } else {
7213 if (isset($selected)) {
7214 $this->load_cache_rule_for_lines_dates();
7215 if (isset($this->cache_rule_for_lines_dates[$selected])) {
7216 $label = $this->cache_rule_for_lines_dates[$selected];
7217 $out .= $langs->trans($label);
7218 }
7219 } else {
7220 $out .= '&nbsp;';
7221 }
7222 }
7223
7224 if (empty($nooutput)) {
7225 print $out;
7226 return '';
7227 }
7228
7229 return $out;
7230 }
7231
7232 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7233
7243 public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
7244 {
7245 dol_syslog(__METHOD__, LOG_DEBUG);
7246 // phpcs:enable
7247 global $langs;
7248 if ($htmlname != "none") {
7249 print '<form method="post" action="' . $page . '">';
7250 print '<input type="hidden" name="action" value="setavailability">';
7251 print '<input type="hidden" name="token" value="' . newToken() . '">';
7252 print $this->selectAvailabilityDelay($selected, $htmlname, '', $addempty, '', 1);
7253 print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7254 print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
7255 print '</form>';
7256 } else {
7257 if ($selected) {
7258 $this->load_cache_availability();
7259 // @phan-suppress-next-line PhanTypeMismatchProperty
7260 if (isset($this->cache_availability[$selected])) {
7261 print $this->cache_availability[$selected]['label'];
7262 } else {
7263 print "&nbsp;";
7264 }
7265 } else {
7266 print "&nbsp;";
7267 }
7268 }
7269 }
7270
7282 public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0, $morecss = '')
7283 {
7284 global $langs;
7285 if ($htmlname != "none") {
7286 print '<form method="post" action="' . $page . '">';
7287 print '<input type="hidden" name="action" value="setdemandreason">';
7288 print '<input type="hidden" name="token" value="' . newToken() . '">';
7289 $this->selectInputReason($selected, $htmlname, '-1', $addempty, $morecss);
7290 print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7291 print '</form>';
7292 } else {
7293 if ($selected) {
7294 $this->loadCacheInputReason();
7295 foreach ($this->cache_demand_reason as $key => $val) {
7296 if ($val['id'] == $selected) {
7297 print $val['label'];
7298 break;
7299 }
7300 }
7301 } else {
7302 print "&nbsp;";
7303 }
7304 }
7305 }
7306
7307 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7308
7322 public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
7323 {
7324 // phpcs:enable
7325 global $langs;
7326
7327 $ret = '';
7328
7329 if ($htmlname != "none") {
7330 $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7331 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
7332 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
7333 if ($type) {
7334 $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7335 }
7336 $ret .= '<table class="nobordernopadding">';
7337 $ret .= '<tr><td>';
7338 $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
7339 $ret .= '</td>';
7340 $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7341 $ret .= '</tr></table></form>';
7342 } else {
7343 if ($displayhour) {
7344 $ret .= dol_print_date($selected, 'dayhour');
7345 } else {
7346 $ret .= dol_print_date($selected, 'day');
7347 }
7348 }
7349
7350 if (empty($nooutput)) {
7351 print $ret;
7352 }
7353 return $ret;
7354 }
7355
7356
7357 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7358
7369 public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
7370 {
7371 // phpcs:enable
7372 global $langs;
7373
7374 if ($htmlname != "none") {
7375 print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7376 print '<input type="hidden" name="action" value="set' . $htmlname . '">';
7377 print '<input type="hidden" name="token" value="' . newToken() . '">';
7378 print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
7379 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7380 print '</form>';
7381 } else {
7382 if ($selected) {
7383 require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
7384 $theuser = new User($this->db);
7385 $theuser->fetch((int) $selected);
7386 print $theuser->getNomUrl(1);
7387 } else {
7388 print "&nbsp;";
7389 }
7390 }
7391 }
7392
7393
7394 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7395
7409 public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
7410 {
7411 // phpcs:enable
7412 global $langs;
7413
7414 $out = '';
7415 if ($htmlname != "none") {
7416 $out .= '<form method="POST" action="' . $page . '">';
7417 $out .= '<input type="hidden" name="action" value="setmode">';
7418 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7419 if ($type) {
7420 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7421 }
7422 $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
7423 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7424 $out .= '</form>';
7425 } else {
7426 if ($selected) {
7427 $this->load_cache_types_paiements();
7428 // @phan-suppress-next-line PhanTypeMismatchProperty
7429 $out .= isset($this->cache_types_paiements[(int) $selected]['label']) ? $this->cache_types_paiements[(int) $selected]['label'] : '';
7430 } else {
7431 $out .= "&nbsp;";
7432 }
7433 }
7434
7435 if ($nooutput) {
7436 return $out;
7437 } else {
7438 print $out;
7439 }
7440 return '';
7441 }
7442
7453 public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
7454 {
7455 global $langs;
7456 if ($htmlname != "none") {
7457 print '<form method="POST" action="' . $page . '">';
7458 print '<input type="hidden" name="action" value="settransportmode">';
7459 print '<input type="hidden" name="token" value="' . newToken() . '">';
7460 $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
7461 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7462 print '</form>';
7463 } else {
7464 if ($selected) {
7465 $this->load_cache_transport_mode();
7466 print $this->cache_transport_mode[$selected]['label'];
7467 } else {
7468 print "&nbsp;";
7469 }
7470 }
7471 }
7472
7473 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7474
7483 public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
7484 {
7485 // phpcs:enable
7486 global $langs;
7487 if ($htmlname != "none") {
7488 print '<form method="POST" action="' . $page . '">';
7489 print '<input type="hidden" name="action" value="setmulticurrencycode">';
7490 print '<input type="hidden" name="token" value="' . newToken() . '">';
7491 print $this->selectMultiCurrency($selected, $htmlname, 0);
7492 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7493 print '</form>';
7494 } else {
7495 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
7496 print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
7497 }
7498 }
7499
7500 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7501
7512 public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '', $rate_direct = 0.0)
7513 {
7514 // phpcs:enable
7515 global $langs, $conf;
7516
7517 if ($htmlname != "none") {
7518 print '<form method="POST" action="' . $page . '">';
7519 print '<input type="hidden" name="action" value="setmulticurrencyrate">';
7520 print '<input type="hidden" name="token" value="' . newToken() . '">';
7521 print '<input type="text" class="maxwidth75" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" spellcheck="false" /> ';
7522 print '<select name="calculation_mode" id="calculation_mode">';
7523 print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
7524 print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
7525 print '</select> ';
7526 print ajax_combobox("calculation_mode");
7527 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7528 print '</form>';
7529 } else {
7530 if (!empty($rate)) {
7531 print price($rate, 1, $langs, 0, 0);
7532 if ($currency && $rate != 1) {
7538 if (getDolGlobalString('MULTICURRENCY_USE_RATE_DIRECT')) {
7539 print ' &nbsp; <span class="opacitymedium">(' . price($rate_direct, 1, $langs, 0, 0) . ' ' . $conf->currency . ' = 1 ' . $currency . ')</span>';
7540 } else {
7541 print ' &nbsp; <span class="opacitymedium">(' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')</span>';
7542 }
7543 }
7544 } else {
7545 print 1;
7546 }
7547 }
7548 }
7549
7550 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7551
7569 public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0, $filterabsolutediscount = 0, $filtercreditnote = 0)
7570 {
7571 // phpcs:enable
7572 global $conf, $langs;
7573
7574 if ($htmlname != "none") {
7575 print '<form method="post" action="' . $page . '" class="inline-block">';
7576 print '<input type="hidden" name="action" value="setabsolutediscount">';
7577 print '<input type="hidden" name="token" value="' . newToken() . '">';
7578 print '<div class="inline-block">';
7579 if (!empty($discount_type)) {
7580 if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
7581 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
7582 $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7583 } else {
7584 $translationKey = 'HasCreditNoteFromSupplier';
7585 }
7586 } else {
7587 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7588 $translationKey = 'HasAbsoluteDiscountFromSupplier';
7589 } else {
7590 $translationKey = 'HasCreditNoteFromSupplier';
7591 }
7592 }
7593 } else {
7594 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
7595 if (!$filter || $filter == "fk_facture_source IS NULL") {
7596 $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7597 } else {
7598 $translationKey = 'CompanyHasCreditNote';
7599 }
7600 } else {
7601 if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7602 $translationKey = 'CompanyHasAbsoluteDiscount';
7603 } else {
7604 $translationKey = 'CompanyHasCreditNote';
7605 }
7606 }
7607 }
7608 print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
7609 if (empty($hidelist)) {
7610 print ' ';
7611 }
7612 print '</div>';
7613 if (empty($hidelist)) {
7614 print '<div class="inline-block" style="padding-right: 10px">';
7615 $newfilter = 'discount_type = ' . intval($discount_type);
7616 if (!empty($discount_type)) {
7617 $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
7618 } else {
7619 $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
7620 }
7621 if ($filter) {
7622 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
7623 $newfilter .= ' AND (' . $sanitizedfilter . ')';
7624 }
7625 // output the combo of discounts
7626 $nbqualifiedlines = $this->select_remises((string) $selected, $htmlname, $newfilter, $socid, $maxvalue);
7627 if ($nbqualifiedlines > 0) {
7628 print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
7629 if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7630 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7631 }
7632 if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7633 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7634 }
7635
7636 print '>';
7637 }
7638 print '</div>';
7639 }
7640 if ($more) {
7641 print '<div class="inline-block">';
7642 print $more;
7643 print '</div>';
7644 }
7645 print '</form>';
7646 } else {
7647 if ($selected) {
7648 print $selected;
7649 } else {
7650 print "0";
7651 }
7652 }
7653 }
7654
7655
7656 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7657
7667 public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
7668 {
7669 // phpcs:enable
7670 global $langs;
7671
7672 if ($htmlname != "none") {
7673 print '<form method="post" action="' . $page . '">';
7674 print '<input type="hidden" name="action" value="set_contact">';
7675 print '<input type="hidden" name="token" value="' . newToken() . '">';
7676 print '<table class="nobordernopadding">';
7677 print '<tr><td>';
7678 print $this->selectcontacts($societe->id, $selected, $htmlname);
7679 $num = $this->num;
7680 if ($num == 0) {
7681 $addcontact = (getDolGlobalString('SOCIETE_ADDRESSES_MANAGEMENT') ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
7682 print '<a href="' . DOL_URL_ROOT . '/contact/card.php?socid=' . $societe->id . '&action=create&backtoreferer=1">' . $addcontact . '</a>';
7683 }
7684 print '</td>';
7685 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7686 print '</tr></table></form>';
7687 } else {
7688 if ($selected) {
7689 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
7690 $contact = new Contact($this->db);
7691 $contact->fetch((int) $selected);
7692 print $contact->getFullName($langs);
7693 } else {
7694 print "&nbsp;";
7695 }
7696 }
7697 }
7698
7699 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7700
7717 public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
7718 {
7719 // phpcs:enable
7720 global $langs;
7721
7722 $out = '';
7723 if ($htmlname != "none") {
7724 $limit = getDolGlobalInt('THIRDPARTY_LIMIT_SIZE');
7725
7726 $out .= '<form method="post" action="' . $page . '">';
7727 $out .= '<input type="hidden" name="action" value="set_thirdparty">';
7728 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7729 $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, $limit, 'minwidth100', '', '', 1, array(), false, $excludeids);
7730 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7731 $out .= '</form>';
7732 } else {
7733 if ($selected) {
7734 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
7735 $soc = new Societe($this->db);
7736 $soc->fetch((int) $selected);
7737 $out .= $soc->getNomUrl(0, '');
7738 } else {
7739 $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
7740 }
7741 }
7742
7743 if ($nooutput) {
7744 return $out;
7745 } else {
7746 print $out;
7747 }
7748
7749 return '';
7750 }
7751
7752 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7753
7762 public function select_currency($selected = '', $htmlname = 'currency_id')
7763 {
7764 // phpcs:enable
7765 print $this->selectCurrency($selected, $htmlname);
7766 }
7767
7777 public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
7778 {
7779 global $langs, $user;
7780
7781 $langs->loadCacheCurrencies('');
7782
7783 $out = '';
7784
7785 if ($selected == 'euro' || $selected == 'euros') {
7786 $selected = 'EUR'; // Pour compatibilite
7787 }
7788
7789 $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
7790 if ($useempty) {
7791 $out .= '<option value="-1" selected></option>';
7792 }
7793 foreach ($langs->cache_currencies as $code_iso => $currency) {
7794 $labeltoshow = $currency['label'];
7795 if ($mode == 1) {
7796 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
7797 } elseif ($mode == 2) {
7798 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso.' - '.$langs->getCurrencySymbol($code_iso) . ')</span>';
7799 } else {
7800 $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
7801 }
7802
7803 if ($selected && $selected == $code_iso) {
7804 $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7805 } else {
7806 $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7807 }
7808 $out .= dol_string_nohtmltag($labeltoshow);
7809 $out .= '</option>';
7810 }
7811 $out .= '</select>';
7812 if ($user->admin) {
7813 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
7814 }
7815
7816 // Make select dynamic
7817 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7818 $out .= ajax_combobox($htmlname);
7819
7820 return $out;
7821 }
7822
7835 public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = 'maxwidth200 widthcentpercentminusx')
7836 {
7837 global $conf, $langs;
7838
7839 $langs->loadCacheCurrencies(''); // Load ->cache_currencies
7840
7841 $TCurrency = array();
7842
7843 $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
7844 $sql .= " WHERE entity IN ('" . getEntity('multicurrency') . "')";
7845 if ($filter) {
7846 $sql .= forgeSQLFromUniversalSearchCriteria($filter);
7847 }
7848 $resql = $this->db->query($sql);
7849 if ($resql) {
7850 while ($obj = $this->db->fetch_object($resql)) {
7851 $TCurrency[$obj->code] = $obj->code;
7852 }
7853 }
7854
7855 $out = '';
7856 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7857 if ($useempty) {
7858 $out .= '<option value="">&nbsp;</option>';
7859 }
7860 // If company current currency not in table, we add it into list. Should always be available.
7861 if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
7862 $TCurrency[$conf->currency] = $conf->currency;
7863 }
7864 if (count($TCurrency) > 0) {
7865 foreach ($langs->cache_currencies as $code_iso => $currency) {
7866 if (isset($TCurrency[$code_iso])) {
7867 if (!empty($selected) && $selected == $code_iso) {
7868 $out .= '<option value="' . $code_iso . '" selected="selected">';
7869 } else {
7870 $out .= '<option value="' . $code_iso . '">';
7871 }
7872
7873 $out .= $currency['label'];
7874 $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
7875 $out .= '</option>';
7876 }
7877 }
7878 }
7879
7880 $out .= '</select>';
7881
7882 // Make select dynamic
7883 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7884 $out .= ajax_combobox($htmlname);
7885
7886 return $out;
7887 }
7888
7889 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7890
7897 public function load_cache_vatrates($country_code)
7898 {
7899 // phpcs:enable
7900 global $langs, $user;
7901
7902 $num = count($this->cache_vatrates);
7903 if ($num > 0) {
7904 return $num; // Cache already loaded
7905 }
7906
7907 dol_syslog(__METHOD__, LOG_DEBUG);
7908
7909 $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";
7910 $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
7911 $sql .= " WHERE t.fk_pays = c.rowid";
7912 $sql .= " AND t.active > 0";
7913 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7914 $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
7915 $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
7916
7917 $resql = $this->db->query($sql);
7918 if ($resql) {
7919 $num = $this->db->num_rows($resql);
7920 if ($num) {
7921 for ($i = 0; $i < $num; $i++) {
7922 $obj = $this->db->fetch_object($resql);
7923
7924 $tmparray = array();
7925 $tmparray['rowid'] = (int) $obj->rowid;
7926 $tmparray['type_vat'] = ($obj->type_vat <= 0 ? 0 : $obj->type_vat); // Some version have type_vat corrupted with value -1
7927 $tmparray['code'] = $obj->code;
7928 $tmparray['txtva'] = $obj->taux;
7929 $tmparray['nprtva'] = $obj->recuperableonly;
7930 $tmparray['localtax1'] = $obj->localtax1;
7931 $tmparray['localtax1_type'] = $obj->localtax1_type;
7932 $tmparray['localtax2'] = $obj->localtax2;
7933 $tmparray['localtax2_type'] = $obj->localtax1_type;
7934 $tmparray['einvoice_vatex'] = $obj->einvoice_vatex;
7935
7936 $tmparray['label'] = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
7937 $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
7938 $positiverates = '';
7939 if ($obj->taux) {
7940 $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
7941 }
7942 if ($obj->localtax1) {
7943 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
7944 }
7945 if ($obj->localtax2) {
7946 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
7947 }
7948 if (empty($positiverates)) {
7949 $positiverates = '0';
7950 }
7951 $tmparray['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
7952
7953 $this->cache_vatrates[$obj->rowid] = $tmparray;
7954 }
7955
7956 return $num;
7957 } else {
7958 $this->error = '<span class="error">';
7959 $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
7960 $reg = array();
7961 if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
7962 $langs->load("errors");
7963 $new_country_code = $reg[1];
7964 $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_country', 'code', 'rowid');
7965 $this->error .= '<br>'.$langs->trans("ErrorFixThisHere", DOL_URL_ROOT.'/admin/dict.php?id=10'.($country_id > 0 ? '&countryidforinsert='.$country_id : ''));
7966 }
7967 $this->error .= '</span>';
7968 return -1;
7969 }
7970 } else {
7971 $this->error = '<span class="error">' . $this->db->error() . '</span>';
7972 return -2;
7973 }
7974 }
7975
7976 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7977
8000 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)
8001 {
8002 // phpcs:enable
8003 global $langs, $mysoc, $hookmanager;
8004
8005 $langs->load('errors');
8006
8007 $return = '';
8008 // Bypass the default method
8009 $hookmanager->initHooks(array('commonobject'));
8010 $info_bits == 1 ? $is_npr = 1 : $is_npr = 0;
8011 $parameters = array(
8012 'htmlname' => $htmlname,
8013 'selectedrate' => $selectedrate,
8014 'seller' => $societe_vendeuse,
8015 'buyer' => $societe_acheteuse,
8016 'idprod' => $idprod,
8017 'is_npr' => $is_npr,
8018 'type' => $type,
8019 'options_only' => $options_only,
8020 'mode' => $mode,
8021 'type_vat' => $type_vat
8022 );
8023 $reshook = $hookmanager->executeHooks('load_tva', $parameters);
8024 if ($reshook > 0) {
8025 return $hookmanager->resPrint;
8026 } elseif ($reshook === 0) {
8027 $return .= $hookmanager->resPrint;
8028 }
8029
8030 // Define defaultnpr, defaultttx and defaultcode
8031 $defaultnpr = ($info_bits & 0x01);
8032 $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
8033 $defaulttx = str_replace('*', '', $selectedrate);
8034 $defaultcode = '';
8035 $reg = array();
8036 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8037 $defaultcode = $reg[1];
8038 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8039 }
8040 //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
8041
8042 // Check parameters
8043 if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
8044 if ($societe_vendeuse->id == $mysoc->id) {
8045 $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
8046 } else {
8047 $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
8048 }
8049 return $return;
8050 }
8051
8052 //var_dump($societe_acheteuse);
8053 //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";
8054 //exit;
8055
8056 // Define list of countries to use to search VAT rates to show
8057 // First we defined code_country to use to find list.
8058 // country_code must be a c_country ISO code (e.g. 'FR', 'CH'). In some setups it may hold a
8059 // country label (e.g. 'Suisse') instead, which would make the "c.code IN (...)" lookup done by
8060 // load_cache_vatrates() match nothing and wrongly force the VAT rate to 0%. A valid ISO code is
8061 // always 2 chars, so when the value is not a well formed ISO code (empty or a label) and we have
8062 // a valid country id, we recover the ISO code from the authoritative country id. This way we do
8063 // not run any SQL on each page access when we already have a valid ISO code.
8064 $sellercountrycode = is_object($societe_vendeuse) ? $societe_vendeuse->country_code : $mysoc->country_code;
8065 $sellercountryid = is_object($societe_vendeuse) ? $societe_vendeuse->country_id : $mysoc->country_id;
8066 if ((int) $sellercountryid > 0 && strlen((string) $sellercountrycode) != 2) {
8067 $tmpcountrycode = dol_getIdFromCode($this->db, (string) $sellercountryid, 'c_country', 'rowid', 'code');
8068 if (!empty($tmpcountrycode) && !is_numeric($tmpcountrycode)) {
8069 $sellercountrycode = $tmpcountrycode;
8070 }
8071 }
8072 $code_country = "'" . $sellercountrycode . "'"; // Pour compatibilite ascendente
8073
8074 if ($societe_vendeuse == $mysoc && getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) { // If option to have vat for end customer for services is on
8075 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8076 // If SERVICE_ARE_ECOMMERCE_200238EC=1 combo list vat rate of purchaser and seller countries
8077 // If SERVICE_ARE_ECOMMERCE_200238EC=2 combo list only the vat rate of the purchaser country
8078 $selectVatComboMode = getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC');
8079 if (is_object($societe_vendeuse) && is_object($societe_acheteuse) && isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()) {
8080 // We also add the buyer country code
8081 if (is_numeric($type)) {
8082 if ($type == 1) { // We know product is a service
8083 switch ($selectVatComboMode) {
8084 case '1':
8085 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8086 break;
8087 case '2':
8088 $code_country = "'" . $societe_acheteuse->country_code . "'";
8089 break;
8090 }
8091 }
8092 } elseif (!$idprod) { // We don't know type of product
8093 switch ($selectVatComboMode) {
8094 case '1':
8095 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8096 break;
8097 case '2':
8098 $code_country = "'" . $societe_acheteuse->country_code . "'";
8099 break;
8100 }
8101 } else {
8102 $prodstatic = new Product($this->db);
8103 $prodstatic->fetch($idprod);
8104 if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
8105 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8106 }
8107 }
8108 }
8109 }
8110
8111 // Now we load the list of VAT
8112 $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
8113
8114 // Keep only the VAT qualified for $type_vat (0 means all types)
8115 $arrayofvatrates = array();
8116 foreach ($this->cache_vatrates as $cachevalue) {
8117 if ($type_vat == 0 || empty($cachevalue['type_vat']) || $cachevalue['type_vat'] == $type_vat) {
8118 $arrayofvatrates[] = $cachevalue;
8119 }
8120 }
8121
8122 $num = count($arrayofvatrates);
8123 if ($num > 0) {
8124 // Define the vat rate to preselect (if defaulttx not forced so is -1 or '')
8125 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8126 // Define a default thirdparty to use if the seller or buyer is not defined
8127 $tmpthirdparty = new Societe($this->db);
8128 $tmpthirdparty->country_code = $mysoc->country_code;
8129
8130 $defaulttx = get_default_tva(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8131 $defaultnpr = get_default_npr(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8132
8133 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8134 $defaultcode = $reg[1];
8135 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8136 }
8137 if (empty($defaulttx)) {
8138 $defaultnpr = 0;
8139 }
8140 }
8141
8142 // If we fails to find a default vat rate, we take the last one in list
8143 // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
8144 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8145 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8146 // We take the last one found in list
8147 $defaulttx = $arrayofvatrates[$num - 1]['txtva'];
8148 } else {
8149 // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
8150 $defaulttx = '';
8151 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8152 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8153 }
8154 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8155 $defaultcode = $reg[1];
8156 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8157 }
8158 }
8159 }
8160
8161 // Disabled is true if the seller is not subject to VAT
8162 $disabled = false;
8163 $title = '';
8164 if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && empty($societe_vendeuse->tva_assuj)) {
8165 // 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
8166 // EXPENSEREPORT_OVERRIDE_VAT is a strange option that allow to override/enable VAT regardless of sellet vat option - needed for expense report if
8167 // expense report used for business expenses instead of using supplier invoices (but this is a very bad idea !)
8168 if (!getDolGlobalString('EXPENSEREPORT_OVERRIDE_VAT')) {
8169 $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
8170 $disabled = true;
8171 }
8172 }
8173
8174 if (!$options_only) {
8175 $return .= '<select class="flat valignmiddle minwidth75imp maxwidth100 right" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
8176 }
8177
8178 $selectedfound = false;
8179 foreach ($arrayofvatrates as $rate) {
8180 // Keep only 0 if seller is not subject to VAT
8181 if ($disabled && $rate['txtva'] != 0) {
8182 continue;
8183 }
8184
8185 // Define key to use into select list
8186 $key = $rate['txtva'];
8187 $key .= $rate['nprtva'] ? '*' : '';
8188 if ($mode > 0 && $rate['code']) {
8189 $key .= ' (' . $rate['code'] . ')';
8190 }
8191 if ($mode < 0) {
8192 $key = $rate['rowid'];
8193 }
8194
8195 $return .= '<option value="' . $key . '" data-vatid="'.$rate['rowid'].'"';
8196 if (!$selectedfound) {
8197 if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
8198 if ($defaultcode == $rate['code']) {
8199 $return .= ' selected';
8200 $selectedfound = true;
8201 }
8202 } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
8203 $return .= ' selected';
8204 $selectedfound = true;
8205 }
8206 }
8207 $return .= '>';
8208
8209 // Show label of VAT
8210 if ($mysoc->country_code == 'IN' || getDolGlobalString('MAIN_VAT_LABEL_IS_POSITIVE_RATES')) {
8211 // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
8212 $return .= $rate['labelpositiverates'];
8213 } else {
8214 // Simple label
8215 $return .= vatrate($rate['label']);
8216 }
8217
8218 //$return.=($rate['code']?' '.$rate['code']:'');
8219 $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
8220
8221 $return .= '</option>';
8222 }
8223
8224 if (!$options_only) {
8225 $return .= '</select>';
8226 //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
8227 }
8228 } else {
8229 $return .= $this->error;
8230 }
8231
8232 $this->num = $num;
8233 return $return;
8234 }
8235
8236
8237 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8238
8263 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 = '')
8264 {
8265 // phpcs:enable
8266 dol_syslog(__METHOD__ . ': using select_date is deprecated. Use selectDate instead.', LOG_WARNING);
8267 $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
8268 if (!empty($nooutput)) {
8269 return $retstring;
8270 }
8271 print $retstring;
8272
8273 return '';
8274 }
8275
8291 public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
8292 {
8293 global $langs;
8294
8295 $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
8296 if ($forcenewline) {
8297 $ret .= '<br>';
8298 }
8299 $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
8300 return $ret;
8301 }
8302
8331 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 = '')
8332 {
8333 global $conf, $langs;
8334
8335 if ($gm === 'auto') {
8336 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
8337 }
8338
8339 $retstring = '';
8340
8341 if ($prefix == '') {
8342 $prefix = 're';
8343 }
8344 if ($h == '') {
8345 $h = 0;
8346 }
8347 if ($m == '') {
8348 $m = 0;
8349 }
8350 $emptydate = 0;
8351 $emptyhours = 0;
8352 if ($stepminutes <= 0 || $stepminutes > 30) {
8353 $stepminutes = 1;
8354 }
8355 if ($empty == 1) {
8356 $emptydate = 1;
8357 $emptyhours = 1;
8358 }
8359 if ($empty == 2) {
8360 $emptydate = 0;
8361 $emptyhours = 1;
8362 }
8363 $orig_set_time = $set_time;
8364
8365 if ($set_time === '' && $emptydate == 0) {
8366 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8367 if ($gm == 'tzuser' || $gm == 'tzuserrel') {
8368 $set_time = dol_now($gm);
8369 } else {
8370 $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
8371 }
8372 }
8373
8374 // Analysis of the preselected date
8375 $reg = array();
8376 $shour = '';
8377 $smin = '';
8378 $ssec = '';
8379 if (!empty($set_time) && preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', (string) $set_time, $reg)) { // deprecated usage
8380 // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
8381 $syear = (!empty($reg[1]) ? $reg[1] : '');
8382 $smonth = (!empty($reg[2]) ? $reg[2] : '');
8383 $sday = (!empty($reg[3]) ? $reg[3] : '');
8384 $shour = (!empty($reg[4]) ? $reg[4] : '');
8385 $smin = (!empty($reg[5]) ? $reg[5] : '');
8386 } elseif (strval($set_time) != '' && $set_time != -1) {
8387 // set_time est un timestamps (0 possible)
8388 $syear = dol_print_date($set_time, "%Y", $gm);
8389 $smonth = dol_print_date($set_time, "%m", $gm);
8390 $sday = dol_print_date($set_time, "%d", $gm);
8391 if ($orig_set_time != '') {
8392 $shour = dol_print_date($set_time, "%H", $gm);
8393 $smin = dol_print_date($set_time, "%M", $gm);
8394 $ssec = dol_print_date($set_time, "%S", $gm);
8395 }
8396 } else {
8397 // Date est '' ou vaut -1
8398 $syear = '';
8399 $smonth = '';
8400 $sday = '';
8401 $shour = getDolGlobalString('MAIN_DEFAULT_DATE_HOUR', ($h == -1 ? '23' : ''));
8402 $smin = getDolGlobalString('MAIN_DEFAULT_DATE_MIN', ($h == -1 ? '59' : ''));
8403 $ssec = getDolGlobalString('MAIN_DEFAULT_DATE_SEC', ($h == -1 ? '59' : ''));
8404 }
8405 if ($h == 3 || $h == 4) {
8406 $shour = '';
8407 }
8408 if ($m == 3) {
8409 $smin = '';
8410 }
8411
8412 $nowgmt = dol_now('gmt');
8413 //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
8414
8415 // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
8416 $usecalendar = 'combo';
8417 if (!empty($conf->use_javascript_ajax) && (!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') != "none")) {
8418 $usecalendar = ((!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') == 'eldy') ? 'jquery' : getDolGlobalString("MAIN_POPUP_CALENDAR"));
8419 }
8420 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8421 // If we use a text browser or screen reader, we use the 'combo' date selector
8422 $usecalendar = 'html';
8423 }
8424
8425 if ($d) {
8426 // Show date with popup
8427 if ($usecalendar != 'combo') {
8428 // Set $format and $formatjs and $formatjquery
8429 $reduceformat = (!empty($conf->dol_optimize_smallscreen) ? 1 : 0); // Test on original $format param.
8430 if ($reduceformat) {
8431 $format = str_replace('%Y', '%y', $langs->transnoentitiesnoconv("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8432 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8433 $formatjs = str_replace('yyyy', 'yy', $langs->transnoentitiesnoconv("FormatDateShortJavaInput"));
8434 $formatjquery = str_replace('yyyy', 'yy', $langs->trans("FormatDateShortJQueryInput"));
8435 } else {
8436 $format = $langs->transnoentitiesnoconv("FormatDateShortInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8437 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8438 $formatjs = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8439 $formatjquery = $langs->trans("FormatDateShortJQueryInput");
8440 }
8441
8442 // Set formatted_date (for example: '%d/%m/%Y', '%m-%d-%y', ...
8443 $formatted_date = '';
8444 if (strval($set_time) != '' && $set_time != -1) {
8445 $formatted_date = dol_print_date($set_time, $format, $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8446 }
8447
8448 // Calendrier popup version eldy
8449 if ($usecalendar == "eldy") {
8450 // To have this manager working back, you must retrieve all functions showDP child found into the lib_head.js of v4 for example
8451 // and load the js that contains them so the call of showDP will works.
8452 /*
8453 // Input area to enter date manually
8454 $retstring .= '<!-- datepicker usecalendar=eldy --><input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formatted_date . '"';
8455 $retstring .= ($disabled ? ' disabled' : '');
8456 $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($formatjslong")) . '\'); "'; // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8457 $retstring .= ' autocomplete="off">';
8458
8459 // Icon calendar
8460 $retstringbuttom = '';
8461 if (!$disabled) {
8462 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
8463 $base = DOL_URL_ROOT . '/core/';
8464 $retstringbuttom .= ' onClick="showDP(\'' . dol_escape_js($base) . '\',\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\',\'' . dol_escape_js($langs->defaultlang) . '\');"';
8465 $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8466 } else {
8467 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8468 }
8469 $retstring = $retstringbuttom . $retstring;
8470
8471 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8472 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8473 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8474 */
8475 } elseif ($usecalendar == 'jquery' || $usecalendar == 'html') {
8476 if (!$disabled && $usecalendar != 'html') {
8477 // Output javascript for datepicker
8478 $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (idate('Y') - 100));
8479 $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (idate('Y') + 100));
8480
8481 $retstring .= '<!-- datepicker usecalendar='.$usecalendar.' --><script nonce="' . getNonce() . '" type="text/javascript">';
8482 $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
8483 dateFormat: '" . dol_escape_js($formatjquery) . "',
8484 autoclose: true,
8485 todayHighlight: true,
8486 yearRange: '" . $minYear . ":" . $maxYear . "',";
8487 if (!empty($conf->dol_use_jmobile)) {
8488 $retstring .= "
8489 beforeShow: function (input, datePicker) {
8490 input.disabled = true;
8491 },
8492 onClose: function (dateText, datePicker) {
8493 this.disabled = false;
8494 },
8495 ";
8496 }
8497 // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
8498 if (!getDolGlobalString('MAIN_POPUP_CALENDAR_ON_FOCUS')) {
8499 $buttonImage = $calendarpicto ?: DOL_URL_ROOT . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png";
8500 $retstring .= "
8501 showOn: 'button', /* both has problem with autocompletion */
8502 buttonImage: '" . $buttonImage . "',
8503 buttonImageOnly: true";
8504 }
8505 $retstring .= "
8506 }) });";
8507 $retstring .= "</script>";
8508 }
8509
8510 // Input area to enter date manually
8511 $retstring .= '<div class="nowraponall inline-block divfordateinput">';
8512 $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="'.($usecalendar == 'html' ? "date" : "text").'" class="maxwidthdate'.(getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' textbrowser' : '').' center" maxlength="11" value="'.$formatted_date.'"';
8513 $retstring .= ($disabled ? ' disabled' : '');
8514 $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
8515 $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
8516 $retstring .= ' autocomplete="off">';
8517
8518 // Icon calendar
8519 if ($disabled) {
8520 $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink ui-datepicker-notrigger"') . '</button>';
8521 $retstring .= $retstringbutton;
8522 }
8523
8524 $retstring .= '</div>';
8525 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8526 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8527 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8528 } else {
8529 $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
8530 }
8531 } else {
8532 // Show date with combo selects
8533 // Day
8534 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
8535
8536 if ($emptydate || $set_time == -1) {
8537 $retstring .= '<option value="0" selected>&nbsp;</option>';
8538 }
8539
8540 for ($day = 1; $day <= 31; $day++) {
8541 $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
8542 }
8543
8544 $retstring .= "</select>";
8545
8546 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
8547 if ($emptydate || $set_time == -1) {
8548 $retstring .= '<option value="0" selected>&nbsp;</option>';
8549 }
8550
8551 // Month
8552 for ($month = 1; $month <= 12; $month++) {
8553 $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
8554 $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
8555 $retstring .= "</option>";
8556 }
8557 $retstring .= "</select>";
8558
8559 // Year
8560 if ($emptydate || $set_time == -1) {
8561 $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 . '">';
8562 } else {
8563 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
8564
8565 $syear = (int) $syear;
8566 for ($year = $syear - 10; $year < (int) $syear + 10; $year++) {
8567 $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
8568 }
8569 $retstring .= "</select>\n";
8570 }
8571 }
8572 }
8573
8574 if ($d && $h) {
8575 $retstring .= (($h == 2 || $h == 4) ? '<br>' : ' ');
8576 $retstring .= '<span class="nowraponall">';
8577 }
8578
8579 if ($h) {
8580 $hourstart = 0;
8581 $hourend = 24;
8582 if ($openinghours != '') {
8583 $openinghours = explode(',', $openinghours);
8584 $hourstart = $openinghours[0];
8585 $hourend = $openinghours[1];
8586 if ($hourend < $hourstart) {
8587 $hourend = $hourstart;
8588 }
8589 }
8590
8591 // Show hour
8592 $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
8593 $retstring .= ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
8594 if ($emptyhours) {
8595 $retstring .= '<option value="-1">&nbsp;</option>';
8596 }
8597 for ($hour = $hourstart; $hour < $hourend; $hour++) {
8598 if (strlen($hour) < 2) {
8599 $hour = "0" . $hour;
8600 }
8601 $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
8602 $retstring .= '</option>';
8603 }
8604 $retstring .= '</select>';
8605
8606 if ($disabled) {
8607 $retstring .= '<input type="hidden" id="' . $prefix . 'hour" name="' . $prefix . 'hour" value="' . $shour . '">' . "\n";
8608 }
8609 if ($m) {
8610 $retstring .= ":";
8611 }
8612 }
8613
8614 if ($m) {
8615 // Show minutes
8616 $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
8617 $retstring .= ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
8618 if ($emptyhours) {
8619 $retstring .= '<option value="-1">&nbsp;</option>';
8620 }
8621 for ($min = 0; $min < 60; $min += $stepminutes) {
8622 $min_str = sprintf("%02d", $min);
8623 $retstring .= '<option value="' . $min_str . '"' . (($min_str == $smin) ? ' selected' : '') . '>' . $min_str . '</option>';
8624 }
8625 $retstring .= '</select>';
8626 if ($disabled) {
8627 $retstring .= '<input type="hidden" id="' . $prefix . 'min" name="' . $prefix . 'min" value="' . $smin . '">' . "\n";
8628 }
8629 // Add also seconds
8630 $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
8631 }
8632
8633 if ($d && $h) {
8634 $retstring .= '</span>';
8635 }
8636
8637 // Add a "Now" link
8638 if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
8639 // Script which will be inserted in the onClick of the "Now" link
8640 $reset_scripts = "";
8641 if ($addnowlink == 2) { // local computer time
8642 // pad add leading 0 on numbers
8643 $reset_scripts .= "Number.prototype.pad = function(size) {
8644 var s = String(this);
8645 while (s.length < (size || 2)) {s = '0' + s;}
8646 return s;
8647 };
8648 var d = new Date();";
8649 }
8650
8651 // Generate the date part, depending on the use or not of the javascript calendar
8652 if ($addnowlink == 1) { // server time expressed in user time setup
8653 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8654 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8655 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8656 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8657 } elseif ($addnowlink == 2) {
8658 /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
8659 * This break application for foreign languages.
8660 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
8661 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
8662 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
8663 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
8664 */
8665 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8666 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8667 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8668 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8669 }
8670 /*if ($usecalendar == "eldy")
8671 {
8672 $base=DOL_URL_ROOT.'/core/';
8673 $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
8674 }
8675 else
8676 {
8677 $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
8678 $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
8679 $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
8680 }*/
8681 // Update the hour part
8682 if ($h) {
8683 if ($fullday) {
8684 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8685 }
8686 //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
8687 if ($addnowlink == 1) {
8688 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8689 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8690 } elseif ($addnowlink == 2) {
8691 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
8692 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8693 }
8694
8695 if ($fullday) {
8696 $reset_scripts .= ' } ';
8697 }
8698 }
8699 // Update the minute part
8700 if ($m) {
8701 if ($fullday) {
8702 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8703 }
8704 //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
8705 if ($addnowlink == 1) {
8706 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8707 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8708 } elseif ($addnowlink == 2) {
8709 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
8710 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8711 }
8712 if ($fullday) {
8713 $reset_scripts .= ' } ';
8714 }
8715 }
8716 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8717 if ($reset_scripts && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8718 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
8719 $retstring .= $langs->trans("Now");
8720 $retstring .= '</button> ';
8721 }
8722 }
8723
8724 // Add a "Plus one hour" link
8725 if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
8726 // Script which will be inserted in the onClick of the "Add plusone" link
8727 $reset_scripts = "";
8728
8729 // Generate the date part, depending on the use or not of the javascript calendar
8730 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
8731 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8732 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8733 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8734 // Update the hour part
8735 if ($h) {
8736 if ($fullday) {
8737 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8738 }
8739 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8740 if ($fullday) {
8741 $reset_scripts .= ' } ';
8742 }
8743 }
8744 // Update the minute part
8745 if ($m) {
8746 if ($fullday) {
8747 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8748 }
8749 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8750 if ($fullday) {
8751 $reset_scripts .= ' } ';
8752 }
8753 }
8754 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8755 if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
8756 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
8757 $retstring .= $langs->trans("DateStartPlusOne");
8758 $retstring .= '</button> ';
8759 }
8760 }
8761
8762 // Add a link to set data
8763 if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
8764 if (!is_array($adddateof)) {
8765 $arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
8766 } else {
8767 $arrayofdateof = $adddateof;
8768 }
8769 foreach ($arrayofdateof as $valuedateof) {
8770 $tmpadddateof = empty($valuedateof['adddateof']) ? 0 : $valuedateof['adddateof'];
8771 $tmplabeladddateof = empty($valuedateof['labeladddateof']) ? '' : $valuedateof['labeladddateof'];
8772 $tmparray = dol_getdate($tmpadddateof);
8773 if (empty($tmplabeladddateof)) {
8774 $tmplabeladddateof = $langs->trans("DateInvoice");
8775 }
8776 $reset_scripts = 'console.log(\'Click on now link\'); ';
8777 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($tmpadddateof, 'dayinputnoreduce').'\');';
8778 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
8779 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
8780 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
8781 $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="'.$reset_scripts.'">'.$tmplabeladddateof.'</button>';
8782 }
8783 }
8784
8785 return $retstring;
8786 }
8787
8797 public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array(), $morecss = 'minwidth75 maxwidth100')
8798 {
8799 global $langs;
8800
8801 $TDurationTypes = $this->getDurationTypes($langs);
8802
8803 // Removed undesired duration types
8804 foreach ($excludetypes as $value) {
8805 unset($TDurationTypes[$value]);
8806 }
8807
8808 $retstring = '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
8809 foreach ($TDurationTypes as $key => $typeduration) {
8810 $retstring .= '<option value="' . $key . '"';
8811 if ($key == $selected) {
8812 $retstring .= " selected";
8813 }
8814 $retstring .= ">" . $typeduration . "</option>";
8815 }
8816 $retstring .= "</select>";
8817
8818 $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
8819
8820 return $retstring;
8821 }
8822
8823 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8824
8838 public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
8839 {
8840 // phpcs:enable
8841 global $langs;
8842
8843 $retstring = '<span class="nowraponall">';
8844
8845 $hourSelected = '';
8846 $minSelected = '';
8847
8848 // Hours
8849 if ($iSecond != '') {
8850 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8851
8852 $hourSelected = convertSecondToTime($iSecond, 'allhour');
8853 $minSelected = convertSecondToTime($iSecond, 'min');
8854 }
8855
8856 if ($typehour == 'select') {
8857 $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
8858 for ($hour = 0; $hour < 25; $hour++) { // For a duration, we allow 24 hours
8859 $retstring .= '<option value="' . $hour . '"';
8860 if (is_numeric($hourSelected) && $hourSelected == $hour) {
8861 $retstring .= " selected";
8862 }
8863 $retstring .= ">" . $hour . "</option>";
8864 }
8865 $retstring .= "</select>";
8866 } elseif ($typehour == 'text' || $typehour == 'textselect') {
8867 $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
8868 } else {
8869 return 'BadValueForParameterTypeHour';
8870 }
8871
8872 if ($typehour != 'text') {
8873 $retstring .= ' ' . $langs->trans('HourShort');
8874 } else {
8875 $retstring .= '<span class="">:</span>';
8876 }
8877
8878 // Minutes
8879 if ($minunderhours) {
8880 $retstring .= '<br>';
8881 } else {
8882 if ($typehour != 'text') {
8883 $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
8884 }
8885 }
8886
8887 if ($typehour == 'select' || $typehour == 'textselect') {
8888 $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
8889 $step = getDolGlobalInt('MAIN_DURATION_STEP');
8890 $duration_step = ($step > 0) ? $step : 5;
8891 for ($min = 0; $min <= 59; $min += $duration_step) {
8892 $retstring .= '<option value="' . $min . '"';
8893 if (is_numeric($minSelected) && $minSelected == $min) {
8894 $retstring .= ' selected';
8895 }
8896 $retstring .= '>' . $min . '</option>';
8897 }
8898 $retstring .= "</select>";
8899 } elseif ($typehour == 'text') {
8900 $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
8901 }
8902
8903 if ($typehour != 'text') {
8904 $retstring .= ' ' . $langs->trans('MinuteShort');
8905 }
8906
8907 $retstring .= "</span>";
8908
8909 if (!empty($nooutput)) {
8910 return $retstring;
8911 }
8912
8913 print $retstring;
8914
8915 return '';
8916 }
8917
8937 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)
8938 {
8939 global $langs, $conf;
8940
8941 $out = '';
8942
8943 // check parameters
8944 if (is_null($ajaxoptions)) {
8945 $ajaxoptions = array();
8946 }
8947
8948 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
8949 $placeholder = '';
8950
8951 if ($selected && empty($selected_input_value)) {
8952 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
8953 $tickettmpselect = new Ticket($this->db);
8954 $tickettmpselect->fetch((int) $selected);
8955 $selected_input_value = $tickettmpselect->ref;
8956 unset($tickettmpselect);
8957 }
8958
8959 $urloption = '';
8960 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
8961
8962 if (empty($hidelabel)) {
8963 $out .= $langs->trans("RefOrLabel") . ' : ';
8964 } elseif ($hidelabel > 1) {
8965 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
8966 if ($hidelabel == 2) {
8967 $out .= img_picto($langs->trans("Search"), 'search');
8968 }
8969 }
8970 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
8971 if ($hidelabel == 3) {
8972 $out .= img_picto($langs->trans("Search"), 'search');
8973 }
8974 } else {
8975 $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
8976 }
8977
8978 if (empty($nooutput)) {
8979 print $out;
8980 } else {
8981 return $out;
8982 }
8983 return '';
8984 }
8985
8986
9003 public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9004 {
9005 global $langs;
9006
9007 $out = '';
9008 $outarray = array();
9009
9010 $selectFields = " p.rowid, p.ref, p.message";
9011
9012 $sql = "SELECT ";
9013 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9014 $sql .= " FROM " . $this->db->prefix() . "ticket as p";
9015 $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
9016
9017 // Add criteria on ref/label
9018 if ($filterkey != '') {
9019 $sql .= ' AND (';
9020 $prefix = getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if TICKET_DONOTSEARCH_ANYWHERE is on
9021 // For natural search
9022 $search_crit = explode(' ', $filterkey);
9023 $i = 0;
9024 if (count($search_crit) > 1) {
9025 $sql .= "(";
9026 }
9027 foreach ($search_crit as $crit) {
9028 if ($i > 0) {
9029 $sql .= " AND ";
9030 }
9031 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9032 $sql .= ")";
9033 $i++;
9034 }
9035 if (count($search_crit) > 1) {
9036 $sql .= ")";
9037 }
9038 $sql .= ')';
9039 }
9040
9041 $sql .= $this->db->plimit($limit, 0);
9042
9043 // Build output string
9044 dol_syslog(get_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
9045 $result = $this->db->query($sql);
9046 if ($result) {
9047 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
9048 require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
9049
9050 $num = $this->db->num_rows($result);
9051
9052 $events = array();
9053
9054 if (!$forcecombo) {
9055 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9056 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('TICKET_USE_SEARCH_TO_SELECT'));
9057 }
9058
9059 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9060
9061 $textifempty = '';
9062 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9063 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9064 if (getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9065 if ($showempty && !is_numeric($showempty)) {
9066 $textifempty = $langs->trans($showempty);
9067 } else {
9068 $textifempty .= $langs->trans("All");
9069 }
9070 } else {
9071 if ($showempty && !is_numeric($showempty)) {
9072 $textifempty = $langs->trans($showempty);
9073 }
9074 }
9075 if ($showempty) {
9076 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9077 }
9078
9079 $i = 0;
9080 while ($num && $i < $num) {
9081 $opt = '';
9082 $optJson = array();
9083 $objp = $this->db->fetch_object($result);
9084
9085 $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
9086 '@phan-var-force array{key:string,value:mixed,type:int} $optJson';
9087 // Add new entry
9088 // "key" value of json key array is used by jQuery automatically as selected value
9089 // "label" value of json key array is used by jQuery automatically as text for combo box
9090 $out .= $opt;
9091 array_push($outarray, $optJson);
9092
9093 $i++;
9094 }
9095
9096 $out .= '</select>';
9097
9098 $this->db->free($result);
9099
9100 if (empty($outputmode)) {
9101 return $out;
9102 }
9103 return $outarray;
9104 } else {
9105 dol_print_error($this->db);
9106 }
9107
9108 return array();
9109 }
9110
9122 protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9123 {
9124 $outkey = '';
9125 $outref = '';
9126 $outtype = '';
9127
9128 $outkey = $objp->rowid;
9129 $outref = $objp->ref;
9130
9131 $opt = '<option value="' . $objp->rowid . '"';
9132 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9133 $opt .= '>';
9134 $opt .= $objp->ref;
9135 $objRef = $objp->ref;
9136 if (!empty($filterkey) && $filterkey != '') {
9137 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
9138 }
9139
9140 $opt .= "</option>\n";
9141 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9142 }
9143
9163 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)
9164 {
9165 global $langs, $conf;
9166
9167 $out = '';
9168
9169 // check parameters
9170 if (is_null($ajaxoptions)) {
9171 $ajaxoptions = array();
9172 }
9173
9174 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9175 $placeholder = '';
9176
9177 if ($selected && empty($selected_input_value)) {
9178 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9179 $projecttmpselect = new Project($this->db);
9180 $projecttmpselect->fetch((int) $selected);
9181 $selected_input_value = $projecttmpselect->ref;
9182 unset($projecttmpselect);
9183 }
9184
9185 $urloption = '';
9186 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9187
9188 if (empty($hidelabel)) {
9189 $out .= $langs->trans("RefOrLabel") . ' : ';
9190 } elseif ($hidelabel > 1) {
9191 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9192 if ($hidelabel == 2) {
9193 $out .= img_picto($langs->trans("Search"), 'search');
9194 }
9195 }
9196 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9197 if ($hidelabel == 3) {
9198 $out .= img_picto($langs->trans("Search"), 'search');
9199 }
9200 } else {
9201 $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
9202 }
9203
9204 if (empty($nooutput)) {
9205 print $out;
9206 } else {
9207 return $out;
9208 }
9209 return '';
9210 }
9211
9228 public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9229 {
9230 global $langs, $conf;
9231
9232 $out = '';
9233 $outarray = array();
9234
9235 $selectFields = " p.rowid, p.ref";
9236
9237 $sql = "SELECT ";
9238 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9239 $sql .= " FROM " . $this->db->prefix() . "projet as p";
9240 $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
9241
9242 // Add criteria on ref/label
9243 if ($filterkey != '') {
9244 $sql .= ' AND (';
9245 $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9246 // For natural search
9247 $search_crit = explode(' ', $filterkey);
9248 $i = 0;
9249 if (count($search_crit) > 1) {
9250 $sql .= "(";
9251 }
9252 foreach ($search_crit as $crit) {
9253 if ($i > 0) {
9254 $sql .= " AND ";
9255 }
9256 $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9257 $sql .= "";
9258 $i++;
9259 }
9260 if (count($search_crit) > 1) {
9261 $sql .= ")";
9262 }
9263 $sql .= ')';
9264 }
9265
9266 $sql .= $this->db->plimit($limit, 0);
9267
9268 // Build output string
9269 dol_syslog(get_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
9270 $result = $this->db->query($sql);
9271 if ($result) {
9272 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9273 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
9274
9275 $num = $this->db->num_rows($result);
9276
9277 $events = array();
9278
9279 if (!$forcecombo) {
9280 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9281 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9282 }
9283
9284 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9285
9286 $textifempty = '';
9287 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9288 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9289 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9290 if ($showempty && !is_numeric($showempty)) {
9291 $textifempty = $langs->trans($showempty);
9292 } else {
9293 $textifempty .= $langs->trans("All");
9294 }
9295 } else {
9296 if ($showempty && !is_numeric($showempty)) {
9297 $textifempty = $langs->trans($showempty);
9298 }
9299 }
9300 if ($showempty) {
9301 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9302 }
9303
9304 $i = 0;
9305 while ($num && $i < $num) {
9306 $opt = '';
9307 $optJson = array();
9308 $objp = $this->db->fetch_object($result);
9309
9310 $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
9311 // Add new entry
9312 // "key" value of json key array is used by jQuery automatically as selected value
9313 // "label" value of json key array is used by jQuery automatically as text for combo box
9314 $out .= $opt;
9315 array_push($outarray, $optJson);
9316
9317 $i++;
9318 }
9319
9320 $out .= '</select>';
9321
9322 $this->db->free($result);
9323
9324 if (empty($outputmode)) {
9325 return $out;
9326 }
9327 return $outarray;
9328 } else {
9329 dol_print_error($this->db);
9330 }
9331
9332 return array();
9333 }
9334
9348 protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9349 {
9350 $outkey = '';
9351 $outref = '';
9352 $outtype = '';
9353
9354 $label = $objp->label;
9355
9356 $outkey = $objp->rowid;
9357 $outref = $objp->ref;
9358 $outlabel = $objp->label;
9359 $outtype = $objp->fk_product_type;
9360
9361 $opt = '<option value="' . $objp->rowid . '"';
9362 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9363 $opt .= '>';
9364 $opt .= $objp->ref;
9365 $objRef = $objp->ref;
9366 if (!empty($filterkey) && $filterkey != '') {
9367 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', (string) $objRef, 1);
9368 }
9369
9370 $opt .= "</option>\n";
9371 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9372 }
9373
9374
9395 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())
9396 {
9397 global $langs, $conf;
9398
9399 $out = '';
9400
9401 // check parameters
9402 if (is_null($ajaxoptions)) {
9403 $ajaxoptions = array();
9404 }
9405
9406 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9407 $placeholder = '';
9408
9409 if ($selected && empty($selected_input_value)) {
9410 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9411 $adherenttmpselect = new Adherent($this->db);
9412 $adherenttmpselect->fetch((int) $selected);
9413 $selected_input_value = $adherenttmpselect->ref;
9414 unset($adherenttmpselect);
9415 }
9416
9417 $urloption = '';
9418
9419 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9420
9421 if (empty($hidelabel)) {
9422 $out .= $langs->trans("RefOrLabel") . ' : ';
9423 } elseif ($hidelabel > 1) {
9424 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9425 if ($hidelabel == 2) {
9426 $out .= img_picto($langs->trans("Search"), 'search');
9427 }
9428 }
9429 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9430 if ($hidelabel == 3) {
9431 $out .= img_picto($langs->trans("Search"), 'search');
9432 }
9433 } else {
9434 $filterkey = '';
9435
9436 $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss, $excludeids);
9437 }
9438
9439 if (empty($nooutput)) {
9440 print $out;
9441 } else {
9442 return $out;
9443 }
9444 return '';
9445 }
9446
9464 public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $excludeids = array())
9465 {
9466 global $langs, $conf;
9467
9468 $out = '';
9469 $outarray = array();
9470
9471 $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
9472
9473 $sql = "SELECT ";
9474 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9475 $sql .= " FROM " . $this->db->prefix() . "adherent as p";
9476 $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
9477
9478 // Add criteria on ref/label
9479 if ($filterkey != '') {
9480 $sql .= ' AND (';
9481 $prefix = !getDolGlobalString('MEMBER_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9482 // For natural search
9483 $search_crit = explode(' ', $filterkey);
9484 $i = 0;
9485 if (count($search_crit) > 1) {
9486 $sql .= "(";
9487 }
9488 foreach ($search_crit as $crit) {
9489 if ($i > 0) {
9490 $sql .= " AND ";
9491 }
9492 $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9493 $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
9494 $i++;
9495 }
9496 if (count($search_crit) > 1) {
9497 $sql .= ")";
9498 }
9499 $sql .= ')';
9500 }
9501 if ($status != -1) {
9502 $sql .= ' AND statut = ' . ((int) $status);
9503 }
9504 if (!empty($excludeids)) {
9505 $sql .= " AND p.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
9506 }
9507 $sql .= $this->db->plimit($limit, 0);
9508
9509 // Build output string
9510 dol_syslog(get_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
9511 $result = $this->db->query($sql);
9512 if ($result) {
9513 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9514 require_once DOL_DOCUMENT_ROOT . '/core/lib/member.lib.php';
9515
9516 $num = $this->db->num_rows($result);
9517
9518 $events = array();
9519
9520 if (!$forcecombo) {
9521 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9522 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9523 }
9524
9525 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9526
9527 $textifempty = '';
9528 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9529 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9530 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9531 if ($showempty && !is_numeric($showempty)) {
9532 $textifempty = $langs->trans($showempty);
9533 } else {
9534 $textifempty .= $langs->trans("All");
9535 }
9536 } else {
9537 if ($showempty && !is_numeric($showempty)) {
9538 $textifempty = $langs->trans($showempty);
9539 }
9540 }
9541 if ($showempty) {
9542 $out .= '<option value="-1" selected>' . $textifempty . '</option>';
9543 }
9544
9545 $i = 0;
9546 while ($num && $i < $num) {
9547 $opt = '';
9548 $optJson = array();
9549 $objp = $this->db->fetch_object($result);
9550
9551 $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
9552
9553 // Add new entry
9554 // "key" value of json key array is used by jQuery automatically as selected value
9555 // "label" value of json key array is used by jQuery automatically as text for combo box
9556 $out .= $opt;
9557 array_push($outarray, $optJson);
9558
9559 $i++;
9560 }
9561
9562 $out .= '</select>';
9563
9564 $this->db->free($result);
9565
9566 if (empty($outputmode)) {
9567 return $out;
9568 }
9569 return $outarray;
9570 } else {
9571 dol_print_error($this->db);
9572 }
9573
9574 return array();
9575 }
9576
9588 protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9589 {
9590 $outkey = '';
9591 $outlabel = '';
9592 $outtype = '';
9593
9594 $outkey = $objp->rowid;
9595 $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
9596 $outtype = $objp->fk_adherent_type;
9597
9598 $opt = '<option value="' . $objp->rowid . '"';
9599 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9600 $opt .= '>';
9601 if (!empty($filterkey) && $filterkey != '') {
9602 $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
9603 }
9604 $opt .= $outlabel;
9605 $opt .= "</option>\n";
9606
9607 $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
9608 }
9609
9631 public function selectForForms($objectdesc, $htmlname, $preSelectedValue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '', $objectfield = '')
9632 {
9633 global $conf, $extrafields, $user, $hookmanager, $action;
9634
9635 // Example of common usage for a link to a thirdparty
9636
9637 // We got this in a modulebuilder form of "MyObject" of module "mymodule".
9638 // 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
9639 // $objectdesc = 'Societe'
9640 // $objectfield = Method 1: 'myobject@mymodule:fk_soc' ('fk_soc' is code to retrieve myobject->fields['fk_soc'])
9641 // 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__))" ...)
9642
9643 // We got this when showing an extrafields on resource that is a link to societe
9644 // 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
9645 // $objectdesc = 'Societe'
9646 // $objectfield = Method 1: 'resource:options_link_to_societe'
9647 // Method 2 recommended (it can be the array): array("type"=>'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...)
9648
9649 // With old usage:
9650 // $objectdesc = 'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))'
9651 // $objectfield = ''
9652
9653 //var_dump($objectdesc.' '.$objectfield);
9654 //debug_print_backtrace();
9655
9656 $objectdescorig = $objectdesc;
9657 $objecttmp = null;
9658 $InfoFieldList = array();
9659 $classname = '';
9660 $filter = ''; // Ensure filter has value (for static analysis)
9661 $sortfield = ''; // Ensure filter has value (for static analysis)
9662
9663 if (is_array($objectfield)) { // objectfield is an array
9664 $objectdesc = $objectfield['type'];
9665 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9666 } 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.
9667 // Example: $objectfield = 'product:options_package' or 'myobject@mymodule:options_myfield'
9668 $tmparray = explode(':', $objectfield);
9669
9670 // Get instance of object from $element
9671 $objectforfieldstmp = fetchObjectByElement(0, strtolower($tmparray[0]));
9672
9673 if (is_object($objectforfieldstmp)) {
9674 $objectdesc = '';
9675
9676 $reg = array();
9677 if (preg_match('/^options_(.*)$/', $tmparray[1], $reg)) {
9678 // For a property in extrafields
9679 $key = $reg[1];
9680 // fetch optionals attributes and labels
9681 $extrafields->fetch_name_optionals_label($objectforfieldstmp->table_element);
9682
9683 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key]) && $extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key] == 'link') {
9684 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options'])) {
9685 $tmpextrafields = array_keys($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options']);
9686 $objectdesc = $tmpextrafields[0];
9687 }
9688 }
9689 } else {
9690 // For a property in ->fields
9691 if (array_key_exists($tmparray[1], $objectforfieldstmp->fields)) {
9692 $objectdesc = $objectforfieldstmp->fields[$tmparray[1]]['type'];
9693 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9694 }
9695 }
9696 }
9697 }
9698
9699 if ($objectdesc) {
9700 // Example of value for $objectdesc:
9701 // Bom:bom/class/bom.class.php:0:t.status=1
9702 // Bom:bom/class/bom.class.php:0:t.status=1:ref
9703 // Bom:bom/class/bom.class.php:0:(t.status:=:1) OR (t.field2:=:2):ref
9704 $InfoFieldList = explode(":", $objectdesc, 4);
9705 $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
9706 $reg = array();
9707 if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
9708 $InfoFieldList[4] = $reg[1]; // take the sort field
9709 }
9710 $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp); // take the filter field
9711
9712 $classname = $InfoFieldList[0];
9713 $classpath = empty($InfoFieldList[1]) ? '' : $InfoFieldList[1];
9714 //$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
9715 $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
9716 $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
9717
9718 // Load object according to $id and $element
9719 $objecttmp = fetchObjectByElement(0, strtolower($InfoFieldList[0]));
9720
9721 // Fallback to another solution to get $objecttmp
9722 if (empty($objecttmp) && !empty($classpath)) {
9723 dol_include_once($classpath);
9724
9725 if ($classname && class_exists($classname)) {
9726 $objecttmp = new $classname($this->db);
9727 }
9728 }
9729 }
9730
9731 // Make some replacement in $filter. May not be used if we used the ajax mode with $objectfield. In such a case
9732 // we propagate the $objectfield and not the filter and replacement is done by the ajax/selectobject.php component.
9733 $sharedentities = (is_object($objecttmp) && property_exists($objecttmp, 'element')) ? getEntity($objecttmp->element) : strtolower($classname);
9734 $filter = str_replace(
9735 array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
9736 array($conf->entity, $sharedentities, $user->id),
9737 $filter
9738 );
9739
9740 if (!is_object($objecttmp)) {
9741 dol_syslog('selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc, LOG_WARNING);
9742 return 'selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc;
9743 }
9744 '@phan-var-force CommonObject $objecttmp';
9746 //var_dump($filter);
9747 $prefixforautocompletemode = $objecttmp->element;
9748 if ($prefixforautocompletemode == 'societe') {
9749 $prefixforautocompletemode = 'company';
9750 }
9751 if ($prefixforautocompletemode == 'product') {
9752 $prefixforautocompletemode = 'produit';
9753 }
9754
9755 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9756
9757 dol_syslog(get_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
9758
9759 // Generate the combo HTML component
9760 $out = '';
9761 if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
9762 // No immediate load of all database
9763 $placeholder = '';
9764
9765 if ($preSelectedValue && empty($selected_input_value)) {
9766 $objecttmp->fetch($preSelectedValue);
9767 $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
9768
9769 $oldValueForShowOnCombobox = 0;
9770 foreach ($objecttmp->fields as $fieldK => $fielV) {
9771 if (!array_key_exists('showoncombobox', $fielV) || !$fielV['showoncombobox'] || empty($objecttmp->$fieldK)) {
9772 continue;
9773 }
9774
9775 if (!$oldValueForShowOnCombobox) {
9776 $selected_input_value = '';
9777 }
9778
9779 $selected_input_value .= $oldValueForShowOnCombobox ? ' - ' : '';
9780 $selected_input_value .= $objecttmp->$fieldK;
9781 $oldValueForShowOnCombobox = empty($fielV['showoncombobox']) ? 0 : $fielV['showoncombobox'];
9782 }
9783 }
9784
9785 // Set url and param to call to get json of the search results
9786 $urlforajaxcall = DOL_URL_ROOT . '/core/ajax/selectobject.php';
9787 $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdescorig) . (is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9788 //$urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1'.(is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9789
9790 // Hook 'selectForFormsListUrl' - Added to allow modules to modify the AJAX URL
9791 $parameters = array(
9792 'urloption' => $urloption,
9793 'object' => $objecttmp,
9794 'htmlname' => $htmlname,
9795 'filter' => $filter,
9796 'searchkey' => $searchkey,
9797 );
9798 $reshook = $hookmanager->executeHooks('selectForFormsListUrl', $parameters, $objecttmp, $action);
9799 if (!empty($reshook)) {
9800 $urloption = $hookmanager->resPrint;
9801 $hookmanager->resPrint = '';
9802 }
9803
9804 // Activate the auto complete using ajax call.
9805 $out .= ajax_autocompleter((string) $preSelectedValue, $htmlname, $urlforajaxcall, $urloption, getDolGlobalInt($confkeyforautocompletemode), 0);
9806 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
9807 $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) . '"' : '') . ' />';
9808 } else {
9809 // Immediate load of table record.
9810 $out .= $this->selectForFormsList($objecttmp, $htmlname, $preSelectedValue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
9811 }
9812
9813 return $out;
9814 }
9815
9816
9838 public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '', $sortorder = 'ASC')
9839 {
9840 global $langs, $user, $hookmanager;
9841
9842 //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
9843
9844 $prefixforautocompletemode = $objecttmp->element;
9845 if ($prefixforautocompletemode == 'societe') {
9846 $prefixforautocompletemode = 'company';
9847 }
9848 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9849
9850 $fieldstoshow = '';
9851 if (!empty($objecttmp->fields)) { // For object that declare it, it is better to use declared fields (like societe, contact, ...)
9852 $tmpfieldstoshow = '';
9853 foreach ($objecttmp->fields as $key => $val) {
9854 if (! (int) dol_eval((string) $val['enabled'], 1, 1, '1')) {
9855 continue;
9856 }
9857 if (!empty($val['showoncombobox'])) {
9858 $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
9859 }
9860 }
9861 if ($tmpfieldstoshow) {
9862 $fieldstoshow = $tmpfieldstoshow;
9863 }
9864 } elseif ($objecttmp->element === 'category') {
9865 $fieldstoshow = 't.label';
9866 } else {
9867 // For backward compatibility
9868 $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'visible' => 4, 'showoncombobox' => 1);
9869 }
9870
9871 if (empty($fieldstoshow)) {
9872 if (!empty($objecttmp->parent_element)) {
9873 $fieldstoshow = 'o.ref';
9874 if (empty($sortfield)) {
9875 $sortfield = 'o.ref';
9876 }
9877 if (in_array($objecttmp->element, ['commandedet', 'propaldet', 'facturedet', 'expeditiondet'])) {
9878 $fieldstoshow .= ',p.ref AS p_ref,p.label,t.description';
9879 $sortfield .= ', p.ref';
9880 }
9881 } elseif (isset($objecttmp->fields['ref'])) {
9882 $fieldstoshow = 't.ref';
9883 } else {
9884 $langs->load("errors");
9885 $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
9886 return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
9887 }
9888 }
9889
9890 $out = '';
9891 $outarray = array();
9892 $tmparray = array();
9893
9894 $num = 0;
9895
9896 $sanitizedfieldstoshow = $fieldstoshow;
9897
9898 // Search data
9899 $sql = "SELECT t.rowid, " . $sanitizedfieldstoshow . " FROM " . $this->db->prefix() . $this->db->sanitize($objecttmp->table_element) . " as t";
9900 if (!empty($objecttmp->isextrafieldmanaged)) {
9901 $extrafieldTable = $objecttmp->table_element;
9902 if ($extrafieldTable == 'categorie') {
9903 $extrafieldTable = 'categories'; // For compatibility
9904 }
9905 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($extrafieldTable) . "_extrafields as e ON t.rowid = e.fk_object";
9906 }
9907 if (!empty($objecttmp->parent_element)) { // If parent_element is defined
9908 '@phan-var-force CommonObjectLine $objecttmp';
9909 $parent_properties = getElementProperties($objecttmp->parent_element);
9910 // @phan-suppress-next-line SqlInjection
9911 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($parent_properties['table_element']) . " as o ON o.rowid = t.".$this->db->sanitize($objecttmp->fk_parent_attribute);
9912 }
9913 if (!empty($objecttmp->parent_element) && in_array($objecttmp->parent_element, ['commande', 'propal', 'facture', 'expedition'])) {
9914 $sql .= " LEFT JOIN " . $this->db->prefix() . "product as p ON p.rowid = t.fk_product";
9915 }
9916 if (!empty($objecttmp->ismultientitymanaged)) {
9917 if ($objecttmp->ismultientitymanaged == 1) { // @phan-suppress-current-line PhanPluginEmptyStatementIf
9918 // No need to join/link another table
9919 }
9920 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9921 $tmparray = explode('@', $objecttmp->ismultientitymanaged);
9922 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($tmparray[1]) . " as parenttable ON parenttable.rowid = t." . $this->db->sanitize($tmparray[0]);
9923 }
9924 }
9925
9926 // Add where from hooks
9927 $parameters = array(
9928 'object' => $objecttmp,
9929 'htmlname' => $htmlname,
9930 'filter' => $filter,
9931 'searchkey' => $searchkey
9932 );
9933
9934 $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
9935 if (!empty($hookmanager->resPrint)) {
9936 $sql .= $hookmanager->resPrint;
9937 } else {
9938 $sql .= " WHERE 1=1";
9939
9940 // If table need a multientity restriction
9941 if (!empty($objecttmp->ismultientitymanaged)) {
9942 if ($objecttmp->ismultientitymanaged == 1) {
9943 $sql .= " AND t.entity IN (" . getEntity($objecttmp->element) . ")";
9944 }
9945 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9946 $sql .= " AND parenttable.entity = t." . $this->db->sanitize($tmparray[0]);
9947 }
9948 // If the parent table is llx_societe and user is not an external user (a more robust test done later for external users),
9949 // then we must also check that user has permissions
9950 if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
9951 if (!$user->hasRight('societe', 'client', 'voir') && empty($user->socid)) {
9952 $sql .= " AND EXISTS (SELECT sc.rowid FROM ".$this->db->prefix() . "societe_commerciaux as sc";
9953 $sql .= " WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $user->id).")";
9954 }
9955 }
9956 }
9957
9958 // If user is external user, we must also make a test on thirdparty
9959 if (!empty($user->socid)) {
9960 if ($objecttmp->element == 'societe') {
9961 $sql .= " AND t.rowid = " . ((int) $user->socid);
9962 } elseif (!empty($objecttmp->fields['fk_soc']) || !empty($objecttmp->fields['t.fk_soc']) || property_exists($objecttmp, 'fk_soc') || property_exists($objecttmp, 'socid')) {
9963 $sql .= " AND t.fk_soc = " . ((int) $user->socid);
9964 } elseif (!empty($objecttmp->parent_element)) {
9965 $tmpparent = fetchObjectByElement(0, $objecttmp->parent_element, '', 1);
9966 if (is_object($tmpparent) && (!empty($tmpparent->fields['fk_soc']) || !empty($tmpparent->fields['t.fk_soc']) || property_exists($tmpparent, 'fk_soc') || property_exists($tmpparent, 'socid'))) {
9967 $sql .= " AND o.fk_soc = " . ((int) $user->socid);
9968 }
9969 }
9970 }
9971
9972 $splittedfieldstoshow = explode(',', $fieldstoshow);
9973 foreach ($splittedfieldstoshow as &$field2) {
9974 if (is_numeric($pos = strpos($field2, ' '))) {
9975 $field2 = substr($field2, 0, $pos);
9976 }
9977 }
9978 if ($searchkey != '') {
9979 $sql .= natural_search($splittedfieldstoshow, $searchkey);
9980 }
9981
9982 if ($filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:>:'20160101')"
9983 $errormessage = '';
9984 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
9985 if ($errormessage) {
9986 return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
9987 }
9988 }
9989 }
9990 $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, $sortorder);
9991 //$sql.=$this->db->plimit($limit, 0);
9992 //print $sql;
9993
9994 // Build output string
9995 $resql = $this->db->query($sql);
9996 if ($resql) {
9997 // Construct $out and $outarray
9998 $out .= '<select id="' . $htmlname . '" class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
9999
10000 // 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
10001 $textifempty = '&nbsp;';
10002
10003 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
10004 if (getDolGlobalInt($confkeyforautocompletemode)) {
10005 if ($showempty && !is_numeric($showempty)) {
10006 $textifempty = $langs->trans($showempty);
10007 } else {
10008 $textifempty .= $langs->trans("All");
10009 }
10010 }
10011 if ($showempty) {
10012 $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
10013 }
10014
10015 $num = $this->db->num_rows($resql);
10016 $i = 0;
10017 if ($num) {
10018 while ($i < $num) {
10019 $obj = $this->db->fetch_object($resql);
10020 $label = '';
10021 $labelhtml = '';
10022 $tmparray = explode(',', $fieldstoshow);
10023 $oldvalueforshowoncombobox = 0;
10024 foreach ($tmparray as $key => $val) {
10025 $val = preg_replace('/(t|p|o)\./', '', $val);
10026 $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10027 $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10028 $label .= $obj->$val;
10029 $labelhtml .= $obj->$val;
10030
10031 $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
10032 }
10033 if (empty($outputmode)) {
10034 if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
10035 $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>';
10036 } else {
10037 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
10038 }
10039 } else {
10040 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
10041 }
10042
10043 $i++;
10044 if (($i % 10) == 0) {
10045 $out .= "\n";
10046 }
10047 }
10048 }
10049
10050 $out .= '</select>' . "\n";
10051
10052 if (!$forcecombo) {
10053 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10054 $out .= ajax_combobox($htmlname, array(), getDolGlobalInt($confkeyforautocompletemode, 0));
10055 }
10056 } else {
10057 dol_print_error($this->db);
10058 }
10059
10060 $this->result = array('nbofelement' => $num);
10061
10062 if ($outputmode) {
10063 return $outarray;
10064 }
10065 return $out;
10066 }
10067
10078 public static function radio($htmlName, $radioItems, $selected = '', $moreGlobalParams = [])
10079 {
10080 // Default parameters for each radio input
10081 $defaultParams = [
10082 'disabled' => false,
10083 'attr' => [
10084 'type' => 'radio',
10085 'name' => $htmlName,
10086 ],
10087 'attrLabel' => [],
10088 'labelIsHtml' => false
10089 ];
10090
10091 // Merge global parameters with defaults
10092 $params = array_merge_recursive_distinct($defaultParams, $moreGlobalParams);
10093
10094 $out = '';
10095 if (!empty($radioItems)) {
10096 foreach ($radioItems as $key => $item) {
10097 // Normalize item to array structure if it's a simple string
10098 if (!is_array($item)) {
10099 $item = [
10100 'attr' => [
10101 'value' => $key,
10102 ],
10103 'label' => $item
10104 ];
10105 }
10106
10107 // Default properties for individual item
10108 $defaultItem = [
10109 'attr' => [
10110 'value' => !isset($item['attr']['value']) ? $key : '',
10111 ],
10112 'label' => '',
10113 ];
10114
10115 // Merge defaults with global params and item-specific properties
10116 $defaultItem = array_merge_recursive_distinct($params, $defaultItem);
10117 $item = array_merge_recursive_distinct($defaultItem, $item);
10118
10119 // Determine if this radio should be checked
10120 if ((is_array($selected) && in_array($item['attr']['value'], $selected, true)) || $selected === $item['attr']['value']) {
10121 $item['attr']['checked'] = true;
10122 }
10123
10124 // Build HTML attributes for input and label
10125 $inputAttributes = implode(' ', commonHtmlAttributeBuilder($item['attr']));
10126 $labelAttributes = implode(' ', commonHtmlAttributeBuilder($item['attrLabel']));
10127
10128 // prevent accidental Xss todo : escape $item['label'] but html friendly compatible
10129 $text = $item['labelIsHtml'] ? $item['label'] : htmlspecialchars($item['label'], ENT_QUOTES | ENT_SUBSTITUTE);
10130
10131 // Generate HTML
10132 $out .= '<label ' . $labelAttributes . '><input ' . $inputAttributes . ' /> ' . $text . '</label> ';
10133 }
10134 }
10135
10136 return $out;
10137 }
10138
10139
10163 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)
10164 {
10165 global $conf, $langs;
10166
10167 // Do we want a multiselect ?
10168 //$jsbeautify = 0;
10169 //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
10170 $jsbeautify = 1;
10171
10172 if ($value_as_key) {
10173 $array = array_combine($array, $array);
10174 }
10175
10176 '@phan-var-force array{label:string,data-html:string,disable?:int<0,1>,css?:string} $array'; // Array combine breaks information
10177
10178 $out = '';
10179
10180 if ($addjscombo < 0) {
10181 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10182 $addjscombo = 1;
10183 } else {
10184 $addjscombo = 0;
10185 }
10186 }
10187 $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
10188 $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . ' selectformat"';
10189 $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
10190 $out .= '>'."\n";
10191
10192 if ($show_empty) {
10193 $textforempty = ' ';
10194 if (!empty($conf->use_javascript_ajax)) {
10195 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
10196 }
10197 if (!is_numeric($show_empty)) {
10198 $textforempty = $show_empty;
10199 }
10200 $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . (((int) $show_empty) < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
10201 }
10202 if (is_array($array)) {
10203 // Translate
10204 if ($translate) {
10205 foreach ($array as $key => $value) {
10206 if (!is_array($value)) {
10207 $array[$key] = $langs->trans($value);
10208 } else {
10209 $array[$key]['label'] = $langs->trans($value['label']);
10210 }
10211 }
10212 }
10213 // Sort
10214 if ($sort == 'ASC') {
10215 asort($array);
10216 } elseif ($sort == 'DESC') {
10217 arsort($array);
10218 }
10219
10220 foreach ($array as $key => $tmpvalue) {
10221 if (is_array($tmpvalue)) {
10222 $value = $tmpvalue['label'];
10223 //$valuehtml = empty($tmpvalue['data-html']) ? $value : $tmpvalue['data-html'];
10224 $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
10225 $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
10226 } else {
10227 $value = $tmpvalue;
10228 //$valuehtml = $tmpvalue;
10229 $disabled = '';
10230 $style = '';
10231 }
10232 if (!empty($disablebademail)) {
10233 if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
10234 || ($disablebademail == 2 && preg_match('/---/', $value))) {
10235 $disabled = ' disabled';
10236 $style = ' class="warning"';
10237 }
10238 }
10239 if ($key_in_label) {
10240 if (empty($nohtmlescape)) {
10241 $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
10242 } else {
10243 $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
10244 }
10245 } else {
10246 if (empty($nohtmlescape)) {
10247 $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
10248 } else {
10249 $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
10250 }
10251 if ($value == '' || $value == '-') {
10252 $selectOptionValue = '&nbsp;';
10253 }
10254 }
10255 $out .= '<option value="' . $key . '"';
10256 $out .= $style . $disabled;
10257 $out .= is_array($tmpvalue) && !empty($tmpvalue['parent']) ? ' parent="' . dolPrintHTMLForAttribute($tmpvalue['parent']) . '"' : '';
10258 if (is_array($id)) {
10259 if (in_array($key, $id) && !$disabled) {
10260 $out .= ' selected'; // To preselect a value
10261 }
10262 } else {
10263 $id = (string) $id; // if $id = 0, then $id = '0'
10264 if ($id != '' && (($id == (string) $key) || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
10265 $out .= ' selected'; // To preselect a value
10266 }
10267 }
10268
10269 if (is_array($tmpvalue)) {
10270 foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
10271 if ($keyforvalue == 'labelhtml') {
10272 $keyforvalue = 'data-html';
10273 }
10274 if (preg_match('/^data-/', $keyforvalue)) { // The best solution if you want to use HTML values into the list is to use data-html.
10275 $out .= ' '.dol_escape_htmltag($keyforvalue).'="'.dol_escape_htmltag($valueforvalue).'"';
10276 }
10277 }
10278 } elseif (!empty($nohtmlescape)) { // deprecated. Use instead the previous cas, an array with 'data-html', 'data-xxx' ... to use HTML content in the select
10279 $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
10280 }
10281
10282 $out .= '>';
10283 $out .= $selectOptionValue;
10284 $out .= "</option>\n";
10285 }
10286 }
10287 $out .= "</select>";
10288
10289 // Add code for jquery to use multiselect
10290 if ($addjscombo && $jsbeautify) {
10291 // Enhance with select2
10292 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10293 $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', (((int) $show_empty) < 0 ? (string) $show_empty : '-1'), $morecss);
10294 }
10295
10296 return $out;
10297 }
10298
10317 public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
10318 {
10319 global $conf;
10320 global $delayedhtmlcontent; // Will be used later outside of this function
10321
10322 // TODO Use an internal dolibarr component instead of select2
10323 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10324 return '';
10325 }
10326
10327 $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
10328
10329 $outdelayed = '';
10330 if (!empty($conf->use_javascript_ajax)) {
10331 $tmpplugin = 'select2';
10332 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10333 <script nonce="' . getNonce() . '">
10334 $(document).ready(function () {
10335
10336 ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
10337
10338 $(".' . $htmlname . '").select2({
10339 ajax: {
10340 dir: "ltr",
10341 url: "' . $url . '",
10342 dataType: \'json\',
10343 delay: 250,
10344 data: function (params) {
10345 return {
10346 q: params.term, // search term
10347 page: params.page
10348 }
10349 },
10350 processResults: function (data) {
10351 // parse the results into the format expected by Select2.
10352 // since we are using custom formatting functions we do not need to alter the remote JSON data
10353 //console.log(data);
10354 saveRemoteData = data;
10355 /* format json result for select2 */
10356 result = []
10357 $.each( data, function( key, value ) {
10358 result.push({id: key, text: value.text});
10359 });
10360 //return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
10361 //console.log(result);
10362 return {results: result, more: false}
10363 },
10364 cache: true
10365 },
10366 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10367 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10368 placeholder: \'' . dol_escape_js($placeholder) . '\',
10369 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10370 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10371 formatResult: function (result, container, query, escapeMarkup) {
10372 return escapeMarkup(result.text);
10373 },
10374 });
10375
10376 ' . ($callurlonselect ? '
10377 /* Code to execute a GET when we select a value */
10378 $(".' . $htmlname . '").change(function() {
10379 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10380 console.log("We select in selectArrayAjax the entry "+selected)
10381 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10382 $.each( saveRemoteData, function( key, value ) {
10383 if (key == selected)
10384 {
10385 console.log("selectArrayAjax - Do a redirect to "+value.url)
10386 location.assign(value.url);
10387 }
10388 });
10389 });' : '') . '
10390
10391 });
10392 </script>';
10393 }
10394
10395 if ($acceptdelayedhtml) {
10396 $delayedhtmlcontent .= $outdelayed;
10397 } else {
10398 $out .= $outdelayed;
10399 }
10400 return $out;
10401 }
10402
10422 public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
10423 {
10424 global $conf;
10425 global $delayedhtmlcontent; // Will be used later outside of this function
10426
10427 // TODO Use an internal dolibarr component instead of select2
10428 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10429 return '';
10430 }
10431
10432 $out = '<select type="text"'.($textfortitle ? ' title="'.dol_escape_htmltag($textfortitle).'"' : '').' id="'.$htmlname.'" class="'.$htmlname.($morecss ? ' ' . $morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.'"><option></option></select>';
10433
10434 $formattedarrayresult = array();
10435
10436 foreach ($array as $key => $value) {
10437 $o = new stdClass();
10438 $o->id = $key;
10439 $o->text = $value['text'];
10440 $o->url = $value['url'];
10441 $formattedarrayresult[] = $o;
10442 }
10443
10444 $outdelayed = '';
10445 if (!empty($conf->use_javascript_ajax)) {
10446 $tmpplugin = 'select2';
10447 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10448 <script nonce="' . getNonce() . '">
10449 $(document).ready(function () {
10450 var data = ' . json_encode($formattedarrayresult) . ';
10451
10452 ' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
10453
10454 $(\'.' . dol_escape_js($htmlname) . '\').select2({
10455 data: data,
10456 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10457 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10458 placeholder: \'' . dol_escape_js($placeholder) . '\',
10459 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10460 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10461 formatResult: function (result, container, query, escapeMarkup) {
10462 return escapeMarkup(result.text);
10463 },
10464 matcher: function (params, data) {
10465
10466 if(! data.id) return null;';
10467
10468 if ($callurlonselect) {
10469 // We forge the url with 'sall='
10470 $outdelayed .= '
10471
10472 var urlBase = data.url;
10473 var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
10474 /* console.log("params.term="+params.term); */
10475 /* console.log("params.term encoded="+encodeURIComponent(params.term)); */
10476 saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
10477 }
10478
10479 if (!$disableFiltering) {
10480 $outdelayed .= '
10481
10482 if(data.text.match(new RegExp(params.term))) {
10483 return data;
10484 }
10485
10486 return null;';
10487 } else {
10488 $outdelayed .= '
10489
10490 return data;';
10491 }
10492
10493 $outdelayed .= '
10494 }
10495 });
10496
10497 ' . ($callurlonselect ? '
10498 /* Code to execute a GET when we select a value */
10499 $(\'.' . dol_escape_js($htmlname) . '\').change(function() {
10500 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10501 console.log("We select "+selected)
10502
10503 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10504 $.each( saveRemoteData, function( key, value ) {
10505 if (key == selected)
10506 {
10507 console.log("selectArrayFilter - Do a redirect to "+value.url)
10508 location.assign(value.url);
10509 }
10510 });
10511 });' : '') . '
10512
10513 });
10514 </script>';
10515 }
10516
10517 if ($acceptdelayedhtml) {
10518 $delayedhtmlcontent .= $outdelayed;
10519 } else {
10520 $out .= $outdelayed;
10521 }
10522 return $out;
10523 }
10524
10543 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)
10544 {
10545 global $conf, $langs;
10546 $out = '';
10547
10548 if ($addjscombo < 0) {
10549 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10550 $addjscombo = 1;
10551 } else {
10552 $addjscombo = 0;
10553 }
10554 }
10555
10556 $useenhancedmultiselect = 0;
10557 if (!empty($conf->use_javascript_ajax) && !defined('MAIN_DO_NOT_USE_JQUERY_MULTISELECT') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
10558 if ($addjscombo) {
10559 $useenhancedmultiselect = 1; // Use the js multiselect in one line. Possible only if $addjscombo not 0.
10560 }
10561 }
10562
10563 $out .= '<span class="multiselectarray'.$htmlname.'">';
10564
10565 // We need a hidden field because when using the multiselect, if we unselect all, there is no
10566 // variable submitted at all, so no way to make a difference between variable not submitted and variable
10567 // submitted to nothing.
10568 $out .= '<input type="hidden" name="'.$htmlname.'_multiselect" value="1">';
10569 // Output select component
10570 $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";
10571 if (is_array($array) && !empty($array)) {
10572 if ($value_as_key) {
10573 $array = array_combine($array, $array);
10574 }
10575
10576 if (!empty($array)) {
10577 foreach ($array as $key => $value) {
10578 $tmpkey = $key;
10579 $tmplabel = $value;
10580 $tmplabelhtml = '';
10581 $tmpcolor = '';
10582 $tmppicto = '';
10583 $tmpdisabled = '';
10584 if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
10585 $tmpkey = $value['id'];
10586 $tmplabel = empty($value['label']) ? '' : $value['label'];
10587 $tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
10588 $tmpcolor = empty($value['color']) ? '' : $value['color'];
10589 $tmppicto = empty($value['picto']) ? '' : $value['picto'];
10590 $tmpdisabled = empty($value['disabled']) ? '' : $value['disabled'];
10591 }
10592 $newval = ($translate ? $langs->trans($tmplabel) : $tmplabel);
10593 $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
10594
10595 $tmplabelhtml = ($translate ? $langs->trans($tmplabelhtml) : $tmplabelhtml);
10596 $tmplabelhtml = ($key_in_label ? $tmpkey . ' - ' . $tmplabelhtml : $tmplabelhtml);
10597
10598 $out .= '<option value="' . $tmpkey . '"';
10599 if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
10600 $out .= ' selected';
10601 }
10602 $out .= is_array($value) && array_key_exists('parent', $value) && !empty($value['parent']) ? ' parent="' . dolPrintHTMLForAttribute($value['parent']) . '"' : '';
10603 if ($tmpdisabled) {
10604 $out .= ' disabled="disabled"';
10605 }
10606 if (!empty($tmplabelhtml)) {
10607 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10608 } else {
10609 $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
10610 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10611 }
10612 $out .= '>';
10613 $out .= dol_htmlentitiesbr($newval);
10614 $out .= '</option>' . "\n";
10615 }
10616 }
10617 }
10618 $out .= '</select>' . "\n";
10619
10620 $out .= '</span>';
10621
10622 // Add code for jquery to use multiselect
10623 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT')) {
10624 $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
10625 $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
10626 if ($addjscombo == 1) {
10627 $tmpplugin = getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT', (defined('REQUIRE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : 'select2'));
10628
10629 // If property data-html set, we decode html entities and use this.
10630 // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
10631 // TODO Move this into common js ?
10632 $out .= 'function formatResult(record, container) {' . "\n";
10633 $out .= ' if ($(record.element).attr("data-html") != undefined && typeof htmlEntityDecodeJs === "function") {';
10634 $out .= ' return htmlEntityDecodeJs($(record.element).attr("data-html"));';
10635 $out .= ' }'."\n";
10636 $out .= ' return record.text;';
10637 $out .= '}' . "\n";
10638
10639 $out .= 'function formatSelection(record) {' . "\n";
10640 $out .= ' return record.text;';
10641 $out .= '}' . "\n";
10642
10643 // Load the select2 enhancer
10644 //$out .= 'console.log(\'addjscombo=1 for htmlname=' . dol_escape_js($htmlname) . '\');';
10645 $out .= '$(document).ready(function () {
10646 $(\'#' . dol_escape_js($htmlname) . '\').' . $tmpplugin . '({';
10647 if ($placeholder) {
10648 $out .= '
10649 placeholder: {
10650 id: \'-1\',
10651 text: \''.dol_escape_js($placeholder).'\'
10652 },';
10653 }
10654 $out .= ' dir: \'ltr\',
10655 containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
10656 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. */
10657 // Specify format function for dropdown item
10658 formatResult: formatResult,
10659 templateResult: formatResult, /* For 4.0 */
10660 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10661 // Specify format function for selected item
10662 formatSelection: formatSelection,
10663 templateSelection: formatSelection, /* For 4.0 */
10664 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage
10665 });
10666
10667 /* Add also morecss to the css .select2 that is after the #htmlname, for component that are shown dynamically after load, because select2 set
10668 the size only if component is not hidden by default on load */
10669 $(\'#' . dol_escape_js($htmlname) . ' + .select2\').addClass(\'' . dol_escape_js($morecss) . '\');
10670 });' . "\n";
10671 } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
10672 // Add other js lib
10673 // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
10674 // ...
10675 $out .= 'console.log(\'addjscombo=2 for htmlname=' . dol_escape_js($htmlname) . '\');';
10676 $out .= '$(document).ready(function () {
10677 $(\'#' . dol_escape_js($htmlname) . '\').multiSelect({
10678 containerHTML: \'<div class="multi-select-container">\',
10679 menuHTML: \'<div class="multi-select-menu">\',
10680 buttonHTML: \'<span class="multi-select-button ' . dol_escape_js($morecss) . '">\',
10681 menuItemHTML: \'<label class="multi-select-menuitem">\',
10682 activeClass: \'multi-select-container--open\',
10683 noneText: \'' . dol_escape_js($placeholder) . '\'
10684 });
10685 })';
10686 }
10687 $out .= '</script>';
10688 }
10689
10690 return $out;
10691 }
10692
10693
10707 public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '', $draganddrop = 0)
10708 {
10709 global $conf, $langs, $user;
10710
10711 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10712 return '';
10713 }
10714 if (empty($array)) {
10715 return '';
10716 }
10717
10718 $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
10719
10720 if (!empty($user->conf->$tmpvar)) { // A list of fields was already customized for user
10721 $tmparray = explode(',', $user->conf->$tmpvar);
10722 foreach ($array as $key => $val) {
10723 //var_dump($key);
10724 //var_dump($tmparray);
10725 if (in_array($key, $tmparray)) {
10726 $array[$key]['checked'] = 1;
10727 } else {
10728 $array[$key]['checked'] = 0;
10729 }
10730 }
10731 } else { // There is no list of fields already customized for user
10732 foreach ($array as $key => $val) {
10733 if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
10734 $array[$key]['checked'] = 0;
10735 }
10736 }
10737 }
10738
10739 $listoffieldsforselection = '';
10740 $listcheckedstring = '';
10741
10742 foreach ($array as $key => $val) {
10743 // var_dump($val);
10744 // var_dump(array_key_exists('enabled', $val));
10745 // var_dump(!$val['enabled']);
10746 if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
10747 unset($array[$key]); // We don't want this field
10748 continue;
10749 }
10750 if (!empty($val['type']) && $val['type'] == 'separate') {
10751 // Field remains in array but we don't add it into $listoffieldsforselection
10752 //$listoffieldsforselection .= '<li>-----</li>';
10753 continue;
10754 }
10755 if (!empty($val['label']) && $val['label']) {
10756 if (!empty($val['langfile']) && is_object($langs)) {
10757 $langs->load($val['langfile']);
10758 }
10759
10760 // Note: $val['checked'] <> 0 means we must show the field into the combo list @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10761 $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']).'" />';
10762 $listoffieldsforselection .= '<label for="checkbox' . $key . '" class="paddingleft">';
10763 $listoffieldsforselection .= dolPrintHTML(dol_string_nohtmltag($langs->trans($val['label'])));
10764 $listoffieldsforselection .= '</label>';
10765 if (!empty($draganddrop)) {
10766 $listoffieldsforselection .= img_picto($langs->trans("MoveField", !empty($key) ? $key : 'none'), 'grip_title', 'class="opacitymedium boxhandle hideonsmartphone cursormove marginleftonly"');
10767 }
10768 $listoffieldsforselection .= '</li>';
10769 $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
10770 }
10771 }
10772
10773 $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
10774
10775 <dl class="dropdown">
10776 <dt>
10777 <a href="#' . $htmlname . '" class="multiselectpicto">
10778 ' . img_picto('', 'list') . '
10779 </a>
10780 <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
10781 </dt>
10782 <dd class="dropdowndd">
10783 <div class="multiselectcheckbox'.$htmlname.'">
10784 <ul class="'.$htmlname.(((string) $pos == '1' || (string) $pos == 'left') ? 'left' : '').(!empty($draganddrop) ? ' sortable' : '').'">
10785 <li class="liinputsearch">
10786 <input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="'.$langs->trans('Search').'">
10787 </li>
10788 '.$listoffieldsforselection.'
10789 </ul>
10790 </div>
10791 </dd>
10792 </dl>
10793
10794 <script>
10795 function updateFieldOrder() {
10796 var positionfields = $(".sortable").sortable("toArray");
10797 $.ajax({
10798 url: \''.DOL_URL_ROOT.'/core/ajax/changepositionfields.php?positionfields=\'+positionfields+\'&token='.newToken().'&action=listafterchangingpositionfields&contextpage='.$varpage.'&userid='.$user->id.'\',
10799 async: false,
10800 success: function () {
10801 // reload page
10802 window.location.href = "'.$_SERVER["PHP_SELF"].'";
10803 }
10804 });
10805 }
10806 $( ".sortable" ).sortable({
10807 handle: \'.boxhandle\',
10808 revert: \'invalid\',
10809 items: \'.fieldsortable\',
10810 stop: function(event, ui) {
10811 console.log("We moved box so we call updateBoxOrder with ajax actions");
10812 updateFieldOrder(); /* 1 to avoid message after a move */
10813 }
10814 });
10815 </script>
10816
10817 <script nonce="' . getNonce() . '" type="text/javascript">
10818 jQuery(document).ready(function () {
10819 $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on("click", function () {
10820 console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
10821
10822 $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\'); // Update field so we know we changed something on selected fields after POST
10823
10824 var title = $(this).val() + ",";
10825 if ($(this).is(\':checked\')) {
10826 $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
10827 }
10828 else {
10829 $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
10830 }
10831 // Now, we submit page
10832 //$(this).parents(\'form:first\').submit();
10833 });
10834
10835 $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
10836 console.log("keyup on inputsearch_dropdownselectedfields");
10837 var value = $(this).val().toLowerCase();
10838 $(\'.multiselectcheckbox'.$htmlname.' li > label\').filter(function() {
10839 $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
10840 });
10841 });
10842 ';
10843 if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') {
10844 $out .= '
10845 $(".dropdown dt a").on("click", function () {
10846 console.log("Click on dropdown, we set focus to search field");
10847 setTimeout(() => { $(\'.inputsearch_dropdownselectedfields\').focus(); }, 200);
10848 });';
10849 }
10850 $out .= '
10851 });
10852 </script>
10853
10854 ';
10855 return $out;
10856 }
10857
10867 public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
10868 {
10869 global $conf;
10870
10871 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
10872
10873 $cat = new Categorie($this->db);
10874 $categories = $cat->containing($id, $type);
10875
10876 if ($rendermode == 1 || $rendermode == 2) {
10877 $toprint = array();
10878 foreach ($categories as $c) {
10879 $ways = $c->print_all_ways('auto', ($nolink ? 'none' : ''), 0, 1, ($rendermode == 2 ? 0 : 1)); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
10880 foreach ($ways as $way) {
10881 $color = $c->color;
10882 $sfortag = '<li class="select2-search-choice-dolibarr noborderoncategories'.(empty($toprint) ? ' nomarginleft' : '');
10883 $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.
10884 if ($c->color && colorIsLight($c->color)) {
10885 $forced_color = 'categtextblack';
10886 }
10887 $sfortag .= ' '.$forced_color;
10888 $sfortag .= '"';
10889 $sfortag .= ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"');
10890 $titlestring = $ways[0];
10891 $titlestring = str_replace('>', ' - ', dol_string_nohtmltag($titlestring));
10892 $sfortag .= ' title="' . dolPrintHTMLForAttribute($titlestring) . '"';
10893 $sfortag .= '>';
10894 if ($rendermode == 1) {
10895 $sfortag .= '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.((int) $c->id).'&type='.urlencode($c->type).'" class="'.$forced_color.'">';
10896 $sfortag .= img_picto('', 'category', 'class="paddingright"');
10897 if ($conf->dol_optimize_smallscreen) {
10898 $sfortag .= dolPrintHTML(dol_trunc($c->label, 8));
10899 } else {
10900 $sfortag .= dolPrintHTML($c->label);
10901 }
10902 $sfortag .= '</a>';
10903 } else {
10904 $sfortag .= $way;
10905 }
10906 $sfortag .= '</li>';
10907
10908 $toprint[] = $sfortag; // Add tag in list of tag to show
10909 }
10910 }
10911 if (empty($toprint)) {
10912 return '';
10913 } else {
10914 return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
10915 }
10916 }
10917
10918 if ($rendermode == 0) {
10919 $arrayselected = array();
10920 $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 3);
10921 foreach ($categories as $c) {
10922 $arrayselected[(string) $c->id] = (string) $c->id;
10923 }
10924
10925 return $this->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, '', 0, '100%', 'disabled', 'category');
10926 }
10927
10928 return 'ErrorBadValueForParameterRenderMode'; // Should not happened
10929 }
10930
10940 public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = array(), $title = 'RelatedObjects')
10941 {
10942 global $conf, $langs, $hookmanager;
10943 global $action;
10944 global $db, $user; // Will be used into tpl
10945
10946 dol_syslog(__METHOD__, LOG_DEBUG);
10947
10948 $object->fetchObjectLinked();
10949
10950 // Bypass the default method
10951 $hookmanager->initHooks(array('commonobject'));
10952 $parameters = array(
10953 'morehtmlright' => $morehtmlright,
10954 'compatibleImportElementsList' => &$compatibleImportElementsList,
10955 );
10956 $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
10957
10958 $nbofdifferenttypes = count($object->linkedObjects);
10959
10960 if (empty($reshook)) {
10961 print '<!-- showLinkedObjectBlock -->';
10962 print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, '', 'showlinkedobjectblock');
10963
10964
10965 print '<div class="div-table-responsive-no-min">';
10966 print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '" data-elementid="' . $object->id . '" >';
10967
10968 print '<tr class="liste_titre">';
10969 print '<td>' . $langs->trans("Type") . '</td>';
10970 print '<td>' . $langs->trans("Ref") . '</td>';
10971 print '<td></td>';
10972 print '<td></td>';
10973 print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
10974 print '<td class="right">' . $langs->trans("Status") . '</td>';
10975 print '<td></td>';
10976 print '</tr>';
10977
10978 $nboftypesoutput = 0;
10979
10980 foreach ($object->linkedObjects as $objecttype => $objects) {
10981 $tplpath = $element = $subelement = $objecttype;
10982
10983 // to display import button on tpl
10984 global $showImportButton; // Will be used into tpl
10985 $showImportButton = false;
10986 if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
10987 $showImportButton = true;
10988 }
10989
10990 $regs = array();
10991
10992 if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
10993 $element = $regs[1];
10994 $subelement = $regs[2];
10995 $tplpath = $element . '/' . $subelement;
10996 }
10997 $tplname = 'linkedobjectblock';
10998
10999 // If we ask a resource form external module (instead of default path)
11000 if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) { // 'myobject@mymodule'
11001 $element = $regs[1];
11002 $module = $regs[2];
11003 $tplpath = $module. '/' . $element;
11004 $tplname = $tplname.'_'.$element;
11005 }
11006
11007 // To work with non standard path
11008 if ($objecttype == 'facture') {
11009 $tplpath = 'compta/' . $element;
11010 if (!isModEnabled('invoice')) {
11011 continue; // Do not show if module disabled
11012 }
11013 } elseif ($objecttype == 'facturerec') {
11014 $tplpath = 'compta/facture';
11015 $tplname = 'linkedobjectblockForRec';
11016 if (!isModEnabled('invoice')) {
11017 continue; // Do not show if module disabled
11018 }
11019 } elseif ($objecttype == 'propal') {
11020 $tplpath = 'comm/' . $element;
11021 if (!isModEnabled('propal')) {
11022 continue; // Do not show if module disabled
11023 }
11024 } elseif ($objecttype == 'supplier_proposal') {
11025 if (!isModEnabled('supplier_proposal')) {
11026 continue; // Do not show if module disabled
11027 }
11028 } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
11029 $tplpath = 'expedition';
11030 if (!isModEnabled('shipping')) {
11031 continue; // Do not show if module disabled
11032 }
11033 } elseif ($objecttype == 'reception') {
11034 $tplpath = 'reception';
11035 if (!isModEnabled('reception')) {
11036 continue; // Do not show if module disabled
11037 }
11038 } elseif ($objecttype == 'delivery') {
11039 $tplpath = 'delivery';
11040 if (!getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) {
11041 continue; // Do not show if sub module disabled
11042 }
11043 } elseif ($objecttype == 'ficheinter') {
11044 $tplpath = 'fichinter';
11045 if (!isModEnabled('intervention')) {
11046 continue; // Do not show if module disabled
11047 }
11048 } elseif ($objecttype == 'invoice_supplier') {
11049 $tplpath = 'fourn/facture';
11050 } elseif ($objecttype == 'order_supplier') {
11051 $tplpath = 'fourn/commande';
11052 } elseif ($objecttype == 'expensereport') {
11053 $tplpath = 'expensereport';
11054 } elseif ($objecttype == 'subscription') {
11055 $tplpath = 'adherents';
11056 } elseif ($objecttype == 'conferenceorbooth') {
11057 $tplpath = 'eventorganization';
11058 } elseif ($objecttype == 'conferenceorboothattendee') {
11059 $tplpath = 'eventorganization';
11060 } elseif ($objecttype == 'mo') {
11061 $tplpath = 'mrp';
11062 if (!isModEnabled('mrp')) {
11063 continue; // Do not show if module disabled
11064 }
11065 } elseif ($objecttype == 'project_task') {
11066 $tplpath = 'projet/tasks';
11067 }
11068
11069 global $linkedObjectBlock; // Will be used into tpl
11070 $linkedObjectBlock = $objects;
11071
11072 // Output template part (modules that overwrite templates must declare this into descriptor)
11073 $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
11074
11075 foreach ($dirtpls as $reldir) {
11076 $reldir = rtrim($reldir, '/');
11077 if ($nboftypesoutput == ($nbofdifferenttypes - 1)) { // No more type to show after
11078 global $noMoreLinkedObjectBlockAfter; // Will be used into tpl
11079 $noMoreLinkedObjectBlockAfter = 1;
11080 }
11081 $file = dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
11082 if (file_exists($file)) {
11083 $res = @include $file;
11084 if ($res) {
11085 $nboftypesoutput++;
11086 break;
11087 }
11088 }
11089 }
11090 }
11091
11092 if (!$nboftypesoutput) {
11093 print '<tr><td colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
11094 }
11095
11096 print '</table>';
11097
11098 if (!empty($compatibleImportElementsList)) {
11099 $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
11100 }
11101
11102 print '</div>';
11103 }
11104
11105 return $nbofdifferenttypes;
11106 }
11107
11117 public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array(), $nooutput = 0)
11118 {
11119 global $conf, $langs, $hookmanager, $form;
11120 global $action;
11121
11122 dol_syslog(__METHOD__, LOG_DEBUG);
11123
11124 if (empty($form)) {
11125 $form = new Form($this->db);
11126 }
11127
11128 $linktoelem = '';
11129 $linktoelemlist = '';
11130 $listofidcompanytoscan = '';
11131
11132 if (!is_object($object->thirdparty)) {
11133 if ($object->element == 'subscription' && isset($object->fk_adherent)) {
11134 $subby = new Subscription($object->db);
11135 $subby->fetch($object->id);
11136 $adh = new Adherent($object->db);
11137 //$fk_adherent = $object->fk_adherent;
11138 // creating new subscription object only to fetch the adherent which obviously exists given the if statement above are Inefficient, but else phan complains
11139 $fk_adherent = $subby->fk_adherent;
11140 $adh->fetch($fk_adherent);
11141 $thirdparty_id = $adh->fetch_thirdparty();
11142 }
11143 } else {
11144 $thirdparty_id = $object->thirdparty->id;
11145 }
11146
11147 $possiblelinks = array();
11148
11149 $dontIncludeCompletedItems = getDolGlobalString('DONT_INCLUDE_COMPLETED_ELEMENTS_LINKS');
11150
11151 if (!empty($thirdparty_id) && $thirdparty_id > 0) {
11152 $listofidcompanytoscan = (int) $thirdparty_id;
11153 if (is_object($object->thirdparty) && ($object->thirdparty->parent > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PARENT_IN_LINKTO')) {
11154 $listofidcompanytoscan .= ',' . (int) $object->thirdparty->parent;
11155 }
11156 if (($object->fk_project > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO')) {
11157 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
11158 $tmpproject = new Project($this->db);
11159 $tmpproject->fetch((int) $object->fk_project);
11160 if ($tmpproject->socid > 0 && ($tmpproject->socid != $thirdparty_id)) {
11161 $listofidcompanytoscan .= ',' . (int) $tmpproject->socid;
11162 }
11163 unset($tmpproject);
11164 }
11165
11166 $possiblelinks = array(
11167 'propal' => array(
11168 'enabled' => isModEnabled('propal'),
11169 'perms' => 1,
11170 'label' => 'LinkToProposal',
11171 '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' : ''),
11172 ),
11173 'shipping' => array(
11174 'enabled' => isModEnabled('shipping'),
11175 'perms' => 1,
11176 'label' => 'LinkToExpedition',
11177 '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' : ''),
11178 ),
11179 'order' => array(
11180 'enabled' => isModEnabled('order'),
11181 'perms' => 1,
11182 'label' => 'LinkToOrder',
11183 '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' : ''),
11184 'linkname' => 'commande',
11185 ),
11186 'subscription' => array(
11187 'enabled' => isModEnabled('member'),
11188 'perms' => 1,
11189 'label' => 'LinkToMemberSubscription',
11190 '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') . ')',
11191 'linkname' => 'subscription',
11192 ),
11193 'conferenceorboothattendee' => array(
11194 'enabled' => isModEnabled('eventorganization'),
11195 'perms' => 1,
11196 'label' => 'LinkToConferenceOrBoothAttendee',
11197 '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),
11198 'linkname' => 'attendee'
11199 ),
11200 'invoice' => array(
11201 'enabled' => isModEnabled('invoice'),
11202 'perms' => 1,
11203 'label' => 'LinkToInvoice',
11204 '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' : ''),
11205 'linkname' => 'facture',
11206 ),
11207 'invoice_template' => array(
11208 'enabled' => isModEnabled('invoice'),
11209 'perms' => 1,
11210 'label' => 'LinkToTemplateInvoice',
11211 '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') . ')',
11212 ),
11213 'contrat' => array(
11214 'enabled' => isModEnabled('contract'),
11215 'perms' => 1,
11216 'label' => 'LinkToContract',
11217 '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
11218 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',
11219 ),
11220 'fichinter' => array(
11221 'enabled' => isModEnabled('intervention'),
11222 'perms' => 1,
11223 'label' => 'LinkToIntervention',
11224 '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') . ')',
11225 ),
11226 'supplier_proposal' => array(
11227 'enabled' => isModEnabled('supplier_proposal'),
11228 'perms' => 1,
11229 'label' => 'LinkToSupplierProposal',
11230 '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' : ''),
11231 ),
11232 'order_supplier' => array(
11233 'enabled' => isModEnabled("supplier_order"),
11234 'perms' => 1,
11235 'label' => 'LinkToSupplierOrder',
11236 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "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' : ''),
11237 ),
11238 'invoice_supplier' => array(
11239 'enabled' => isModEnabled("supplier_invoice"),
11240 'perms' => 1, 'label' => 'LinkToSupplierInvoice',
11241 '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' : ''),
11242 ),
11243 'ticket' => array(
11244 'enabled' => isModEnabled('ticket'),
11245 'perms' => 1,
11246 'label' => 'LinkToTicket',
11247 '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' : ''),
11248 ),
11249 'mo' => array(
11250 'enabled' => isModEnabled('mrp'),
11251 'perms' => 1,
11252 'label' => 'LinkToMo',
11253 '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' : ''),
11254 ),
11255 );
11256 }
11257
11258 if ($object->table_element == 'commande_fournisseur') {
11259 $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' : '');
11260 } elseif ($object->table_element == 'mrp_mo') {
11261 $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' : '');
11262 }
11263
11264 $reshook = 0; // Ensure $reshook is defined for static analysis
11265 if (!empty($listofidcompanytoscan)) { // If empty, we don't have criteria to scan the object we can link to
11266 // Can complete the possiblelink array
11267 $hookmanager->initHooks(array('commonobject'));
11268 $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
11269 $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
11270 }
11271
11272 if (empty($reshook)) {
11273 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11274 $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
11275 }
11276 } elseif ($reshook > 0) {
11277 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11278 $possiblelinks = $hookmanager->resArray;
11279 }
11280 }
11281
11282 if (!empty($possiblelinks)) {
11283 $object->fetchObjectLinked();
11284 }
11285
11286 // Build the html part with possible suggested links
11287 $htmltoenteralink = '';
11288 foreach ($possiblelinks as $key => $possiblelink) {
11289 $num = 0;
11290 if (empty($possiblelink['enabled'])) {
11291 continue;
11292 }
11293
11294
11295 // If we ask a resource form external module (instead of default path)
11296 $module = '';
11297 if (preg_match('/^([^@]+)@([^@]+)$/i', $key, $regs)) { // 'myobject@mymodule'
11298 $key = $regs[1];
11299 $module = $regs[2];
11300 }
11301
11302 if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
11303 $htmltoenteralink .= '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
11304
11305 // Section for free ref input
11306 if (!getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11307 $htmltoenteralink .= '<br>'."\n";
11308 $htmltoenteralink .= '<!-- form to add a link from anywhere -->'."\n";
11309 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
11310 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11311 $htmltoenteralink .= '<input type="hidden" name="action" value="addlinkbyref">';
11312 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11313 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key .(!empty($module) ? '@'.$module : ''). '">';
11314 $htmltoenteralink .= '<table class="noborder">';
11315 $htmltoenteralink .= '<tr class="liste_titre">';
11316 //print '<td>' . $langs->trans("Ref") . '</td>';
11317 $htmltoenteralink .= '<td class="center"><input type="text" placeholder="'.dol_escape_htmltag($langs->trans("Ref")).'" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">';
11318 $htmltoenteralink .= '<br>';
11319 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
11320 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp" name="cancel" value="' . $langs->trans('Cancel') . '">';
11321 $htmltoenteralink .= '</td>';
11322 $htmltoenteralink .= '</tr>';
11323 $htmltoenteralink .= '</table>';
11324 $htmltoenteralink .= '</form>';
11325 }
11326
11327 $sql = $possiblelink['sql'];
11328
11329 $resqllist = $this->db->query($sql);
11330 if ($resqllist) {
11331 $num = $this->db->num_rows($resqllist);
11332
11333 if ($num > 0) {
11334 // Section for free predefined list
11335 if (getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11336 $htmltoenteralink .= '<br>';
11337 }
11338 $htmltoenteralink .= '<!-- form to add a link from object to same thirdparty -->'."\n";
11339 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
11340 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11341 $htmltoenteralink .= '<input type="hidden" name="action" value="addlink">';
11342 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11343 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key . (!empty($module) ? '@'.$module : ''). '">';
11344 $htmltoenteralink .= '<table class="noborder">';
11345
11346 switch ($key) {
11347 case 'conferenceorboothattendee':
11348 // Custom logic for linking to attendees
11349 $htmltoenteralink .= $this->makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist);
11350 break;
11351
11352 default:
11353 // Standard logic for all other object types
11354 $htmltoenteralink .= $this->makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist);
11355 break;
11356 }
11357
11358 $htmltoenteralink .= '</table>';
11359 $htmltoenteralink .= '<div class="center">';
11360 if ($num) {
11361 $htmltoenteralink .= '<input type="submit" class="button valignmiddle marginleftonly marginrightonly smallpaddingimp" value="' . $langs->trans('ToLink') . '">';
11362 }
11363 if (empty($conf->use_javascript_ajax)) {
11364 $htmltoenteralink .= '<input type="submit" class="button button-cancel marginleftonly marginrightonly smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
11365 } else {
11366 $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>';
11367 }
11368 $htmltoenteralink .= '</form>';
11369 }
11370
11371 $this->db->free($resqllist);
11372 } else {
11373 dol_print_error($this->db);
11374 }
11375 $htmltoenteralink .= '</div>';
11376
11377
11378 // Complete the list for the combo box
11379 if ($num > 0 || !getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11380 $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
11381 // } else $linktoelem.=$langs->trans($possiblelink['label']);
11382 } else {
11383 $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
11384 }
11385 }
11386 }
11387
11388 if ($linktoelemlist) {
11389 $linktoelem = '
11390 <dl class="dropdown" id="linktoobjectname">
11391 ';
11392 if (!empty($conf->use_javascript_ajax)) {
11393 $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
11394 }
11395 $linktoelem .= '<dd>
11396 <div class="multiselectlinkto">
11397 <ul class="ulselectedfields">' . $linktoelemlist . '
11398 </ul>
11399 </div>
11400 </dd>
11401 </dl>';
11402 } else {
11403 $linktoelem = '';
11404 }
11405
11406 if (!empty($conf->use_javascript_ajax)) {
11407 print '<!-- Add js to show linkto box -->
11408 <script nonce="' . getNonce() . '">
11409 jQuery(document).ready(function() {
11410 jQuery(".linkto").click(function() {
11411 console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
11412 jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
11413 });
11414 });
11415 </script>
11416 ';
11417 }
11418
11419 if ($nooutput) {
11420 return array('linktoelem' => $linktoelem, 'htmltoenteralink' => $htmltoenteralink);
11421 } else {
11422 print $htmltoenteralink;
11423 }
11424
11425 return $linktoelem;
11426 }
11427
11442 public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = 'yesno width75', $labelyes = 'Yes', $labelno = 'No')
11443 {
11444 global $langs;
11445
11446 $yes = "yes";
11447 $no = "no";
11448 if ($option) {
11449 $yes = "1";
11450 $no = "0";
11451 }
11452
11453 $disabled = ($disabled ? ' disabled' : '');
11454
11455 $resultyesno = '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
11456 if ($useempty) {
11457 $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
11458 }
11459 if (("$value" == 'yes') || ($value == 1)) {
11460 $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
11461 $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
11462 } else {
11463 $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
11464 $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
11465 $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
11466 }
11467 $resultyesno .= '</select>' . "\n";
11468
11469 if ($addjscombo) {
11470 $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
11471 }
11472
11473 return $resultyesno;
11474 }
11475
11476 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11477
11487 public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
11488 {
11489 // phpcs:enable
11490 $sql = "SELECT rowid, label";
11491 $sql .= " FROM " . $this->db->prefix() . "export_model";
11492 $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
11493 $sql .= " ORDER BY rowid";
11494 $result = $this->db->query($sql);
11495 if ($result) {
11496 print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
11497 if ($useempty) {
11498 print '<option value="-1">&nbsp;</option>';
11499 }
11500
11501 $num = $this->db->num_rows($result);
11502 $i = 0;
11503 while ($i < $num) {
11504 $obj = $this->db->fetch_object($result);
11505 if ($selected == $obj->rowid) {
11506 print '<option value="' . $obj->rowid . '" selected>';
11507 } else {
11508 print '<option value="' . $obj->rowid . '">';
11509 }
11510 print $obj->label;
11511 print '</option>';
11512 $i++;
11513 }
11514 print "</select>";
11515 } else {
11516 dol_print_error($this->db);
11517 }
11518 }
11519
11538 public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
11539 {
11540 global $conf, $langs, $hookmanager, $extralanguages;
11541
11542 $ret = '';
11543 if (empty($fieldid)) {
11544 $fieldid = 'rowid';
11545 }
11546 if (empty($fieldref)) {
11547 $fieldref = 'ref';
11548 }
11549
11550 // Preparing gender's display if there is one
11551 $addgendertxt = '';
11552 if (property_exists($object, 'gender') && !empty($object->gender)) {
11553 $addgendertxt = ' ';
11554 switch ($object->gender) {
11555 case 'man':
11556 $addgendertxt .= '<i class="fas fa-mars valignmiddle"></i>';
11557 break;
11558 case 'woman':
11559 $addgendertxt .= '<i class="fas fa-venus valignmiddle"></i>';
11560 break;
11561 case 'other':
11562 $addgendertxt .= '<i class="fas fa-transgender valignmiddle"></i>';
11563 break;
11564 }
11565 }
11566
11567 // Add where from hooks
11568 if (is_object($hookmanager)) {
11569 $parameters = array('showrefnav' => true);
11570 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
11571 if (!empty($hookmanager->resPrint)) {
11572 if (empty($object->next_prev_filter) && preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11573 $object->next_prev_filter = (string) preg_replace('/^\s*AND\s*/i', '', $hookmanager->resPrint);
11574 } elseif (!empty($object->next_prev_filter) && !preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11575 $object->next_prev_filter .= ' AND '.$hookmanager->resPrint;
11576 } else {
11577 $object->next_prev_filter .= $hookmanager->resPrint;
11578 }
11579 }
11580 }
11581
11582 $previous_ref = $next_ref = '';
11583 if ($shownav) {
11584 //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,fieldid=$fieldid,filedref=$fieldref,morehtmlref=$morehtmlref,moreparam=$moreparam";
11585 $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
11586
11587 $navurl = $_SERVER["PHP_SELF"];
11588
11589 // Special case for token card
11590 if ($paramid == 'api_token_card') {
11591 if (preg_match('/\/user\/api_token/', $navurl)) {
11592 $navurl = preg_replace('/card/', 'list', $navurl);
11593 $paramid = 'id';
11594 }
11595 }
11596
11597 // Special case for project/task page
11598 if ($paramid == 'project_ref') {
11599 if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) { // TODO Remove this when nav with project_ref on task pages are ok
11600 $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
11601 $paramid = 'ref';
11602 }
11603 }
11604
11605 $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>';
11606 $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>';
11607 }
11608
11609 //print "xx".$previous_ref."x".$next_ref;
11610 $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
11611
11612 // Right part of banner
11613 if ($morehtmlright) {
11614 $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
11615 }
11616
11617 if ($previous_ref || $next_ref || $morehtml) {
11618 $ret .= '<div class="pagination paginationref"><ul class="right">';
11619 }
11620 if ($morehtml && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
11621 $ret .= '<!-- morehtml --><li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
11622 }
11623 if ($shownav && ($previous_ref || $next_ref)) {
11624 $ret .= '<li class="pagination">' . $previous_ref . '</li>';
11625 $ret .= '<li class="pagination">' . $next_ref . '</li>';
11626 }
11627 if ($previous_ref || $next_ref || $morehtml) {
11628 $ret .= '</ul></div>';
11629 }
11630
11631 // Status
11632 $parameters = array('morehtmlstatus' => $morehtmlstatus);
11633 $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
11634 if (empty($reshook)) {
11635 $morehtmlstatus .= $hookmanager->resPrint;
11636 } else {
11637 $morehtmlstatus = $hookmanager->resPrint;
11638 }
11639 if ($morehtmlstatus) {
11640 $ret .= '<!-- status --><div class="statusref">' . $morehtmlstatus . '</div>';
11641 }
11642
11643 $parameters = array();
11644 $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
11645 if (empty($reshook)) {
11646 $morehtmlref .= $hookmanager->resPrint;
11647 } elseif ($reshook > 0) {
11648 $morehtmlref = $hookmanager->resPrint;
11649 }
11650
11651 // Left part of banner
11652 if ($morehtmlleft) {
11653 if ($conf->browser->layout == 'phone') {
11654 $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
11655 } else {
11656 $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
11657 }
11658 }
11659
11660 //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
11661 $ret .= '<!-- Ref or ID --><div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
11662
11663 // For thirdparty, contact, user, member, the ref is the id, so we show something else
11664 if ($object->element == 'societe') {
11665 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->name).'</span>';
11666
11667 // List of extra languages
11668 $arrayoflangcode = array();
11669 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
11670 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
11671 }
11672
11673 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
11674 if (!is_object($extralanguages)) {
11675 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
11676 $extralanguages = new ExtraLanguages($this->db);
11677 }
11678 $extralanguages->fetch_name_extralanguages('societe');
11679
11680 // Guard against PHP 8 'Undefined array key' when MAIN_USE_ALTERNATE_TRANSLATION_FOR
11681 // is not configured and fetch_name_extralanguages() leaves attributes empty (issue #34596).
11682 if (!empty($extralanguages->attributes['societe']) && !empty($extralanguages->attributes['societe']['name'])) {
11683 $object->fetchValuesForExtraLanguages();
11684
11685 $htmltext = '';
11686 // If there is extra languages
11687 foreach ($arrayoflangcode as $extralangcode) {
11688 $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
11689 if ($object->array_languages['name'][$extralangcode]) {
11690 $htmltext .= $object->array_languages['name'][$extralangcode];
11691 } else {
11692 $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
11693 }
11694 }
11695 $ret .= '<!-- Show translations of name -->' . "\n";
11696 $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
11697 }
11698 }
11699 } elseif ($object->element == 'member') {
11700 '@phan-var-force Adherent $object';
11701 $ret .= $object->ref . '<br>';
11702 $fullname = $object->getFullName($langs);
11703 if ($object->morphy == 'mor' && $object->societe) {
11704 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '').'</span>';
11705 } else {
11706 $ret .= '<span class="valignmiddle">'.dolPrintHTML($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities((string) $object->societe) . ')' : '').'</span>';
11707 }
11708 } elseif (in_array($object->element, array('contact', 'user'))) {
11709 $ret .= '<span class="valignmiddle">'.dolPrintHTML($object->getFullName($langs)).'</span>'.$addgendertxt;
11710 } elseif ($object->element == 'usergroup') {
11711 $ret .= dol_htmlentities((string) $object->name);
11712 } elseif (in_array($object->element, array('action', 'agenda'))) {
11713 '@phan-var-force ActionComm $object';
11714 $ret .= $object->ref . '<br>' . $object->label;
11715 } elseif (in_array($object->element, array('adherent_type'))) {
11716 $ret .= $object->label;
11717 } elseif ($object->element == 'ecm_directories') {
11718 $ret .= '';
11719 } elseif ($object->element == 'accountingbookkeeping' && !empty($object->context['mode']) && $object->context['mode'] == '_tmp') {
11720 $ret .= '<span class="valignmiddle">'.$langs->trans("Draft").'</span>';
11721 } elseif ($object instanceof Ticket) {
11722 '@phan-var-force Ticket $object';
11723 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11724 $ret .= ' &nbsp; <span class="nobold small" title="'.dolPrintHTMLForAttribute($langs->trans("TicketTrackId")).'">('.$object->track_id.')</span>';
11725 } elseif ($fieldref != 'none') {
11726 // Generic case
11727 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11728 }
11729 if ($morehtmlref) {
11730 // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
11731 if (substr($morehtmlref, 0, 4) != '<div') {
11732 $ret .= ' ';
11733 }
11734
11735 $ret .= '<!-- morehtmlref -->'.$morehtmlref;
11736 }
11737
11738 $ret .= '</div>';
11739
11740 $ret .= '</div><!-- End banner content -->';
11741
11742 return $ret;
11743 }
11744
11745
11754 public function showbarcode(&$object, $width = 100, $morecss = '')
11755 {
11756 //Check if barcode is filled in the card
11757 if (empty($object->barcode)) {
11758 return '';
11759 }
11760
11761 // Complete object if not complete
11762 if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
11763 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11764 $result = $object->fetchBarCode();
11765 //Check if fetchBarCode() failed
11766 if ($result < 1) {
11767 return '<!-- ErrorFetchBarcode -->';
11768 }
11769 }
11770
11771 // Barcode image @phan-suppress-next-line PhanUndeclaredProperty
11772 $url = DOL_URL_ROOT . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
11773 $out = '<!-- url barcode = ' . $url . ' -->';
11774 $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
11775
11776 return $out;
11777 }
11778
11797 public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0, $usesharelinkifavailable = 0)
11798 {
11799 global $conf, $db, $langs;
11800
11801 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
11802 $id = (empty($object->id) ? $object->rowid : $object->id); // @phan-suppress-current-line PhanUndeclaredProperty (->rowid)
11803
11804 $dir = '';
11805 $file = '';
11806 $originalfile = '';
11807 $altfile = '';
11808 $email = '';
11809 $capture = '';
11810 if ($modulepart == 'societe') {
11811 $dir = $conf->societe->multidir_output[$entity];
11812 if (!empty($object->logo)) {
11813 if (dolIsAllowedForPreview($object->logo)) {
11814 if ((string) $imagesize == 'mini') {
11815 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
11816 } elseif ((string) $imagesize == 'small') {
11817 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
11818 } else {
11819 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11820 }
11821 $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11822 }
11823 }
11824 $email = $object->email;
11825 } elseif ($modulepart == 'contact') {
11826 $dir = $conf->societe->multidir_output[$entity] . '/contact';
11827 $photo = $object->photo; // Copy to help static analysis
11828 if (!empty($photo)) {
11829 if (dolIsAllowedForPreview($photo)) {
11830 if ((string) $imagesize == 'mini') {
11831 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11832 } elseif ((string) $imagesize == 'small') {
11833 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_small');
11834 } else {
11835 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11836 }
11837 $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11838 }
11839 }
11840 $email = $object->email;
11841 $capture = 'user';
11842 } elseif ($modulepart == 'userphoto') {
11843 $dir = $conf->user->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, 'user') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11849 } elseif ((string) $imagesize == 'small') {
11850 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_small');
11851 } else {
11852 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11853 }
11854 $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . '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 } elseif ($modulepart == 'memberphoto') {
11863 $dir = $conf->member->dir_output;
11864 $photo = $object->photo; // Copy to help static analysis
11865 if (!empty($photo)) {
11866 if (dolIsAllowedForPreview($photo)) {
11867 if ((string) $imagesize == 'mini') {
11868 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11869 } elseif ((string) $imagesize == 'small') {
11870 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_small');
11871 } else {
11872 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11873 }
11874 $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11875 }
11876 }
11877 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11878 $altfile = $object->id . ".jpg"; // For backward compatibility
11879 }
11880 $email = $object->email;
11881 $capture = 'user';
11882 } else {
11883 // Generic case to show photos
11884 // TODO Implement this method in previous objects so we can always use this generic method.
11885 if ($modulepart != "unknown" && method_exists($object, 'getDataToShowPhoto')) {
11886 $tmpdata = $object->getDataToShowPhoto($modulepart, $imagesize);
11887
11888 $dir = $tmpdata['dir'];
11889 $file = $tmpdata['file'];
11890 $originalfile = $tmpdata['originalfile'];
11891 $altfile = $tmpdata['altfile'];
11892 $email = $tmpdata['email'];
11893 $capture = $tmpdata['capture'];
11894 }
11895 }
11896
11897 if ($forcecapture) {
11898 $capture = $forcecapture;
11899 }
11900
11901 $ret = '';
11902
11903 if ($dir) {
11904 if ($file && file_exists($dir . "/" . $file)) {
11905 if ($addlinktofullsize) {
11906 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11907 if ($urladvanced) {
11908 $ret .= '<a href="' . $urladvanced . '">';
11909 } else {
11910 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11911 }
11912 }
11913
11914 $sharekey = '';
11915 if ($usesharelinkifavailable) {
11916 // $dir is a full path '/home/.../dolibarr_documents/module'
11917 $relativefileforecm = preg_replace('/^'.preg_quote(DOL_DATA_ROOT.'/', '/').'/', '', $dir.'/'.$originalfile);
11918 // $relativefileforecme = 'module/...'
11919 require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php';
11920 $ecmfiles = new EcmFiles($db);
11921 $ecmfiles->fetch(0, '', $relativefileforecm);
11922
11923 $sharekey = (string) $ecmfiles->share;
11924 }
11925
11926 if (!empty($sharekey)) {
11927 $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) . '">';
11928 } else {
11929 $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) . '">';
11930 }
11931 if ($addlinktofullsize) {
11932 $ret .= '</a>';
11933 }
11934 } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
11935 if ($addlinktofullsize) {
11936 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11937 if ($urladvanced) {
11938 $ret .= '<a href="' . $urladvanced . '">';
11939 } else {
11940 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11941 }
11942 }
11943 $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) . '">';
11944 if ($addlinktofullsize) {
11945 $ret .= '</a>';
11946 }
11947 } else {
11948 $nophoto = '/public/theme/common/nophoto.png';
11949 $defaultimg = 'identicon'; // For gravatar
11950 if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) { // For modules that need a special image when photo not found
11951 if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor') !== false)) {
11952 $nophoto = 'company';
11953 } else {
11954 $nophoto = '/public/theme/common/user_anonymous.png';
11955 if (!empty($object->gender) && $object->gender == 'man') {
11956 $nophoto = '/public/theme/common/user_man.png';
11957 }
11958 if (!empty($object->gender) && $object->gender == 'woman') {
11959 $nophoto = '/public/theme/common/user_woman.png';
11960 }
11961 }
11962 }
11963
11964 if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
11965 // see https://gravatar.com/site/implement/images/php/
11966 $ret .= '<!-- Put link to gravatar -->';
11967 $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
11968 } else {
11969 if ($nophoto == 'company') {
11970 $ret .= '<div class="divforspanimg valignmiddle inline-block center photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
11971 //$ret .= '<div class="difforspanimgright"></div>';
11972 } else {
11973 $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '">';
11974 }
11975 }
11976 }
11977
11978 if ($caneditfield) {
11979 if ($object->photo) {
11980 $ret .= "<br>\n";
11981 }
11982 $ret .= '<table class="nobordernopadding centpercent">';
11983 if ($object->photo) {
11984 $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
11985 }
11986 $ret .= '<tr><td class="tdoverflow">';
11987 $maxfilesizearray = getMaxFileSizeArray();
11988 $maxmin = $maxfilesizearray['maxmin'];
11989 if ($maxmin > 0) {
11990 $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">'; // MAX_FILE_SIZE must precede the field type=file
11991 }
11992 $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . dolPrintHTMLForAttribute($capture) . '"' : '') . '>';
11993 $ret .= '</td></tr>';
11994 $ret .= '</table>';
11995 }
11996 }
11997
11998 return $ret;
11999 }
12000
12001 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
12002
12019 public function select_dolgroups($selected = 0, $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0', $multiple = false, $morecss = 'minwidth200')
12020 {
12021 // phpcs:enable
12022 global $conf, $user, $langs;
12023
12024 // Allow excluding groups
12025 $excludeGroups = null;
12026 if (is_array($exclude)) {
12027 $excludeGroups = implode(",", $exclude);
12028 }
12029 // Allow including groups
12030 $includeGroups = null;
12031 if (is_array($include)) {
12032 $includeGroups = implode(",", $include);
12033 }
12034
12035 if (!is_array($selected)) {
12036 $selected = array($selected);
12037 }
12038
12039 $out = '';
12040
12041 // Build sql to search groups
12042 $sql = "SELECT ug.rowid, ug.nom as name";
12043 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12044 $sql .= ", e.label";
12045 }
12046 $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
12047 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12048 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
12049 if ($force_entity) {
12050 $sql .= " WHERE ug.entity IN (0, " . ((int) $force_entity) . ")";
12051 } else {
12052 $sql .= " WHERE ug.entity IS NOT NULL";
12053 }
12054 } else {
12055 $sql .= " WHERE ug.entity IN (0, " . ((int) $conf->entity) . ")";
12056 }
12057 if (is_array($exclude) && $excludeGroups) {
12058 $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
12059 }
12060 if (is_array($include) && $includeGroups) {
12061 $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
12062 }
12063 $sql .= " ORDER BY ug.nom ASC";
12064
12065 dol_syslog(get_class($this) . "::select_dolgroups", LOG_DEBUG);
12066 $resql = $this->db->query($sql);
12067 if ($resql) {
12068 // Enhance with select2
12069 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12070
12071 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
12072
12073 $num = $this->db->num_rows($resql);
12074 $i = 0;
12075 if ($num) {
12076 if ($show_empty && !$multiple) {
12077 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
12078 }
12079
12080 while ($i < $num) {
12081 $obj = $this->db->fetch_object($resql);
12082 $disableline = 0;
12083 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
12084 $disableline = 1;
12085 }
12086
12087 $label = $obj->name;
12088 $labelhtml = $obj->name;
12089 if (isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1) {
12090 $label .= " (" . $obj->label . ")";
12091 $labelhtml .= ' <span class="opacitymedium">(' . $obj->label . ')</span>';
12092 }
12093
12094 $out .= '<option value="' . $obj->rowid . '"';
12095 if ($disableline) {
12096 $out .= ' disabled';
12097 }
12098 if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid)
12099 || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
12100 $out .= ' selected';
12101 }
12102 $out .= ' data-html="'.dol_escape_htmltag($labelhtml).'"';
12103 $out .= '>';
12104 $out .= $label;
12105 $out .= '</option>';
12106 $i++;
12107 }
12108 } else {
12109 if ($show_empty) {
12110 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
12111 }
12112 $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
12113 }
12114 $out .= '</select>';
12115
12116 $out .= ajax_combobox($htmlname);
12117 } else {
12118 dol_print_error($this->db);
12119 }
12120
12121 return $out;
12122 }
12123
12124
12131 public function showFilterButtons($pos = '')
12132 {
12133 $out = '<div class="nowraponall">';
12134 $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fas fa-search"></span></button>';
12135 $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fas fa-times"></span></button>';
12136 $out .= '</div>';
12137
12138 return $out;
12139 }
12140
12149 public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12150 {
12151 global $conf;
12152
12153 $out = '';
12154
12155 if (!empty($conf->use_javascript_ajax)) {
12156 $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
12157 }
12158 $out .= '<script nonce="' . getNonce() . '">
12159 $(document).ready(function() {
12160 $("#' . $cssclass . 's").click(function() {
12161 if($(this).is(\':checked\')){
12162 console.log("We check all ' . $cssclass . ' and trigger the change method");
12163 $(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
12164 }
12165 else
12166 {
12167 console.log("We uncheck all");
12168 $(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
12169 }' . "\n";
12170 if ($calljsfunction) {
12171 $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
12172 }
12173 $out .= ' });
12174/*
12175 $(".' . $cssclass . '").change(function() {
12176 console.log("We check and change the tr class highlight after a change on .'.$cssclass.'");
12177 var $row = $(this).closest("tr");
12178 if ($row.length) {
12179 var anyChecked = $row.find(\'input[type="checkbox"].checkforselect:checked\').length > 0;
12180 console.log("anychecked="+anyChecked);
12181 if (!anyChecked) {
12182 $row.removeClass("highlight");
12183 } else {
12184 $row.addClass("highlight");
12185 }
12186 }
12187 });
12188*/
12189 });
12190 </script>';
12191
12192 return $out;
12193 }
12194
12204 public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12205 {
12206 $out = $this->showFilterButtons();
12207 if ($addcheckuncheckall) {
12208 $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
12209 }
12210 return $out;
12211 }
12212
12226 public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
12227 {
12228 global $langs, $user;
12229
12230 $out = '';
12231 $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
12232 $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
12233 if (!empty($excludeid)) {
12234 $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
12235 }
12236 $sql .= " ORDER BY label";
12237
12238 $resql = $this->db->query($sql);
12239 if ($resql) {
12240 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
12241 if ($useempty) {
12242 $out .= '<option value="0">&nbsp;</option>';
12243 }
12244
12245 while ($obj = $this->db->fetch_object($resql)) {
12246 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
12247 }
12248 $out .= '</select>';
12249 $out .= ajax_combobox('select_' . $htmlname);
12250
12251 if (!empty($htmlname) && $user->admin && $info_admin) {
12252 $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
12253 }
12254
12255 if (!empty($target)) {
12256 $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
12257 $resql = $this->db->query($sql);
12258 if ($resql) {
12259 if ($this->db->num_rows($resql) > 0) {
12260 $obj = $this->db->fetch_object($resql);
12261 $out .= '<script nonce="' . getNonce() . '">
12262 $(function() {
12263 $("select[name=' . $target . ']").on("change", function() {
12264 var current_val = $(this).val();
12265 if (current_val == ' . $obj->id . ') {';
12266 if (!empty($default_selected) || !empty($selected)) {
12267 $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
12268 }
12269
12270 $out .= '
12271 $("select[name=' . $htmlname . ']").change();
12272 }
12273 });
12274
12275 $("select[name=' . $htmlname . ']").change(function() {
12276
12277 if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
12278 // get price of kilometer to fill the unit price
12279 $.ajax({
12280 method: "POST",
12281 dataType: "json",
12282 data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
12283 url: "' . (DOL_URL_ROOT . '/expensereport/ajax/ajaxik.php?' . implode('&', $params)) . '",
12284 }).done(function( data, textStatus, jqXHR ) {
12285 console.log(data);
12286 if (typeof data.up != "undefined") {
12287 $("input[name=value_unit]").val(data.up);
12288 $("select[name=' . $htmlname . ']").attr("title", data.title);
12289 } else {
12290 $("input[name=value_unit]").val("");
12291 $("select[name=' . $htmlname . ']").attr("title", "");
12292 }
12293 });
12294 }
12295 });
12296 });
12297 </script>';
12298 }
12299 }
12300 }
12301 } else {
12302 dol_print_error($this->db);
12303 }
12304
12305 return $out;
12306 }
12307
12316 public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
12317 {
12318 global $conf, $langs;
12319
12320 $out = '';
12321 $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
12322 $sql .= " WHERE entity = " . ((int) $conf->entity) . " AND active = 1";
12323
12324 $resql = $this->db->query($sql);
12325 if ($resql) {
12326 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12327 if ($useempty) {
12328 $out .= '<option value="0"></option>';
12329 }
12330
12331 while ($obj = $this->db->fetch_object($resql)) {
12332 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
12333 }
12334 $out .= '</select>';
12335 } else {
12336 dol_print_error($this->db);
12337 }
12338
12339 return $out;
12340 }
12341
12352 public function selectExpenseFees($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
12353 {
12354 global $langs;
12355
12356 $out = '';
12357 $sql = "SELECT id, code, label";
12358 $sql .= " FROM ".$this->db->prefix()."c_type_fees";
12359 $sql .= " WHERE active = 1";
12360
12361 $resql = $this->db->query($sql);
12362 if ($resql) {
12363 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12364 if ($useempty) {
12365 $out .= '<option value="0"></option>';
12366 }
12367 if ($allchoice) {
12368 $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
12369 }
12370
12371 $field = 'code';
12372 if ($useid) {
12373 $field = 'id';
12374 }
12375
12376 while ($obj = $this->db->fetch_object($resql)) {
12377 $key = $langs->trans($obj->code);
12378 $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
12379 }
12380 $out .= '</select>';
12381
12382 $out .= ajax_combobox('select_'.$htmlname);
12383 } else {
12384 dol_print_error($this->db);
12385 }
12386
12387 return $out;
12388 }
12389
12408 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)
12409 {
12410 global $user, $conf, $langs;
12411
12412 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
12413
12414 if (is_null($usertofilter)) {
12415 $usertofilter = $user;
12416 }
12417
12418 $out = '';
12419
12420 $hideunselectables = false;
12421 if (getDolGlobalString('INVOICE_HIDE_UNSELECTABLES')) {
12422 $hideunselectables = true;
12423 }
12424
12425 if (empty($projectsListId)) {
12426 if (!$usertofilter->hasRight('projet', 'all', 'lire')) {
12427 $projectstatic = new Project($this->db);
12428 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
12429 }
12430 }
12431
12432 // Search all projects
12433 $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";
12434 $sql .= " FROM " . $this->db->prefix() . "facture as f";
12435 $sql .= " INNER JOIN " . $this->db->prefix() . "projet as p ON p.entity IN (" . getEntity('project') . ") AND f.fk_projet = p.rowid";
12436 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = p.fk_soc";
12437 $sql .= " WHERE f.fk_statut = 0"; // Draft invoices only
12438 //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
12439 //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
12440 //if ($socid > 0) $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
12441 $sql .= " ORDER BY p.ref, f.ref ASC";
12442
12443 $resql = $this->db->query($sql);
12444 if ($resql) {
12445 // Use select2 selector
12446 if (!empty($conf->use_javascript_ajax)) {
12447 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12448 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12449 $out .= $comboenhancement;
12450 $morecss = 'minwidth200imp maxwidth500';
12451 }
12452
12453 if (empty($option_only)) {
12454 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12455 }
12456 if (!empty($show_empty)) {
12457 $out .= '<option value="0" class="optiongrey">';
12458 if (!is_numeric($show_empty)) {
12459 $out .= $show_empty;
12460 } else {
12461 $out .= '&nbsp;';
12462 }
12463 $out .= '</option>';
12464 }
12465 $num = $this->db->num_rows($resql);
12466 $i = 0;
12467 if ($num) {
12468 while ($i < $num) {
12469 $obj = $this->db->fetch_object($resql);
12470 // 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.
12471 if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && !$usertofilter->hasRight('societe', 'lire')) {
12472 // Do nothing
12473 } else {
12474 if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
12475 $i++;
12476 continue;
12477 }
12478
12479 $labeltoshow = '';
12480
12481 if ($showproject == 'all') {
12482 $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
12483 if ($obj->name) {
12484 $labeltoshow .= ' - ' . $obj->name; // Soc name
12485 }
12486
12487 $disabled = 0;
12488 if ($obj->fk_statut == Project::STATUS_DRAFT) {
12489 $disabled = 1;
12490 $labeltoshow .= ' - ' . $langs->trans("Draft");
12491 } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
12492 if ($discard_closed == 2) {
12493 $disabled = 1;
12494 }
12495 $labeltoshow .= ' - ' . $langs->trans("Closed");
12496 } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
12497 $disabled = 1;
12498 $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
12499 }
12500 }
12501
12502 if (!empty($selected) && $selected == $obj->rowid) {
12503 $out .= '<option value="' . $obj->rowid . '" selected';
12504 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12505 $out .= '>' . $labeltoshow . '</option>';
12506 } else {
12507 if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
12508 $resultat = '';
12509 } else {
12510 $resultat = '<option value="' . $obj->rowid . '"';
12511 if ($disabled) {
12512 $resultat .= ' disabled';
12513 }
12514 //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
12515 //else $labeltoshow.=' ('.$langs->trans("Private").')';
12516 $resultat .= '>';
12517 $resultat .= $labeltoshow;
12518 $resultat .= '</option>';
12519 }
12520 $out .= $resultat;
12521 }
12522 }
12523 $i++;
12524 }
12525 }
12526 if (empty($option_only)) {
12527 $out .= '</select>';
12528 }
12529
12530 $this->db->free($resql);
12531
12532 return $out;
12533 } else {
12534 dol_print_error($this->db);
12535 return '';
12536 }
12537 }
12538
12553 public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
12554 {
12555 global $conf, $langs;
12556
12557 $out = '';
12558
12559 dol_syslog('FactureRec::fetch', LOG_DEBUG);
12560
12561 $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
12562 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
12563 $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
12564 $sql .= " ORDER BY f.titre ASC";
12565
12566 $resql = $this->db->query($sql);
12567 if ($resql) {
12568 // Use select2 selector
12569 if (!empty($conf->use_javascript_ajax)) {
12570 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12571 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12572 $out .= $comboenhancement;
12573 $morecss = 'minwidth200imp maxwidth500';
12574 }
12575
12576 if (empty($option_only)) {
12577 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12578 }
12579 if (!empty($show_empty)) {
12580 $out .= '<option value="0" class="optiongrey">';
12581 if (!is_numeric($show_empty)) {
12582 $out .= $show_empty;
12583 } else {
12584 $out .= '&nbsp;';
12585 }
12586 $out .= '</option>';
12587 }
12588 $num = $this->db->num_rows($resql);
12589 if ($num) {
12590 while ($obj = $this->db->fetch_object($resql)) {
12591 $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
12592
12593 $disabled = 0;
12594 if (!empty($obj->suspended)) {
12595 $disabled = 1;
12596 $labeltoshow .= ' - ' . $langs->trans("Closed");
12597 }
12598
12599
12600 if (!empty($selected) && $selected == $obj->rowid) {
12601 $out .= '<option value="' . $obj->rowid . '" selected';
12602 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12603 $out .= '>' . $labeltoshow . '</option>';
12604 } else {
12605 if ($disabled && ($selected != $obj->rowid)) {
12606 $resultat = '';
12607 } else {
12608 $resultat = '<option value="' . $obj->rowid . '"';
12609 if ($disabled) {
12610 $resultat .= ' disabled';
12611 }
12612 $resultat .= '>';
12613 $resultat .= $labeltoshow;
12614 $resultat .= '</option>';
12615 }
12616 $out .= $resultat;
12617 }
12618 }
12619 }
12620 if (empty($option_only)) {
12621 $out .= '</select>';
12622 }
12623
12624 print $out;
12625
12626 $this->db->free($resql);
12627 return $num;
12628 } else {
12629 $this->errors[] = $this->db->lasterror;
12630 return -1;
12631 }
12632 }
12633
12634
12645 public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '', $arrayoffiltercriterias = array())
12646 {
12647 // TODO: Use $arrayoffiltercriterias param instead of $arrayofcriterias to include linked object fields in search
12648 global $langs, $form;
12649
12650 //require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
12651 //$formother = new FormOther($this->db);
12652
12653 if ($search_component_params_hidden != '' && !preg_match('/^\‍(.*\‍)$/', $search_component_params_hidden)) { // If $search_component_params_hidden does not start and end with ()
12654 $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
12655 }
12656
12657 $ret = '<!-- searchComponent -->';
12658
12659 $ret .= '<div class="divadvancedsearchfieldcomp centpercent inline-block">';
12660 $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
12661 $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
12662 $ret .= '</a>';
12663
12664 $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
12665
12666 // Show select fields as tags.
12667 $ret .= '<div id="divsearch_component_params" name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
12668
12669 if ($search_component_params_hidden) {
12670 // Split the criteria on each AND
12671 //var_dump($search_component_params_hidden);
12672
12673 $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
12674
12675 // $arrayofandtags is now array( '...' , '...', ...)
12676 // Show each AND part
12677 foreach ($arrayofandtags as $tmpkey => $tmpval) {
12678 $errormessage = '';
12679 $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
12680 if ($errormessage) {
12681 $this->error = 'ERROR in parsing search string: '.$errormessage;
12682 }
12683 // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
12684 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12685 $searchtags = removeGlobalParenthesis($searchtags);
12686
12687 $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="'.($tmpkey + 1).'" data-ufilter="'.dol_escape_htmltag($tmpval).'">';
12688 $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="'.($tmpkey + 1).'">x</span> ';
12689 $ret .= dol_escape_htmltag($searchtags);
12690 $ret .= '</span>';
12691 }
12692 }
12693
12694 //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
12695
12696 //$ret .= search_component_params
12697 //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
12698 //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
12699
12700 $show_search_component_params_hidden = 1;
12701 if ($show_search_component_params_hidden) {
12702 $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
12703 }
12704 $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%')) -->";
12705 $ret .= '<input type="hidden" id="search_component_params_hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
12706 // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
12707
12708 // TODO : Use $arrayoffiltercriterias instead of $arrayofcriterias
12709 // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
12710 foreach ($arrayofcriterias as $criteria) {
12711 foreach ($criteria as $criteriafamilykey => $criteriafamilyval) {
12712 if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
12713 continue;
12714 }
12715 if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
12716 continue;
12717 }
12718 if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
12719 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
12720 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
12721 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
12722 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
12723 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
12724 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
12725 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
12726 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
12727 } else {
12728 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
12729 }
12730 }
12731 }
12732
12733 $ret .= '</div>';
12734
12735 $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";
12736 $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" id="search_component_params_input" name="search_component_params_input" class="noborderall search_component_input" value="">';
12737
12738 $ret .= '</div>';
12739 $ret .= '</div>';
12740
12741 $ret .= '<script>
12742 jQuery(".tagsearchdelete").click(function(e) {
12743 var filterid = $(this).parents().attr("data-ufilterid");
12744 console.log("We click to delete the criteria nb "+filterid);
12745
12746 // Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
12747 var newparamstring = \'\';
12748 $(\'.tagsearch\').each(function(index, element) {
12749 tmpfilterid = $(this).attr("data-ufilterid");
12750 if (tmpfilterid != filterid) {
12751 // We keep this criteria
12752 if (newparamstring == \'\') {
12753 newparamstring = $(this).attr("data-ufilter");
12754 } else {
12755 newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
12756 }
12757 }
12758 });
12759 console.log("newparamstring = "+newparamstring);
12760
12761 jQuery("#search_component_params_hidden").val(newparamstring);
12762
12763 // We repost the form
12764 $(this).closest(\'form\').submit();
12765 });
12766
12767 jQuery("#search_component_params_input").keydown(function(e) {
12768 console.log("We press a key on the filter field that is "+jQuery("#search_component_params_input").val());
12769 console.log(e.which);
12770 if (jQuery("#search_component_params_input").val() == "" && e.which == 8) {
12771 /* We click on back when the input field is already empty */
12772 event.preventDefault();
12773 jQuery("#divsearch_component_params .tagsearch").last().remove();
12774 /* Regenerate content of search_component_params_hidden from remaining .tagsearch */
12775 var s = "";
12776 jQuery("#divsearch_component_params .tagsearch").each(function( index ) {
12777 if (s != "") {
12778 s = s + " AND ";
12779 }
12780 s = s + $(this).attr("data-ufilter");
12781 });
12782 console.log("New value for search_component_params_hidden = "+s);
12783 jQuery("#search_component_params_hidden").val(s);
12784 }
12785 });
12786
12787 </script>
12788 ';
12789
12790 // Convert $arrayoffiltercriterias into a json object that can be used in jquery to build the search component dynamically
12791 $arrayoffiltercriterias_json = json_encode($arrayoffiltercriterias);
12792 $ret .= '<script>
12793 var arrayoffiltercriterias = ' . $arrayoffiltercriterias_json . ';
12794 </script>';
12795
12796
12797 $arrayoffilterfieldslabel = array();
12798 foreach ($arrayoffiltercriterias as $key => $val) {
12799 $arrayoffilterfieldslabel[$key]['label'] = $val['label'];
12800 $arrayoffilterfieldslabel[$key]['data-type'] = $val['type'];
12801 }
12802
12803 // Adding the div for search assistance
12804 $ret .= '<div class="search-component-assistance">';
12805 $ret .= '<div>';
12806
12807 $ret .= '<p class="assistance-title">' . img_picto('', 'filter') . ' ' . $langs->trans('FilterAssistance') . ' </p>';
12808
12809 $ret .= '<p class="assistance-errors error" style="display:none">' . $langs->trans('AllFieldsRequired') . ' </p>';
12810
12811 $ret .= '<div class="operand">';
12812 $ret .= $form->selectarray('search_filter_field', $arrayoffilterfieldslabel, '', $langs->trans("Fields"), 0, 0, '', 0, 0, 0, '', 'width200 combolargeelem', 1);
12813 $ret .= '</div>';
12814
12815 $ret .= '<span class="separator"></span>';
12816
12817 // Operator selector (will be populated dynamically)
12818 $ret .= '<div class="operator">';
12819 $ret .= '<select class="operator-selector width150" id="operator-selector"">';
12820 $ret .= '</select>';
12821 $ret .= '<script>$(document).ready(function() {';
12822 $ret .= ' $(".operator-selector").select2({';
12823 $ret .= ' placeholder: \'' . dol_escape_js($langs->transnoentitiesnoconv('Operator')) . '\'';
12824 $ret .= ' });';
12825 $ret .= '});</script>';
12826 $ret .= '</div>';
12827
12828 $ret .= '<span class="separator"></span>';
12829
12830 $ret .= '<div class="value">';
12831 // Input field for entering values
12832 $ret .= '<input type="text" class="flat width100 value-input" placeholder="' . dolPrintHTML($langs->trans('Value')) . '">';
12833
12834 // Date selector
12835 $dateOne = '';
12836 $ret .= '<span class="date-one" style="display:none">';
12837 $ret .= $form->selectDate(($dateOne ? $dateOne : -1), 'dateone', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '');
12838 $ret .= '</span>';
12839
12840 // Value selector (will be populated dynamically) based on search_filter_field value if a selected value has an array of values
12841 $ret .= '<select class="value-selector width150" id="value-selector" style="display:none">';
12842 $ret .= '</select>';
12843 $ret .= '<script>
12844 $(document).ready(function() {
12845 $("#value-selector").select2({
12846 placeholder: "' . dol_escape_js($langs->trans('Value')) . '"
12847 });
12848 $("#value-selector").hide();
12849 $("#value-selector").next(".select2-container").hide();
12850 });
12851 </script>';
12852
12853 $ret .= '</div>';
12854
12855 $ret .= '<div class="btn-div">';
12856 $ret .= '<button class="button buttongen button-save add-filter-btn" type="button">' . $langs->trans("addToFilter") . '</button>';
12857 $ret .= '</div>';
12858
12859 $ret .= '</div>';
12860 //$ret .= '</tbody></table>';
12861
12862 // End of the assistance div
12863 $ret .= '</div>';
12864
12865 // Script jQuery to show/hide the floating assistance
12866 $ret .= '<script>
12867 $(document).ready(function() {
12868 $("#search_component_params_input").on("click", function() {
12869 const inputPosition = $(this).offset();
12870 const inputHeight = $(this).outerHeight();
12871 $(".search-component-assistance").css({
12872 top: inputPosition.top + inputHeight + 5 + "px",
12873 left: $("#divsearch_component_params").position().left
12874 }).slideToggle(200);
12875 });
12876 $(document).on("click", function(e) {
12877 if (!$(e.target).closest("#search_component_params_input, .search-component-assistance, #ui-datepicker-div").length) {
12878 $(".search-component-assistance").hide();
12879 }
12880 });
12881 });
12882 </script>';
12883
12884 $ret .= '<script>
12885 $(document).ready(function() {
12886 $(".search_filter_field").on("change", function() {
12887 console.log("We change search_filter_field");
12888
12889 let maybenull = 0;
12890 const selectedField = $(this).find(":selected");
12891 let fieldType = selectedField.data("type");
12892 const selectedFieldValue = selectedField.val();
12893
12894 // If the selected field has an array of values then ask toshow the value selector instead of the value input
12895 if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12896 fieldType = "select";
12897 }
12898
12899 // If the selected field may be null then ask to append the "IsDefined" and "IsNotDefined" operators
12900 if (arrayoffiltercriterias[selectedFieldValue]["maybenull"] !== undefined) {
12901 maybenull = 1;
12902 }
12903 const operators = getOperatorsForFieldType(fieldType, maybenull);
12904 const operatorSelector = $(".operator-selector");
12905
12906 // Clear existing options
12907 operatorSelector.empty();
12908
12909 // Populate operators
12910 Object.entries(operators).forEach(function([operator, label]) {
12911 operatorSelector.append("<option value=\'" + operator + "\'>" + label + "</option>");
12912 });
12913
12914 operatorSelector.trigger("change.select2");
12915
12916 // Clear and hide all input elements initially
12917 $(".value-input, .dateone, .datemonth, .dateyear").val("").hide();
12918 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12919 $("#dateone").datepicker("setDate", null);
12920 $(".date-one, .date-month, .date-year").hide();
12921 $("#value-selector").val("").hide();
12922 $("#value-selector").next(".select2-container").hide();
12923 $("#value-selector").val(null).trigger("change.select2");
12924
12925 if (fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp") {
12926 $(".date-one").show();
12927 } else if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12928 var arrayofkeyval = arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"];
12929 var valueSelector = $("#value-selector");
12930 valueSelector.empty();
12931 Object.entries(arrayofkeyval).forEach(function([key, val]) {
12932 valueSelector.append("<option value=\'" + key + "\'>" + val + "</option>");
12933 });
12934 valueSelector.trigger("change.select2");
12935
12936 $("#value-selector").show();
12937 $("#value-selector").next(".select2-container").show();
12938 } else {
12939 $(".value-input").show();
12940 }
12941 });
12942
12943 $("#operator-selector").on("change", function() {
12944 console.log("We change operator-selector");
12945
12946 const selectedOperator = $(this).find(":selected").val();
12947 if (selectedOperator === "IsDefined" || selectedOperator === "IsNotDefined") {
12948 // Disable all value input elements
12949 $(".value-input, .dateone, .datemonth, .dateyear").val("").prop("disabled", true);
12950 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12951 $("#dateone").datepicker("setDate", null).datepicker("option", "disabled", true);
12952 $(".date-one, .date-month, .date-year").prop("disabled", true);
12953 $("#value-selector").val("").prop("disabled", true);
12954 $("#value-selector").val(null).trigger("change.select2");
12955 } else {
12956 // Enable all value input elements
12957 $(".value-input, .dateone, .datemonth, .dateyear").prop("disabled", false);
12958 $(".date-one, .date-month, .date-year").prop("disabled", false);
12959 $("#dateone").datepicker("option", "disabled", false);
12960 $("#value-selector").prop("disabled", false);
12961 }
12962 });
12963
12964 $(".add-filter-btn").on("click", function(event) {
12965 console.log("We click on add-filter-btn");
12966
12967 event.preventDefault();
12968
12969 const field = $(".search_filter_field").val();
12970 const operator = $(".operator-selector").val();
12971 let value = $(".value-input").val();
12972 const fieldType = $(".search_filter_field").find(":selected").data("type");
12973
12974 if (["date", "datetime", "timestamp"].includes(fieldType)) {
12975 const year = $("#dateoneyear").val().toString().padStart(4, "0");;
12976 const month = $("#dateonemonth").val().toString().padStart(2, "0");
12977 const day = $("#dateoneday").val().toString().padStart(2, "0");
12978 value = `${year}-${month}-${day}`;
12979 console.log("value="+value);
12980 }
12981
12982 // If the selected field has an array of values then take the selected value
12983 if (arrayoffiltercriterias[field]["arrayofkeyval"] !== undefined) {
12984 value = $("#value-selector").val();
12985 }
12986
12987 // If the operator is "IsDefined" or "IsNotDefined" then set the value to 1 (it will not be used)
12988 if (operator === "IsDefined" || operator === "IsNotDefined") {
12989 value = "1";
12990 }
12991
12992 const filterString = generateFilterString(field, operator, value, fieldType);
12993
12994 // Submit the form
12995 if (filterString !== "" && field !== "" && operator !== "" && value !== "") {
12996 $("#search_component_params_input").val($("#search_component_params_input").val() + " " + filterString);
12997 $("#search_component_params_input").closest("form").submit();
12998 } else {
12999 $(".assistance-errors").show();
13000 }
13001 });
13002 });
13003 </script>';
13004
13005 return $ret;
13006 }
13007
13019 public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0, $selected = 0, $morecss = '')
13020 {
13021 global $langs, $user;
13022
13023 $retstring = '';
13024
13025 $TModels = array();
13026
13027 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
13028 $formmail = new FormMail($this->db);
13029 $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
13030
13031 if ($default) {
13032 $TModels[0] = $langs->trans('DefaultMailModel');
13033 }
13034 if ($result > 0) {
13035 foreach ($formmail->lines_model as $model) {
13036 $TModels[(int) $model->id] = $model->label;
13037 }
13038 }
13039
13040 $retstring .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
13041
13042 foreach ($TModels as $id_model => $label_model) {
13043 $retstring .= '<option value="' . $id_model . '"';
13044 if (!empty($selected) && ((int) $selected) == $id_model) {
13045 $retstring .= "selected";
13046 }
13047 $retstring .= ">" . $label_model . "</option>";
13048 }
13049
13050 $retstring .= "</select>";
13051
13052 if ($addjscombo) {
13053 $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
13054 }
13055
13056 return $retstring;
13057 }
13058
13070 public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = false, $morecss = '', $dol_openinpopup = '')
13071 {
13072 global $langs;
13073
13074 $buttons = array();
13075
13076 $save = array(
13077 'name' => 'save',
13078 'label_key' => $save_label,
13079 );
13080
13081 if ($save_label == 'Create' || $save_label == 'Add') {
13082 $save['name'] = 'add';
13083 } elseif ($save_label == 'Modify') {
13084 $save['name'] = 'edit';
13085 }
13086
13087 $cancel = array(
13088 'name' => 'cancel',
13089 'label_key' => 'Cancel',
13090 );
13091
13092 // If MAIN_BUTTON_POSITION_FIRST_OR_LEFT not set, default is to have main action first, then complementary, then cancel at end
13093 if (!getDolGlobalInt('MAIN_BUTTON_POSITION_FIRST_OR_LEFT')) {
13094 !empty($save_label) ? $buttons[] = $save : '';
13095 if (!empty($morebuttons)) {
13096 $buttons[] = $morebuttons;
13097 }
13098 !empty($cancel_label) ? $buttons[] = $cancel : '';
13099 } else {
13100 if (!empty($morebuttons)) {
13101 $buttons[] = $morebuttons;
13102 }
13103 !empty($cancel_label) ? $buttons[] = $cancel : '';
13104 !empty($save_label) ? $buttons[] = $save : '';
13105 }
13106
13107 $retstring = $withoutdiv ? '' : '<div class="center">';
13108
13109 foreach ($buttons as $button) {
13110 $addclass = empty($button['addclass']) ? '' : $button['addclass'];
13111 $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'])) . '">';
13112 }
13113 $retstring .= $withoutdiv ? '' : '</div>';
13114
13115 if ($dol_openinpopup) {
13116 $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . dol_escape_htmltag($dol_openinpopup) . ' context, so we enable the close of dialog on cancel -->' . "\n";
13117 $retstring .= '<script nonce="' . getNonce() . '">';
13118 $retstring .= 'jQuery(".button-cancel").click(function(e) {
13119 e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . dol_escape_js($dol_openinpopup) . '\');
13120 window.parent.jQuery(\'#idfordialog' . dol_escape_js($dol_openinpopup) . '\').dialog(\'close\');
13121 });';
13122 $retstring .= '</script>';
13123 }
13124
13125 return $retstring;
13126 }
13127
13128
13129 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
13130
13137 {
13138 // phpcs:enable
13139 global $langs;
13140
13141 $num = count($this->cache_invoice_subtype);
13142 if ($num > 0) {
13143 return 0; // Cache already loaded
13144 }
13145
13146 dol_syslog(__METHOD__, LOG_DEBUG);
13147
13148 $sql = "SELECT rowid, code, label as label";
13149 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
13150 $sql .= " WHERE active = 1";
13151
13152 $resql = $this->db->query($sql);
13153 if ($resql) {
13154 $num = $this->db->num_rows($resql);
13155 $i = 0;
13156 while ($i < $num) {
13157 $obj = $this->db->fetch_object($resql);
13158
13159 // If translation exists, we use it, otherwise we take the default wording
13160 $label = ($langs->trans("InvoiceSubtype" . $obj->rowid) != "InvoiceSubtype" . $obj->rowid) ? $langs->trans("InvoiceSubtype" . $obj->rowid) : (($obj->label != '-') ? $obj->label : '');
13161 $this->cache_invoice_subtype[$obj->rowid]['rowid'] = $obj->rowid;
13162 $this->cache_invoice_subtype[$obj->rowid]['code'] = $obj->code;
13163 $this->cache_invoice_subtype[$obj->rowid]['label'] = $label;
13164 $i++;
13165 }
13166
13167 $this->cache_invoice_subtype = dol_sort_array($this->cache_invoice_subtype, 'code', 'asc', 0, 0, 1);
13168
13169 return $num;
13170 } else {
13171 dol_print_error($this->db);
13172 return -1;
13173 }
13174 }
13175
13176
13187 public function getSelectInvoiceSubtype($selected = 0, $htmlname = 'subtypeid', $addempty = 0, $noinfoadmin = 0, $morecss = '')
13188 {
13189 global $langs, $user;
13190
13191 $out = '';
13192 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
13193
13194 $this->load_cache_invoice_subtype();
13195
13196 $out .= '<select id="' . $htmlname . '" class="flat selectsubtype' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
13197 if ($addempty) {
13198 $out .= '<option value="0">&nbsp;</option>';
13199 }
13200
13201 foreach ($this->cache_invoice_subtype as $rowid => $subtype) {
13202 $label = $subtype['label'];
13203 $out .= '<option value="' . $subtype['rowid'] . '"';
13204 if ($selected == $subtype['rowid']) {
13205 $out .= ' selected="selected"';
13206 }
13207 $out .= '>';
13208 $out .= $label;
13209 $out .= '</option>';
13210 }
13211
13212 $out .= '</select>';
13213 if ($user->admin && empty($noinfoadmin)) {
13214 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
13215 }
13216 $out .= ajax_combobox($htmlname);
13217
13218 return $out;
13219 }
13220
13230 public function getSearchFilterToolInput($dataTarget, $htmlName = 'search-tools-input', $value = '', $params = [])
13231 {
13232 global $langs;
13233
13234 $attr = array(
13235 'type' => 'search',
13236 'name' => $htmlName,
13237 'value' => $value,
13238 'class' => "search-tool-input",
13239 'placeholder' => $langs->trans('Search'),
13240 'autocomplete' => 'off'
13241 );
13242
13243 // Optional data attr
13244 // 'autofocus' : will set auto focus on field ,
13245 // data-counter-target : will get count results
13246 // data-no-item-target : will be display if count results is 0
13247
13248 if ($dataTarget !== false) {
13249 $attr['data-search-tool-target'] = $dataTarget;
13250 }
13251
13252 // Override attr
13253 if (!empty($params['attr']) && is_array($params['attr'])) {
13254 foreach ($params['attr'] as $key => $value) {
13255 if ($key == 'class') {
13256 $attr['class'] .= ' '.$value;
13257 } elseif ($key == 'classOverride') {
13258 $attr['class'] = $value;
13259 } else {
13260 $attr[$key] = $value;
13261 }
13262 }
13263 }
13264
13265 // automatic add tooltip when title is detected
13266 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13267 $attr['class'] .= ' classfortooltip';
13268 }
13269
13270 $TCompiledAttr = [];
13271 foreach ($attr as $key => $value) {
13272 if (in_array($key, ['data-target'])
13273 || (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr']))) { // Not recommended
13274 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13275 } else {
13276 $value = dolPrintHTMLForAttribute($value);
13277 }
13278
13279 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13280 }
13281
13282 $compiledAttributes = implode(' ', $TCompiledAttr);
13283
13284
13285 return '<div class="search-tool-container"><input '.$compiledAttributes.'></div>';
13286 }
13287
13301 public function inputType($type, $name, $value = '', $id = '', $morecss = '', $moreparam = '', $label = '', $addInputLabel = '')
13302 {
13303 $out = '';
13304 if ($label != '') {
13305 $out .= '<label for="' . dolPrintHTMLForAttribute($id) . '">';
13306 }
13307 $out .= '<input type="' . dolPrintHTMLForAttribute($type) . '"';
13308 $out .= ' class="flat valignmiddle maxwidthonsmartphone ' . dolPrintHTMLForAttribute($morecss) . '"';
13309 if ($id != '') {
13310 $out .= ' id="' . dolPrintHTMLForAttribute($id) . '"';
13311 }
13312 $out .= ' name="' . dolPrintHTMLForAttribute($name) . '"';
13313 $out .= ' value="' . dolPrintHTMLForAttribute($value) . '" ';
13314 $out .= ($moreparam ? ' ' . $moreparam : '');
13315 $out .= ' />' . $addInputLabel;
13316 if ($label != '') {
13317 $out .= $label . '</label>';
13318 }
13319
13320 return $out;
13321 }
13322
13335 public function inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData = [], $morecss = 'minwidth75', $moreparam = '')
13336 {
13337 $out = "
13338 <script>
13339 $(document).ready(function () {
13340 $('#" . $htmlName . "').select2({
13341 ajax: {
13342 url: '" . $ajaxUrl . "',
13343 dataType: 'json',
13344 delay: 250, // wait 250 milliseconds before triggering the request
13345 data: function (params) {
13346 var query = {
13347 search: params.term,
13348 page: params.page || 1";
13349 if (!empty($ajaxData) && is_array($ajaxData)) {
13350 foreach ($ajaxData as $key => $value) {
13351 $out .= ", " . $key . ": '" . $value . "'";
13352 }
13353 }
13354 $out .= "
13355 }
13356 return query;
13357 }
13358 }
13359 })
13360 });
13361 </script>";
13362
13363 $out .= $this->selectarray($htmlName, $array, $id, 0, 0, 0, $moreparam, 0, 0, 0, '', $morecss);
13364
13365 return $out;
13366 }
13367
13377 public function inputHtml($htmlName, $value, $morecss = '', $moreparam = '')
13378 {
13379 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13380 $doleditor = new DolEditor($htmlName, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
13381
13382 return (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13383 }
13384
13395 public function inputText($htmlName, $value, $morecss = '', $moreparam = '', $options = array())
13396 {
13397 global $langs;
13398
13399 $out = '';
13400 if (!empty($options)) {
13401 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
13402 $out .= $this->selectarray($htmlName . "_multiinput", $options, '', 1, 0, 0, $moreparam, 0, 0, 0, '', "flat maxwidthonphone" . $morecss);
13403 $out .= '<input id="' . $htmlName . '_multiinputadd" type="button" class="button" value="' . $langs->trans("Add") . '">';
13404 $out .= "<script>";
13405 $out .= '
13406 function handlemultiinputdisabling(htmlname){
13407 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
13408 multiinput = $("#"+htmlname+"_multiinput");
13409 multiinput.find("option").each(function(){
13410 tmpval = $("#"+htmlname).val();
13411 tmpvalarray = tmpval.split("\n");
13412 valtotest = $(this).val();
13413 if(tmpvalarray.includes(valtotest)){
13414 $(this).prop("disabled",true);
13415 } else {
13416 if($(this).prop("disabled") == true){
13417 console.log(valtotest)
13418 $(this).prop("disabled", false);
13419 }
13420 }
13421 });
13422 }
13423
13424 $(document).ready(function () {
13425 $("#' . $htmlName . '_multiinputadd").on("click",function() {
13426 tmpval = $("#' . $htmlName . '").val();
13427 tmpvalarray = tmpval.split(",");
13428 valtotest = $("#' . $htmlName . '_multiinput").val();
13429 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
13430 console.log("We add the selected value to the text area ' . $htmlName . '");
13431 if(tmpval == ""){
13432 tmpval = valtotest;
13433 } else {
13434 tmpval = tmpval + "\n" + valtotest;
13435 }
13436 $("#' . $htmlName . '").val(tmpval);
13437 handlemultiinputdisabling("' . $htmlName . '");
13438 $("#' . $htmlName . '_multiinput").val(-1);
13439 } else {
13440 console.log("We add nothing the text area ' . $htmlName . '");
13441 }
13442 });
13443 $("#' . $htmlName . '").on("change",function(){
13444 handlemultiinputdisabling("' . $htmlName . '");
13445 });
13446 handlemultiinputdisabling("' . $htmlName . '");
13447 })';
13448 $out .= "</script>";
13449 $value = str_replace(',', "\n", $value);
13450 }
13451
13452 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13453 $doleditor = new DolEditor($htmlName, (string) $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
13454 $out .= (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13455
13456 return $out;
13457 }
13458
13469 public function inputRadio($htmlName, $options, $selectedValue, $morecss = '', $moreparam = '')
13470 {
13471 $out = '';
13472 foreach ($options as $optionKey => $optionLabel) {
13473 $selected = ((string) $selectedValue) === ((string) $optionKey) ? ' checked="checked"' : '';
13474 $optionId = $htmlName . '_' . $optionKey;
13475 $out .= '<input class="flat' . $morecss . '" type="radio" name="' . $htmlName . '" id="' . $optionId . '" value="' . dolPrintHTMLForAttribute((string) $optionKey) . '"' . $selected . $moreparam . '/><label for="' . $optionId . '">' . $optionLabel . '</label><br>';
13476 }
13477
13478 return $out;
13479 }
13480
13491 public function inputStars($htmlName, $size, $value, $morecss = '', $moreparam = '')
13492 {
13493 $out = '<input type="hidden" class="flat ' . $morecss . '" name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '"' . $moreparam . '>';
13494 $out .= '<div class="star-selection" id="' . $htmlName . '_selection">';
13495 for ($i = 1; $i <= $size; $i++) {
13496 $out .= '<span class="star" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13497 }
13498 $out .= '</div>';
13499 $out .= '<script>
13500 jQuery(function($) { /* commonobject.class.php 1 */
13501 let container = $("#' . $htmlName . '_selection");
13502 let selectedStars = parseInt($("#' . $htmlName . '").val()) || 0;
13503 container.find(".star").each(function() {
13504 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13505 });
13506 container.find(".star").on("mouseover", function() {
13507 let selectedStar = $(this).data("value");
13508 container.find(".star").each(function() {
13509 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
13510 });
13511 });
13512 container.on("mouseout", function() {
13513 container.find(".star").each(function() {
13514 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13515 });
13516 });
13517 container.find(".star").off("click").on("click", function() {
13518 selectedStars = $(this).data("value");
13519 if (selectedStars === 1 && $("#' . $htmlName . '").val() == 1) {
13520 selectedStars = 0;
13521 }
13522 $("#' . $htmlName . '").val(selectedStars);
13523 container.find(".star").each(function() {
13524 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13525 });
13526 });
13527 });
13528 </script>';
13529
13530 return $out;
13531 }
13532
13542 public function inputIcon($htmlName, $value, $morecss = '', $moreparam = '')
13543 {
13544 global $langs;
13545
13546 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
13547 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
13548 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
13549 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
13550 */
13551 $out = '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat ' . $morecss . ' maxwidthonsmartphone"';
13552 $out .= ' name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '" ' . ((string) $moreparam) . '>';
13553 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
13554 $out .= '<script>';
13555 $options = "{ title: '<b>" . $langs->trans("IconFieldSelector") . "</b>', placement: 'right', showFooter: false, templates: {";
13556 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
13557 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
13558 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
13559 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
13560 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
13561 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"" . $langs->trans("TypeToFilter") . "\" />',";
13562 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
13563 $options .= " <div class=\"arrow\" ></div>";
13564 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
13565 $options .= " <div class=\"popover-content \" ></div>";
13566 $options .= "</div>'}}";
13567 $out .= "$('#" . $htmlName . "').iconpicker(" . $options . ");";
13568 $out .= '</script>';
13569 }
13570
13571 return $out;
13572 }
13573
13582 public function inputGeoPoint($htmlName, $value, $type = '')
13583 {
13584 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13585 require_once DOL_DOCUMENT_ROOT . '/core/class/geomapeditor.class.php';
13586 $dolgeophp = new DolGeoPHP($this->db);
13587 $geomapeditor = new GeoMapEditor();
13588
13589 $geojson = '{}';
13590 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
13591 if (!empty($value)) {
13592 $tmparray = $dolgeophp->parseGeoString($value);
13593 $geojson = $tmparray['geojson'];
13594 $centroidjson = $tmparray['centroidjson'];
13595 }
13596
13597 return $geomapeditor->getHtml($htmlName, $geojson, $centroidjson, $type);
13598 }
13599
13606 public function outputMultiValues($values)
13607 {
13608 $out = '';
13609 $toPrint = array();
13610 $values = is_array($values) ? $values : array();
13611
13612 foreach ($values as $value) {
13613 $toPrint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $value . '</li>';
13614 }
13615 if (!empty($toPrint)) {
13616 $out = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toPrint) . '</ul></div>';
13617 }
13618
13619 return $out;
13620 }
13621
13629 public function outputStars($size, $value)
13630 {
13631 $out = '<div class="star-selection" data-value="' . dolPrintHTMLForAttribute((string) $value) . '">';
13632 for ($i = 1; $i <= $size; $i++) {
13633 $out .= '<span class="star' . ($i <= $value ? ' active' : '') . '" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13634 }
13635 $out .= '</div>';
13636
13637 return $out;
13638 }
13639
13646 public function outputIcon($value)
13647 {
13648 $out = '<span class="' . dolPrintHTMLForAttribute((string) $value) . '"></span>';
13649
13650 return $out;
13651 }
13652
13660 public function outputGeoPoint($value, $type)
13661 {
13662 $out = '';
13663
13664 if (!empty($value)) {
13665 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13666 $dolgeophp = new DolGeoPHP($this->db);
13667 if ($type == 'point') {
13668 $out = $dolgeophp->getXYString($value);
13669 } else { // multipts, linestrg, polygon
13670 $out = $dolgeophp->getPointString($value);
13671 }
13672 }
13673
13674 return $out;
13675 }
13676
13691 public function getNomUrl(&$object, $withpicto = 0, $option = '', $maxlength = 0, $save_lastsearch_value = -1, $notooltip = 0, $morecss = '', $add_label = 0, $sep = ' - ')
13692 {
13693 if (is_object($object) && method_exists($object, 'getNomUrl')) {
13694 $out = $object->getNomUrl($withpicto, $option, $maxlength, $save_lastsearch_value, $notooltip, $morecss, $add_label, $sep);
13695 return $out;
13696 } else {
13697 return '';
13698 }
13699 }
13700}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLength=2, $autoselect=0, $ajaxoptions=array(), $moreparams='')
Generic function that return javascript to add to transform a common input text or select field into ...
Definition ajax.lib.php:50
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
ajax_multiautocompleter($htmlname, $fields, $url, $option='', $minLength=2, $autoselect=0)
Generic function that return javascript to add to a page to transform a common input text field into ...
Definition ajax.lib.php:325
ajax_event($htmlname, $events)
Add event management script.
Definition ajax.lib.php:599
$c
Definition line.php:334
Class to manage bank accounts.
Class to manage members of a foundation.
Class to manage categories.
Class to manage bank accounts description of third parties.
Class for ConferenceOrBoothAttendee.
Class to manage contact/addresses.
Class to manage a WYSIWYG editor.
Class to manage Geo processing Usage: $dolgeophp=new DolGeoPHP($db);.
DAO Resource object.
Class to manage ECM files.
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
const STATUS_CLOSED
Warehouse closed, inactive.
Class to manage standard extra languages.
Class to manage invoices.
Class to manage invoice templates.
Class to manage generation of HTML components Only common components must be here.
showLinkToObjectBlock($object, $restrictlinksto=array(), $excludelinksto=array(), $nooutput=0)
Show block with links "to link to" other objects.
inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData=[], $morecss='minwidth75', $moreparam='')
Html for select with get options by AJAX.
selectMultiCurrency($selected='', $htmlname='multicurrency_code', $useempty=0, $filter='', $excludeConfCurrency=false, $morecss='maxwidth200 widthcentpercentminusx')
Return array of currencies in user language.
showFilterButtons($pos='')
Return HTML to show the search and clear search button.
select_dolusers_forevent($action='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include=array(), $enableonly=array(), $force_entity='0', $maxlength=0, $showstatus=0, $morefilter='', $showproperties=0, $listofuserid=array(), $listofcontactid=array(), $listofotherid=array(), $canremoveowner=1)
Return select list of users.
load_cache_vatrates($country_code)
Load into the cache ->cache_vatrates, all the vat rates of a country.
inputRadio($htmlName, $options, $selectedValue, $morecss='', $moreparam='')
Html for input radio.
select_comptes($selected='', $htmlname='accountid', $status=0, $filtre='', $useempty=0, $moreattrib='', $showcurrency=0, $morecss='', $nooutput=0, $addentrynone=0)
Return a HTML select list of bank accounts.
static selectArrayAjax($htmlname, $url, $id='', $moreparam='', $moreparamtourl='', $disabled=0, $minimumInputLength=1, $morecss='', $callurlonselect=0, $placeholder='', $acceptdelayedhtml=0)
Return a HTML select string, built from an array of key+value, but content returned into select come ...
inputGeoPoint($htmlName, $value, $type='')
Html for input geo point.
editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata='string', $editvalue='', $extObject=null, $custommsg=null, $moreparam='', $notabletag=1, $formatfunc='', $paramid='id', $gm='auto', $moreoptions=array(), $editaction='')
Output value of a field for an editable field.
form_availability($page, $selected='', $htmlname='availability', $addempty=0)
Show a form to select a delivery delay.
showLinkedObjectBlock($object, $morehtmlright='', $compatibleImportElementsList=array(), $title='RelatedObjects')
Show linked object block.
select_dolusers($userselected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity='', $maxlength=0, $showstatus=0, $morefilter='', $showalso=0, $enableonlytext='', $morecss='', $notdisabled=0, $outputmode=0, $multiple=false, $forcecombo=0)
Return select list of users.
selectMassAction($selected, $arrayofaction, $alwaysvisible=0, $name='massaction', $cssclass='checkforselect')
Generate select HTML to choose massaction.
getSelectRuleForLinesDates($selected='', $htmlname='rule_for_lines_dates', $addempty=0)
Returns select with rule for lines dates.
select_dolresources_forevent($action='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include=array(), $enableonly=array(), $force_entity='0', $maxlength=0, $showstatus=0, $morefilter='', $showproperties=0, $listofresourceid=array())
Return select list of resources.
form_multicurrency_code($page, $selected='', $htmlname='multicurrency_code')
Show form with multicurrency code.
static radio($htmlName, $radioItems, $selected='', $moreGlobalParams=[])
Generates a set of HTML radio inputs from an array of key-value items.
formRib($page, $selected='', $htmlname='ribcompanyid', $filtre='', $addempty=0, $showibanbic=0)
Display form to select bank customer account.
showFilterAndCheckAddButtons($addcheckuncheckall=0, $cssclass='checkforaction', $calljsfunction=0, $massactionname="massaction")
Return HTML to show the search and clear search button.
select_company($selected='', $htmlname='socid', $filter='', $showempty='', $showtype=0, $forcecombo=0, $events=array(), $limit=0, $morecss='minwidth100', $moreparam='', $selected_input_value='', $hidelabel=1, $ajaxoptions=array(), $multiple=false, $excludeids=array(), $showcode=0)
Output html form to select a third party This call select_thirdparty_list() or ajax depending on setu...
select_produits($selected=0, $htmlname='productid', $filtertype='', $limit=0, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='', $selected_combinations=null, $nooutput=0, $status_purchase=-1, $warehouseId=0)
Return list of products.
inputType($type, $name, $value='', $id='', $morecss='', $moreparam='', $label='', $addInputLabel='')
Html for input with label.
select_dolgroups($selected=0, $htmlname='groupid', $show_empty=0, $exclude='', $disabled=0, $include='', $enableonly=array(), $force_entity='0', $multiple=false, $morecss='minwidth200')
Return select list of user groups.
selectInputReason($selected='', $htmlname='demandreasonid', $exclude='', $addempty=0, $morecss='', $notooltip=0)
Return list of input reason (events that triggered an object creation, like after sending an emailing...
select_contact($socid, $selected='', $htmlname='contactid', $showempty=0, $exclude='', $limitto='', $showfunction=0, $morecss='', $nokeyifsocid=true, $showsoc=0, $forcecombo=0, $events=array(), $moreparam='', $htmlid='', $selected_input_value='', $filter='')
Output html form to select a contact This call select_contacts() or ajax depending on setup.
select_incoterms($selected='', $location_incoterms='', $page='', $htmlname='incoterm_id', $htmloption='', $forcecombo=1, $events=array(), $disableautocomplete=0)
Return select list of incoterms.
select_type_of_lines($selected='', $htmlname='type', $showempty=0, $hidetext=0, $forceall=0, $morecss="", $useajaxcombo=1)
Return list of types of lines (product or service) Example: 0=product, 1=service, 9=other (for extern...
selectModelMail($prefix, $modelType='', $default=0, $addjscombo=0, $selected=0, $morecss='')
selectModelMail
select_types_paiements($selected='', $htmlname='paiementtype', $filtertype='', $format=0, $empty=1, $noadmininfo=0, $maxlength=0, $active=1, $morecss='', $nooutput=0)
Return list of payment methods Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value bu...
select_currency($selected='', $htmlname='currency_id')
Returns the list of currencies in the user's language.
inputStars($htmlName, $size, $value, $morecss='', $moreparam='')
Html for input stars.
formSelectTransportMode($page, $selected='', $htmlname='transport_mode_id', $active=1, $addempty=0)
Show form with transport mode.
selectShippingMethod($selected='', $htmlname='shipping_method_id', $filtre='', $useempty=0, $moreattrib='', $noinfoadmin=0, $morecss='')
Return a HTML select list of shipping mode.
select_produits_list($selected=0, $htmlname='productid', $filtertype='', $limit=1000, $price_level=0, $filterkey='', $status=1, $finished=2, $outputmode=0, $socid=0, $showempty='1', $forcecombo=0, $morecss='maxwidth500', $hidepriceinlabel=0, $warehouseStatus='', $status_purchase=-1, $warehouseId=0)
Return list of products for a customer.
selectRib($selected='', $htmlname='ribcompanyid', $filtre='', $useempty=0, $moreattrib='', $showibanbic=0, $morecss='', $nooutput=0)
Return a HTML select list of bank accounts customer.
formSelectShippingMethod($page, $selected='', $htmlname='shipping_method_id', $addempty=0)
Display form to select shipping mode.
static multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='', $nu='', $placeholder='', $addjscombo=-1)
Show a multiselect form from an array.
getSelectInvoiceSubtype($selected=0, $htmlname='subtypeid', $addempty=0, $noinfoadmin=0, $morecss='')
Return list of invoice subtypes.
form_contacts($page, $societe, $selected='', $htmlname='contactid')
Show forms to select a contact.
load_tva($htmlname='tauxtva', $selectedrate='', $societe_vendeuse=null, $societe_acheteuse=null, $idprod=0, $info_bits=0, $type='', $options_only=false, $mode=0, $type_vat=0)
Output an HTML select vat rate.
load_cache_availability()
Load int a cache property the list of possible delivery delays.
select_bom($selected='', $htmlname='bom_id', $limit=0, $status=1, $type=0, $showempty='1', $morecss='', $nooutput='', $forcecombo=0, $TProducts=[])
Return list of BOM for customer in Ajax if Ajax activated or go to select_produits_list.
form_rule_for_lines_dates($page, $selected='', $htmlname='rule_for_lines_dates', $addempty=0, $nooutput=0)
Form select for rule for lines dates.
selectcontacts($socid, $selected=array(), $htmlname='contactid', $showempty=0, $exclude='', $limitto='', $showfunction=0, $morecss='', $options_only=0, $showsoc=0, $forcecombo=0, $events=array(), $moreparam='', $htmlid='', $multiple=false, $disableifempty=0, $filter='')
Return HTML code of the SELECT of list of all contacts (for a third party or all).
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
select_type_fees($selected='', $htmlname='type', $showempty=0)
Return list of types of notes.
selectInvoiceRec($selected='', $htmlname='facrecid', $maxlength=24, $option_only=0, $show_empty='1', $forcefocus=0, $disabled=0, $morecss='maxwidth500')
Output a combo list with invoices qualified for a third party TODO Bad method.
inputIcon($htmlName, $value, $morecss='', $moreparam='')
Html for input icon.
form_multicurrency_rate($page, $rate=0.0, $htmlname='multicurrency_tx', $currency='', $rate_direct=0.0)
Show form with multicurrency rate.
editInPlace($object, $value, $htmlname, $condition, $inputType='textarea', $editvalue=null, $extObject=null, $custommsg=null)
Output edit in place form.
getPhoneInputSharedJs($countrySelectorId)
Return inline JS for country-selector → phone code sync (output once per page).
showPhoneInput($phoneValue, $htmlname, $country_id_hint=0, $picto='object_phoning', $morecss='maxwidth150', $maxlength=0, $countrySelectorId='selectcountry_id')
Show a self-contained phone input: hidden field + country code dropdown + number text field + JS.
selectUnits($selected='', $htmlname='units', $showempty=0, $unit_type='')
Creates HTML units selector (code => label)
static showphoto($modulepart, $object, $width=100, $height=0, $caneditfield=0, $cssclass='photowithmargin', $imagesize='', $addlinktofullsize=1, $cache=0, $forcecapture='', $noexternsourceoverwrite=0, $usesharelinkifavailable=0)
Return HTML code to output a photo.
form_modes_reglement($page, $selected='', $htmlname='mode_reglement_id', $filtertype='', $active=1, $addempty=0, $type='', $nooutput=0)
Show form with payment mode.
constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel=0, $filterkey='', $novirtualstock=0)
Function to forge the string with OPTIONs of SELECT.
buttonsSaveCancel($save_label='Save', $cancel_label='Cancel', $morebuttons=array(), $withoutdiv=false, $morecss='', $dol_openinpopup='')
Output the buttons to submit a creation/edit form.
selectTransportMode($selected='', $htmlname='transportmode', $format=0, $empty=1, $noadmininfo=0, $maxlength=0, $active=1, $morecss='')
Return list of transport mode for intracomm report.
form_conditions_reglement($page, $selected='', $htmlname='cond_reglement_id', $addempty=0, $type='', $filtertype=-1, $deposit_percent=-1, $nooutput=0)
Show a form to select payment conditions.
selectSituationInvoices($selected='', $socid=0)
Creates HTML last in cycle situation invoices selector.
selectPriceBaseType($selected='', $htmlname='price_base_type', $addjscombo=0)
Selection HT or TTC.
load_cache_transport_mode()
getSearchFilterToolInput($dataTarget, $htmlName='search-tools-input', $value='', $params=[])
select_conditions_paiements($selected=0, $htmlname='condid', $filtertype=-1, $addempty=0, $noinfoadmin=0, $morecss='', $deposit_percent=-1, $noprint=0)
print list of payment modes.
select_remises($selected, $htmlname, $filter, $socid, $maxvalue=0)
Return HTML combo list of absolute discounts.
showbarcode(&$object, $width=100, $morecss='')
Return HTML code to output a barcode.
load_cache_rule_for_lines_dates()
Loads into a cache property the list of possible rules for line dates.
form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter='', $maxvalue=0, $more='', $hidelist=0, $discount_type=0, $filterabsolutediscount=0, $filtercreditnote=0)
Show a select box with available absolute discounts.
getPhoneInputFieldJs($htmlname, $codename)
Return inline JS that syncs the hidden phone field from select + text input.
form_confirm($page, $title, $question, $action, $formquestion=array(), $selectedchoice="", $useajax=0, $height=170, $width=500)
load_cache_conditions_paiements()
Load into cache list of payment terms.
selectExpenseCategories($selected='', $htmlname='fk_c_exp_tax_cat', $useempty=0, $excludeid=array(), $target='', $default_selected=0, $params=array(), $info_admin=1)
Return HTML to show the select of expense categories.
selectPhoneCode($selected='', $htmlname='phone_code', $morecss='maxwidth150', $showempty=0, $country_id_hint=0)
Return a select list of country phone calling codes.
select_product_fourn_price($productid, $htmlname='productfournpriceid', $selected_supplier=0)
Return list of suppliers prices for a product.
getNomUrl(&$object, $withpicto=0, $option='', $maxlength=0, $save_lastsearch_value=-1, $notooltip=0, $morecss='', $add_label=0, $sep=' - ')
Return link of object.
form_project($page, $socid, $selected='', $htmlname='projectid', $discard_closed=0, $maxlength=20, $forcefocus=0, $nooutput=0, $textifnoproject='', $morecss='', $option='')
Show a form to select a project.
selectAvailabilityDelay($selected='', $htmlname='availid', $filtertype='', $addempty=0, $morecss='', $noouput=0)
Return the list of type of delay available.
outputGeoPoint($value, $type)
Html for show geo point.
form_date($page, $selected, $htmlname, $displayhour=0, $displaymin=0, $nooutput=0, $type='')
Show a form + html select a date.
showCheckAddButtons($cssclass='checkforaction', $calljsfunction=0, $massactionname="massaction")
Return HTML to show the search and clear search button.
__construct($db)
Constructor.
select_thirdparty_list($selected='', $htmlname='socid', $filter='', $showempty='', $showtype=0, $forcecombo=0, $events=array(), $filterkey='', $outputmode=0, $limit=0, $morecss='minwidth100', $moreparam='', $multiple=false, $excludeids=array(), $showcode=0)
Output html form to select a third party.
select_users($selected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly=array(), $force_entity='0')
Return the HTML select list of users.
selectDate($set_time='', $prefix='re', $h=0, $m=0, $empty=0, $form_name="", $d=1, $addnowlink=0, $disabled=0, $fullday='', $addplusone='', $adddateof='', $openinghours='', $stepminutes=1, $labeladddateof='', $placeholder='', $gm='auto', $calendarpicto='')
Show a HTML widget to input a date or combo list for day, month, years and optionally hours and minut...
inputText($htmlName, $value, $morecss='', $moreparam='', $options=array())
Html for HTML area.
select_all_categories($type, $selected='', $htmlname="parent", $maxlength=64, $fromid=0, $outputmode=0, $include=0, $morecss='', $useempty=1)
Return list of categories having chosen type.
textwithpicto($text, $htmltooltip, $direction=1, $type='help', $extracss='valignmiddle', $noencodehtmltext=0, $notabs=3, $tooltiptrigger='', $forcenowrap=0)
Show a text with a picto and a tooltip on picto.
select_date($set_time='', $prefix='re', $h=0, $m=0, $empty=0, $form_name="", $d=1, $addnowlink=0, $nooutput=0, $disabled=0, $fullday=0, $addplusone='', $adddateof='')
Show a HTML widget to input a date or combo list for day, month, years and optionally hours and minut...
load_cache_invoice_subtype()
Load into cache list of invoice subtypes.
makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
Generate HTML table rows for standard object linking (invoices, orders, proposals,...
select_export_model($selected='', $htmlname='exportmodelid', $type='', $useempty=0)
Return list of export templates.
selectDateToDate($set_time='', $set_time_end='', $prefix='re', $empty=0, $forcenewline=0)
Show 2 HTML widget to input a date or combo list for day, month, years and optionally hours and minut...
outputMultiValues($values)
Html for show selected multiple values.
textwithtooltip($text, $htmltext, $tooltipon=1, $direction=0, $img='', $extracss='', $notabs=3, $incbefore='', $noencodehtmltext=0, $tooltiptrigger='', $forcenowrap=0)
Show a text and picto with tooltip on text or picto.
selectCategories($categtype, $htmlname, $object=null)
Return HTML component to select a category.
formconfirm($page, $title, $question, $action, $formquestion='', $selectedchoice='', $useajax=0, $height=0, $width=600, $disableformtag=0, $labelbuttonyes='Yes', $labelbuttonno='No', $helpContent='')
Show a confirmation HTML form or AJAX popup.
outputIcon($value)
Html for show icon.
getHelpBlock($content, $icon='fa-question-circle')
Generate a collapsible help block with a standard '?' icon.
getSelectConditionsPaiements($selected=0, $htmlname='condid', $filtertype=-1, $addempty=0, $noinfoadmin=0, $morecss='', $deposit_percent=-1)
Return list of payment modes.
widgetForTranslation($fieldname, $object, $perm, $typeofdata='string', $check='', $morecss='')
Output edit in place form.
load_cache_types_fees()
Load into cache cache_types_fees, array of types of fees.
outputStars($size, $value)
Html for show stars.
getDurationTypes(Translate $langs, $plurial=true, $reverse=false)
Return an array of Duration Types.
form_thirdparty($page, $selected='', $htmlname='socid', $filter='', $showempty=0, $showtype=0, $forcecombo=0, $events=array(), $nooutput=0, $excludeids=array(), $textifnothirdparty='')
Output html select to select thirdparty.
selectEstablishments($selected='', $htmlname='entity', $status=0, $filtre='', $useempty=0, $moreattrib='')
Return a HTML select list of establishment.
formInputReason($page, $selected='', $htmlname='demandreason', $addempty=0, $morecss='')
Output HTML form to select list of input reason (events that triggered an object creation,...
formSelectAccount($page, $selected='', $htmlname='fk_account', $addempty=0)
Display form to select bank account.
form_users($page, $selected='', $htmlname='userid', $exclude=array(), $include=array())
Show a select form to choose a user.
editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0, $paramid='id', $help='')
Output key field for an editable field.
showCategories($id, $type, $rendermode=0, $nolink=0)
Render list of categories linked to object with id $id and type $type.
load_cache_types_paiements()
Load into the cacha array all possible payment modes.
select_produits_fournisseurs_list($socid, $selected='', $htmlname='productid', $filtertype='', $notused='', $filterkey='', $statut=-1, $outputmode=0, $limit=100, $alsoproductwithnosupplierprice=0, $morecss='', $showstockinlist=0, $placeholder='')
Return list of suppliers products.
makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
Generate HTML table rows for conference/booth attendee linking.
selectCurrency($selected='', $htmlname='currency_id', $mode=0, $useempty='')
Returns the list of currencies in the user's language.
selectyesno($htmlname, $value='', $option=0, $disabled=false, $useempty=0, $addjscombo=0, $morecss='yesno width75', $labelyes='Yes', $labelno='No')
Return an html string with a select combo box to choose yes or no.
inputHtml($htmlName, $value, $morecss='', $moreparam='')
Html for HTML area.
select_produits_fournisseurs($socid, $selected='', $htmlname='productid', $filtertype='', $notused='', $ajaxoptions=array(), $hidelabel=0, $alsoproductwithnosupplierprice=0, $morecss='', $placeholder='', $nooutput=0)
Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs...
showrefnav($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $morehtmlright='')
Return a HTML area with the reference of object and a navigation bar for a business object Note: To c...
searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput=array(), $search_component_params_hidden='', $arrayoffiltercriterias=array())
Output the component to make advanced search criteria.
selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty='', $searchkey='', $placeholder='', $morecss='', $moreparams='', $forcecombo=0, $outputmode=0, $disabled=0, $sortfield='', $filter='', $sortorder='ASC')
Output html form to select an object.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
Class to manage building of HTML components.
Class to manage forms for the module resource.
Class to manage a Leaflet map width geometrics objects.
Class to manage hooks.
Class for MyObject.
Class to parse product price expressions.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage subscriptions of foundation members.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
currency_name($code_iso, $withcode=0, $outputlangs=null)
Return label of currency or code+label.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
global $mysoc
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:87
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
removeGlobalParenthesis($string)
Remove first and last parenthesis but only if first is the opening and last the closing of the same g...
dol_now($mode='gmt')
Return date for now.
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=2, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
commonHtmlAttributeBuilder($attr, array $unescapedAttr=[])
Builds an array of safe and properly escaped HTML attributes from a key-value pair list.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
dolPrintHTML($s, $allowiframe=0, $moreallowedtags=array())
Return a string (that can be on several lines) ready to be output on a HTML page.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='classlink button bordertransp', $jsonopen='', $jsonclose='', $accesskey='')
Return HTML code to output a button to open a dialog popup box.
currentToken()
Return the value of token currently saved into session with name 'token'.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getElementProperties($elementType)
Get an array with properties of an element.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
colorIsLight($stringcolor)
Return true if the color is light.
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
getNonce()
Return a random string to be used as a nonce value for js.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external links.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_sanitizeKeyCode($str)
Clean a string to use it as a key or code.
isModEnabled($module)
Is Dolibarr module enabled.
array_merge_recursive_distinct(array $array1, array $array2)
Recursively merges two arrays while preserving keys and replacing existing values.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formatted text with and (WARNING: string must not have mixed and br sep...
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
a disabled
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
Class to generate the form for creating a new ticket.
dol_get_trunk_prefix($db, $phone_code)
Get the national trunk prefix for a phone code.
dol_parse_phone($phone)
Parse a stored phone number into country code and number parts.
Definition phone.lib.php:33
dol_get_phone_code_from_country($db, $country_id)
Get the phone calling code for a country.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:137
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:140
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