dolibarr 24.0.0-beta
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 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 3 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program. If not, see <https://www.gnu.org/licenses/>.
43 */
44
58class Form
59{
63 public $db;
64
68 public $error = '';
69
73 public $errors = array();
74
75 // Some properties used to return data by some methods
77 public $result;
78
80 public $num;
81
82 // Cache arrays
84 public $cache_types_paiements = array();
86 public $cache_conditions_paiements = array();
88 public $cache_transport_mode = array();
90 public $cache_availability = array();
92 public $cache_demand_reason = array();
94 public $cache_types_fees = array();
96 public $cache_vatrates = array();
98 public $cache_invoice_subtype = array();
100 public $cache_rule_for_lines_dates = array();
101
105 private $phoneInputSharedJsLoaded = false;
106
107
113 public function __construct($db)
114 {
115 $this->db = $db;
116 }
117
126 public function getDurationTypes(Translate $langs, $plurial = true, $reverse = false)
127 {
128 if ($plurial) {
129 $arrayoftypes = [
130 'y' => $langs->trans('Years'),
131 'm' => $langs->trans('Month'),
132 'w' => $langs->trans('Weeks'),
133 'd' => $langs->trans('Days'),
134 'h' => $langs->trans('Hours'),
135 'i' => $langs->trans('Minutes'),
136 's' => $langs->trans('Seconds'),
137 ];
138 } else {
139 $arrayoftypes = [
140 "y" => $langs->trans("Year"),
141 "m" => $langs->trans("Month"),
142 "w" => $langs->trans("Week"),
143 "d" => $langs->trans("Day"),
144 "h" => $langs->trans("Hour"),
145 "i" => $langs->trans("Minute"),
146 's' => $langs->trans('Second'),
147 ];
148 }
149 if ($reverse) {
150 return array_reverse($arrayoftypes);
151 } else {
152 return $arrayoftypes;
153 }
154 }
155
172 public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
173 {
174 global $langs;
175
176 $ret = '';
177
178 // TODO change for compatibility
179 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;/', $typeofdata)) {
180 if ($perm) {
181 $tmp = explode(':', $typeofdata);
182 $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
183 if ($fieldrequired) {
184 $ret .= '<span class="fieldrequired">';
185 }
186 if ($help) {
187 $ret .= $this->textwithpicto($langs->trans($text), $help);
188 } else {
189 $ret .= $langs->trans($text);
190 }
191 if ($fieldrequired) {
192 $ret .= '</span>';
193 }
194 $ret .= '</div>' . "\n";
195 } else {
196 if ($fieldrequired) {
197 $ret .= '<span class="fieldrequired">';
198 }
199 if ($help) {
200 $ret .= $this->textwithpicto($langs->trans($text), $help);
201 } else {
202 $ret .= $langs->trans($text);
203 }
204 if ($fieldrequired) {
205 $ret .= '</span>';
206 }
207 }
208 } else {
209 if (empty($notabletag) && $perm) {
210 $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
211 }
212 if ($fieldrequired) {
213 $ret .= '<span class="fieldrequired">';
214 }
215 if ($help) {
216 $ret .= $this->textwithpicto($langs->trans($text), $help);
217 } else {
218 $ret .= $langs->trans($text);
219 }
220 if ($fieldrequired) {
221 $ret .= '</span>';
222 }
223 if (!empty($notabletag)) {
224 $ret .= ' ';
225 }
226 if (empty($notabletag) && $perm) {
227 $ret .= '</td>';
228 }
229 if (empty($notabletag) && $perm) {
230 $ret .= '<td class="right">';
231 }
232 if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm && is_object($object)) {
233 $ret .= '<a class="editfielda reposition" href="' . dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'edit' . $htmlname, $paramid => $object->id], true) . $moreparam . '">';
234 $ret .= img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1));
235 $ret .= '</a>';
236 }
237 if (!empty($notabletag) && $notabletag == 1) {
238 if ($text) {
239 $ret .= ' : ';
240 } else {
241 $ret .= ' ';
242 }
243 }
244 if (!empty($notabletag) && $notabletag == 3) {
245 $ret .= ' ';
246 }
247 if (empty($notabletag) && $perm) {
248 $ret .= '</td>';
249 }
250 if (empty($notabletag) && $perm) {
251 $ret .= '</tr></table>';
252 }
253 }
254
255 return $ret;
256 }
257
281 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 = '')
282 {
283 global $conf, $langs;
284
285 $ret = '';
286
287 // Check parameters
288 if (empty($typeofdata)) {
289 return 'ErrorBadParameter typeofdata is empty';
290 }
291 // Clean parameter $typeofdata
292 if ($typeofdata == 'datetime') {
293 $typeofdata = 'dayhour';
294 }
295 $reg = array();
296 if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
297 if ($reg[1] == 'varchar') {
298 $typeofdata = 'string';
299 } elseif ($reg[1] == 'int') {
300 $typeofdata = 'numeric';
301 } else {
302 return 'ErrorBadParameter ' . $typeofdata;
303 }
304 }
305
306 // When option to edit inline is activated
307 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
308 $ret .= $this->editInPlace($object, $value, $htmlname, ($perm ? 1 : 0), $typeofdata, $editvalue, $extObject, $custommsg);
309 } else {
310 if ($editaction == '') {
311 $editaction = GETPOST('action', 'aZ09');
312 }
313 $editmode = ($editaction == 'edit' . $htmlname);
314 if ($editmode) { // edit mode
315 $ret .= "<!-- formeditfieldval -->\n";
316 $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
317 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
318 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
319 $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
320 if (empty($notabletag)) {
321 $ret .= '<table class="nobordernopadding centpercent">';
322 }
323 if (empty($notabletag)) {
324 $ret .= '<tr><td>';
325 }
326 if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
327 $tmp = explode(':', $typeofdata);
328 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus spellcheck="false">';
329 } elseif (preg_match('/^(integer)/', $typeofdata)) {
330 $tmp = explode(':', $typeofdata);
331 $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
332 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
333 } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
334 $tmp = explode(':', $typeofdata);
335 $valuetoshow = price2num($editvalue ? $editvalue : $value);
336 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
337 } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
338 $tmp = explode(':', $typeofdata);
339 $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
340 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
341 $tmp = explode(':', $typeofdata);
342 $cols = (empty($tmp[2]) ? '' : $tmp[2]);
343 $morealt = '';
344 if (preg_match('/%/', $cols)) {
345 $morealt = ' style="width: ' . $cols . '"';
346 $cols = '';
347 }
348 $valuetoshow = ($editvalue ? $editvalue : $value);
349 $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
350 // textarea convert automatically entities chars into simple chars.
351 // 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.
352 $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
353 $ret .= dol_htmlwithnojs(dol_string_neverthesehtmltags($valuetoshow, array('textarea')));
354 $ret .= '</textarea><div class="clearboth"></div>';
355 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
356 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
357 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
358 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
359 $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
360 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
361 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
362 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
363 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
364 $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
365 } elseif (preg_match('/^select;/', $typeofdata)) {
366 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
367 $arraylist = array();
368 foreach ($arraydata as $val) {
369 $tmp = explode(':', $val);
370 $tmpkey = str_replace('|', ':', $tmp[0]);
371 $arraylist[$tmpkey] = $tmp[1];
372 }
373 $ret .= $this->selectarray($htmlname, $arraylist, $value);
374 } elseif (preg_match('/^link/', $typeofdata)) {
375 // TODO Not yet implemented. See code for extrafields
376 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
377 $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
378 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
379 $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]));
380 $ret .= $doleditor->Create(1);
381 } elseif ($typeofdata == 'asis') {
382 $ret .= ($editvalue ? $editvalue : $value);
383 }
384 if (empty($notabletag)) {
385 $ret .= '</td>';
386 }
387
388 // Button save-cancel
389 if (empty($notabletag)) {
390 $ret .= '<td>';
391 }
392 //else $ret.='<div class="clearboth"></div>';
393 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Save") . '">';
394 if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
395 $ret .= '<br>' . "\n";
396 }
397 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
398 if (empty($notabletag)) {
399 $ret .= '</td>';
400 }
401
402 if (empty($notabletag)) {
403 $ret .= '</tr></table>' . "\n";
404 }
405 $ret .= '</form>' . "\n";
406 } else { // view mode
407 if (preg_match('/^email/', $typeofdata)) {
408 $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
409 } elseif (preg_match('/^phone/', $typeofdata)) {
410 $ret .= dol_print_phone($value, '_blank', 32, 1);
411 } elseif (preg_match('/^url/', $typeofdata)) {
412 $ret .= dol_print_url($value, '_blank', 32, 1);
413 } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
414 $ret .= ($value != '' ? price($value, 0, $langs, 0, -1, -1, $conf->currency) : '');
415 } elseif (preg_match('/^checkbox/', $typeofdata)) {
416 $tmp = explode(':', $typeofdata);
417 $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
418 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
420 } elseif (preg_match('/^(safehtmlstring|restricthtml)/', $typeofdata)) { // 'restricthtml' is not an allowed type for editfieldval. Value is 'safehtmlstring'
422 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
423 $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
424 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
425 $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
426 } elseif (preg_match('/^select;/', $typeofdata)) {
427 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
428 $arraylist = array();
429 foreach ($arraydata as $val) {
430 $tmp = explode(':', $val);
431 $arraylist[$tmp[0]] = $tmp[1];
432 }
433 $ret .= $arraylist[$value];
434 if ($htmlname == 'fk_product_type') {
435 if ($value == 0) {
436 $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
437 } else {
438 $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
439 }
440 }
441 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
442 $tmpcontent = dol_htmlentitiesbr($value);
443 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
444 $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
445 $firstline = preg_replace('/[\n\r].*/', '', $firstline);
446 $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
447 }
448 // We don't use dol_escape_htmltag to get the html formatting active, but this need we must also
449 // clean data from some dangerous html
451 } else {
452 if (empty($moreoptions['valuealreadyhtmlescaped'])) {
453 $ret .= dol_escape_htmltag($value);
454 } else {
455 $ret .= $value; // $value must be already html escaped.
456 }
457 }
458
459 // Custom format if parameter $formatfunc has been provided
460 if ($formatfunc && method_exists($object, $formatfunc)) {
461 $ret = $object->$formatfunc($ret);
462 }
463 }
464 }
465 return $ret;
466 }
467
479 public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
480 {
481 global $conf, $langs, $extralanguages;
482
483 $result = '';
484
485 // List of extra languages
486 $arrayoflangcode = array();
487 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
488 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
489 }
490
491 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
492 if (!is_object($extralanguages)) {
493 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
494 $extralanguages = new ExtraLanguages($this->db);
495 }
496 $extralanguages->fetch_name_extralanguages('societe');
497
498 // ExtraLanguages::fetch_name_extralanguages() leaves $this->attributes empty
499 // when MAIN_USE_ALTERNATE_TRANSLATION_FOR is not configured, so PHP 8 raises
500 // 'Undefined array key' on the read below if we do not guard it (issue #34596).
501 if (empty($extralanguages->attributes[$object->element]) || !is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
502 return ''; // No extralang field to show
503 }
504
505 $result .= '<!-- Widget for translation -->' . "\n";
506 $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
507 $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', 0, 0, 0, '', 'fa-15 editfieldlang');
508 $result .= $s;
509 $result .= '</div>';
510
511 $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
512
513 $resultforextrlang = '';
514 foreach ($arrayoflangcode as $langcode) {
515 $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
516 if (empty($valuetoshow)) {
517 $object->fetchValuesForExtraLanguages();
518 //var_dump($object->array_languages);
519 $valuetoshow = $object->array_languages[$fieldname][$langcode];
520 }
521
522 $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
523 $resultforextrlang .= $s;
524
525 // TODO Use the showInputField() method of ExtraLanguages object
526 if ($typeofdata == 'textarea') {
527 $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
528 $resultforextrlang .= $valuetoshow;
529 $resultforextrlang .= '</textarea>';
530 } else {
531 $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
532 }
533 }
534 $result .= $resultforextrlang;
535
536 $result .= '</div>';
537 $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
538 }
539
540 return $result;
541 }
542
556 protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
557 {
558 $out = '';
559
560 // Check parameters
561 if (preg_match('/^text/', $inputType)) {
562 $value = dol_nl2br($value);
563 } elseif (preg_match('/^numeric/', $inputType)) {
564 $value = price($value);
565 } elseif ($inputType == 'day' || $inputType == 'datepicker') {
566 $value = dol_print_date($value, 'day');
567 }
568
569 if ($condition) {
570 $element = false;
571 $table_element = false;
572 $fk_element = false;
573 $loadmethod = false;
574 $savemethod = false;
575 $ext_element = false;
576 $button_only = false;
577 $inputOption = '';
578 $rows = '';
579 $cols = '';
580
581 if (is_object($object)) {
582 $element = $object->element;
583 $table_element = $object->table_element;
584 $fk_element = $object->id;
585 }
586
587 if (is_object($extObject)) {
588 $ext_element = $extObject->element;
589 }
590
591 if (preg_match('/^(string|email|numeric)/', $inputType)) {
592 $tmp = explode(':', $inputType);
593 $inputType = $tmp[0];
594 if (!empty($tmp[1])) {
595 $inputOption = $tmp[1];
596 }
597 if (!empty($tmp[2])) {
598 $savemethod = $tmp[2];
599 }
600 $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
601 } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
602 $tmp = explode(':', $inputType);
603 $inputType = $tmp[0];
604 if (!empty($tmp[1])) {
605 $inputOption = $tmp[1];
606 }
607 if (!empty($tmp[2])) {
608 $savemethod = $tmp[2];
609 }
610
611 $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
612 } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
613 $tmp = explode(':', $inputType);
614 $inputType = $tmp[0];
615 $loadmethod = $tmp[1];
616 if (!empty($tmp[2])) {
617 $savemethod = $tmp[2];
618 }
619 if (!empty($tmp[3])) {
620 $button_only = true;
621 }
622 } elseif (preg_match('/^textarea/', $inputType)) {
623 $tmp = explode(':', $inputType);
624 $inputType = $tmp[0];
625 $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
626 $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
627 } elseif (preg_match('/^ckeditor/', $inputType)) {
628 $tmp = explode(':', $inputType);
629 $inputType = $tmp[0];
630 $toolbar = $tmp[1];
631 if (!empty($tmp[2])) {
632 $width = $tmp[2];
633 }
634 if (!empty($tmp[3])) {
635 $height = $tmp[3];
636 }
637 if (!empty($tmp[4])) {
638 $savemethod = $tmp[4];
639 }
640
641 if (isModEnabled('fckeditor')) {
642 $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
643 } else {
644 $inputType = 'textarea';
645 }
646 }
647
648 $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
649 $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
650 $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
651 $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
652 if (!empty($savemethod)) {
653 $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
654 }
655 if (!empty($ext_element)) {
656 $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
657 }
658 if (!empty($custommsg)) {
659 if (is_array($custommsg)) {
660 if (!empty($custommsg['success'])) {
661 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
662 }
663 if (!empty($custommsg['error'])) {
664 $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
665 }
666 } else {
667 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
668 }
669 }
670 if ($inputType == 'textarea') {
671 $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
672 $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
673 }
674 $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
675 $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
676 } else {
677 $out = $value;
678 }
679
680 return $out;
681 }
682
701 public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
702 {
703 if ($incbefore) {
704 $text = $incbefore . $text;
705 }
706 if (!$htmltext) {
707 return $text;
708 }
709 $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
710
711 $tag = 'td';
712 if ($notabs == 2) {
713 $tag = 'div';
714 }
715 if ($notabs == 3) {
716 $tag = 'span';
717 }
718 // Sanitize tooltip
719 $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
720
721 $extrastyle = '';
722 if ($direction < 0) {
723 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
724 $extrastyle = 'padding: 0px; padding-left: 2px;';
725 }
726 if ($direction > 0) {
727 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
728 $extrastyle = 'padding: 0px; padding-right: 2px;';
729 }
730
731 $classfortooltip = 'classfortooltip';
732
733 $s = '';
734 $textfordialog = '';
735
736 if ($tooltiptrigger == '') {
737 $htmltext = str_replace('"', '&quot;', $htmltext);
738 } else {
739 $classfortooltip = 'classfortooltiponclick';
740 $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext"';
741 // Set default title of dialog
742 global $langs;
743 if ($langs instanceof Translate) {
744 $textfordialog .= ' title="'.$langs->trans("Note").'"';
745 }
746 $textfordialog .= '>' . $htmltext . '</div>';
747 }
748 if ($tooltipon == 2 || $tooltipon == 3) {
749 $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
750 if ($tooltiptrigger == '') {
751 $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on img tag to store tooltip
752 } else {
753 $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
754 }
755 } else {
756 $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
757 }
758 if ($tooltipon == 1 || $tooltipon == 3) {
759 $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ($tag != 'td' ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
760 if ($tooltiptrigger == '') {
761 $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on td tag to store tooltip
762 } else {
763 $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
764 }
765 } else {
766 $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
767 }
768 if (empty($notabs)) {
769 $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
770 } elseif ($notabs == 2) {
771 $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
772 }
773 // Define value if value is before
774 if ($direction < 0) {
775 $s .= '<' . $tag . $paramfortooltipimg;
776 if ($tag == 'td') {
777 $s .= ' class="valigntop" width="14"';
778 }
779 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
780 }
781 // Use another method to help avoid having a space in value in order to use this value with jquery
782 // Define label
783 if ((string) $text != '') {
784 $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
785 }
786 // Define value if value is after
787 if ($direction > 0) {
788 $s .= '<' . $tag . $paramfortooltipimg;
789 if ($tag == 'td') {
790 $s .= ' class="valignmiddle" width="14"';
791 }
792 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
793 }
794 if (empty($notabs)) {
795 $s .= '</tr></table>';
796 } elseif ($notabs == 2) {
797 $s .= '</div>';
798 }
799
800 return $s;
801 }
802
817 public function textwithpicto($text, $htmltooltip, $direction = 1, $type = 'help', $extracss = 'valignmiddle', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
818 {
819 global $conf, $langs;
820
821 //For backwards compatibility
822 if ($type == '0') {
823 $type = 'info';
824 } elseif ($type == '1') {
825 $type = 'help';
826 }
827 // Clean parameters
828 $tooltiptrigger = preg_replace('/[^a-z0-9]/i', '', $tooltiptrigger);
829
830 if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
831 $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
832 }
833 $alt = '';
834 if ($tooltiptrigger) {
835 $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
836 }
837
838 // If info or help with no javascript, show only text
839 if (empty($conf->use_javascript_ajax)) {
840 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
841 return $text;
842 } else {
843 $alt = $htmltooltip;
844 $htmltooltip = '';
845 }
846 }
847
848 // If info or help with smartphone, show only text (tooltip hover can't works)
849 if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
850 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
851 return $text;
852 }
853 }
854 // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
855 //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
856 //{
857 //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
858 //}
859
860 $img = '';
861 if ($type == 'info') {
862 $img = img_help(($tooltiptrigger != '' ? 2 : 0), $alt);
863 } elseif ($type == 'help') {
864 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
865 } elseif ($type == 'helpclickable') {
866 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
867 } elseif ($type == 'warning') {
868 $img = img_warning($alt);
869 } elseif ($type != 'none') {
870 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
871 $img = img_picto($alt, $type); // $type can be an image path
872 }
873
874 $tooltipon = ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2);
875
876 return $this->textwithtooltip($text, $htmltooltip, $tooltipon, $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
877 }
878
889 public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
890 {
891 global $conf, $langs, $hookmanager;
892
893 $disabled = 0;
894 $ret = '<div class="centpercent center">';
895 $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
896
897 // 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.
898 $parameters = array();
899 $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
900 // check if there is a mass action
901
902 if (is_array($arrayofaction) && count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
903 return;
904 }
905 if (empty($reshook)) {
906 $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
907 if (is_array($arrayofaction)) {
908 foreach ($arrayofaction as $code => $label) {
909 $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
910 }
911 }
912 }
913 $ret .= $hookmanager->resPrint;
914
915 $ret .= '</select>';
916
917 if (empty($conf->dol_optimize_smallscreen)) {
918 $ret .= ajax_combobox('.' . $name . 'select');
919 }
920
921 // 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
922 $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.
923 $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")) . '">';
924 $ret .= '</div>';
925
926 if (!empty($conf->use_javascript_ajax)) {
927 $ret .= '<!-- JS CODE TO ENABLE mass action select -->
928 <script nonce="' . getNonce() . '">
929 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 */
930 atleastoneselected=0;
931 jQuery("."+cssclass).each(function( index ) {
932 /* console.log( index + ": " + $( this ).text() ); */
933 if ($(this).is(\':checked\')) atleastoneselected++;
934 });
935
936 console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
937
938 if (atleastoneselected || ' . ((int) $alwaysvisible) . ') {
939 jQuery("."+name).show();
940 ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
941 ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
942 } else {
943 jQuery("."+name).hide();
944 jQuery("."+name+"other").hide();
945 }
946 }
947
948 jQuery(document).ready(function () {
949 initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
950 jQuery(".' . $cssclass . '").change(function() {
951 console.log("A change was done on .' . $cssclass . '");
952 initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
953 });
954 jQuery(".' . $name . 'select").change(function() {
955 var massaction = $( this ).val();
956 var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
957 if (massaction == "builddoc") {
958 urlform = urlform + "#show_files";
959 }
960 $( this ).closest("form").attr("action", urlform);
961 console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
962 /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
963 if ($(this).val() != \'0\') {
964 jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
965 jQuery(".' . $name . 'other").hide(); /* To disable if another div was open */
966 jQuery(".' . $name . '"+massaction).show();
967 } else {
968 jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
969 jQuery(".' . $name . 'other").hide(); /* To disable any div open */
970 }
971 });
972 });
973 </script>
974 ';
975 }
976
977 return $ret;
978 }
979
980 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
981
999 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)
1000 {
1001 // phpcs:enable
1002 global $langs, $mysoc;
1003
1004 $langs->load("dict");
1005
1006 $selected = (string) $selected;
1007
1008 $out = '';
1010 $countryArray = array();
1011 $favorite = array();
1012 $label = array();
1013 $atleastonefavorite = 0;
1014
1015 $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
1016 $sql .= " FROM " . $this->db->prefix() . "c_country";
1017 $sql .= " WHERE active > 0";
1018 //$sql.= " ORDER BY code ASC";
1019
1020 dol_syslog(get_class($this) . "::select_country", LOG_DEBUG);
1021
1022 $resql = $this->db->query($sql);
1023 if ($resql) {
1024 $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
1025 $num = $this->db->num_rows($resql);
1026 $i = 0;
1027 if ($num) {
1028 while ($i < $num) {
1029 $obj = $this->db->fetch_object($resql);
1030
1031 $countryArray[$i]
1032 = array(
1033 'rowid' => (int) $obj->rowid,
1034 'code_iso' => (string) $obj->code_iso,
1035 'code_iso3' => (string) $obj->code_iso3,
1036 '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 : '')),
1037 'favorite' => (string) $obj->favorite,
1038 'eec' => (string) $obj->eec,
1039 );
1040 $favorite[$i] = $obj->favorite;
1041 $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
1042 $i++;
1043 }
1044
1045 if (empty($disablefavorites)) {
1046 $array1_sort_order = SORT_DESC;
1047 $array2_sort_order = SORT_ASC;
1048 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
1049 } else {
1050 $countryArray = dol_sort_array($countryArray, 'label');
1051 }
1052
1053 if ($showempty) {
1054 if (is_numeric($showempty)) {
1055 $out .= '<option value="">&nbsp;</option>' . "\n";
1056 } else {
1057 $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
1058 }
1059 }
1060
1061 if ($addspecialentries) { // Add dedicated entries for groups of countries
1062 //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
1063 $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1064 $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
1065 if ($mysoc->isInEEC()) {
1066 $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1067 }
1068 $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
1069 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1070 }
1071
1072 foreach ($countryArray as $row) {
1073 //if (empty($showempty) && empty($row['rowid'])) continue;
1074 if (empty($row['rowid'])) {
1075 continue;
1076 }
1077 if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1078 continue; // exclude some countries
1079 }
1080
1081 if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1082 $atleastonefavorite++;
1083 }
1084 if (empty($row['favorite']) && $atleastonefavorite) {
1085 $atleastonefavorite = 0;
1086 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1087 }
1088
1089 $labeltoshow = '';
1090 if ($row['label']) {
1091 $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1092 } else {
1093 $labeltoshow .= '&nbsp;';
1094 }
1095 if ($row['code_iso']) {
1096 $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1097 if (empty($hideflags)) {
1098 $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1099 $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1100 }
1101 }
1102
1103 if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1104 $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']) . '">';
1105 } else {
1106 $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']) . '">';
1107 }
1108 $out .= dol_string_nohtmltag($labeltoshow);
1109 $out .= '</option>' . "\n";
1110 }
1111 }
1112 $out .= '</select>';
1113 } else {
1114 dol_print_error($this->db);
1115 }
1116
1117 // Make select dynamic
1118 if (empty($forcecombo)) {
1119 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1120 $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1121 }
1122
1123 return $out;
1124 }
1125
1136 public function selectPhoneCode($selected = '', $htmlname = 'phone_code', $morecss = 'maxwidth150', $showempty = 0, $country_id_hint = 0)
1137 {
1138 global $langs;
1139
1140 $langs->load("dict");
1141
1142 $out = '';
1143 $codeArray = array();
1144 $favorite = array();
1145 $label = array();
1146 $atleastonefavorite = 0;
1147
1148 $sql = "SELECT rowid, code, label, phone_code, favorite, trunk_prefix";
1149 $sql .= " FROM ".$this->db->prefix()."c_country";
1150 $sql .= " WHERE active > 0 AND phone_code IS NOT NULL AND phone_code != ''";
1151
1152 dol_syslog(get_class($this)."::selectPhoneCode", LOG_DEBUG);
1153 $resql = $this->db->query($sql);
1154 if ($resql) {
1155 $num = $this->db->num_rows($resql);
1156 $i = 0;
1157 while ($i < $num) {
1158 $obj = $this->db->fetch_object($resql);
1159
1160 $translabel = ($obj->code && $langs->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langs->transnoentitiesnoconv("Country".$obj->code) : $obj->label;
1161
1162 $codeArray[$i]['rowid'] = $obj->rowid;
1163 $codeArray[$i]['code'] = $obj->code;
1164 $codeArray[$i]['label'] = $translabel;
1165 $codeArray[$i]['phone_code'] = '+'.$obj->phone_code;
1166 $codeArray[$i]['favorite'] = $obj->favorite;
1167 $codeArray[$i]['trunk_prefix'] = $obj->trunk_prefix;
1168 $favorite[$i] = $obj->favorite;
1169 $label[$i] = dol_string_unaccent($translabel);
1170 $i++;
1171 }
1172
1173 $array1_sort_order = SORT_DESC;
1174 $array2_sort_order = SORT_ASC;
1175 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $codeArray);
1176
1177 $out .= '<select id="select'.$htmlname.'" class="flat selectphonecode'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1178
1179 if ($showempty) {
1180 $out .= '<option value="">&nbsp;</option>'."\n";
1181 }
1182
1183 // Determine which row index to select: prefer country_id_hint match, fallback to first phone_code match
1184 $selectedIdx = -1;
1185 $firstMatchIdx = -1;
1186 if ($selected !== '') {
1187 foreach ($codeArray as $idx => $row) {
1188 if ($row['phone_code'] == $selected) {
1189 if ($firstMatchIdx < 0) {
1190 $firstMatchIdx = $idx;
1191 }
1192 if ($country_id_hint > 0 && $row['rowid'] == $country_id_hint) {
1193 $selectedIdx = $idx;
1194 break;
1195 }
1196 }
1197 }
1198 if ($selectedIdx < 0 && $firstMatchIdx >= 0) {
1199 $selectedIdx = $firstMatchIdx;
1200 }
1201 }
1202
1203 foreach ($codeArray as $idx => $row) {
1204 if (empty($row['code'])) {
1205 continue;
1206 }
1207
1208 if ($row['favorite']) {
1209 $atleastonefavorite++;
1210 }
1211 if (empty($row['favorite']) && $atleastonefavorite) {
1212 $atleastonefavorite = 0;
1213 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1214 }
1215
1216 $tmpflag = picto_from_langcode($row['code'], 'class="saturatemedium paddingrightonly"', 1);
1217
1218 // Short label for selected display: flag + country code
1219 $selectlabel = ($tmpflag ? $tmpflag.' ' : '').$row['code'];
1220
1221 // Detailed label for dropdown list: flag + country name + phone code
1222 $labeltoshow = ($tmpflag ? $tmpflag.' ' : '').$row['label'].' '.$row['phone_code'];
1223
1224 $out .= '<option value="'.dol_escape_htmltag($row['phone_code']).'"';
1225 if ($idx === $selectedIdx) {
1226 $out .= ' selected';
1227 }
1228 $out .= ' data-html="'.dol_escape_htmltag($labeltoshow).'"';
1229 $out .= ' data-select-html="'.dol_escape_htmltag($selectlabel).'"';
1230 $out .= ' data-country-id="'.((int) $row['rowid']).'"';
1231 $out .= ' data-trunk-prefix="'.dol_escape_htmltag((string) $row['trunk_prefix']).'"';
1232 $out .= '>';
1233 $out .= dol_string_nohtmltag($labeltoshow);
1234 $out .= '</option>'."\n";
1235 }
1236 $out .= '</select>';
1237 } else {
1238 dol_print_error($this->db);
1239 }
1240
1241 // Make select dynamic
1242 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1243 $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1244
1245 return $out;
1246 }
1247
1264 public function showPhoneInput($phoneValue, $htmlname, $country_id_hint = 0, $picto = 'object_phoning', $morecss = 'maxwidth150', $maxlength = 0, $countrySelectorId = 'selectcountry_id')
1265 {
1266 global $mysoc;
1267
1268 include_once DOL_DOCUMENT_ROOT.'/core/lib/phone.lib.php';
1269
1270 $codename = $htmlname.'_code';
1271
1272 // Fallback country_id: use caller hint, else main company country
1273 if (empty($country_id_hint) && !empty($mysoc->country_id)) {
1274 $country_id_hint = $mysoc->country_id;
1275 }
1276
1277 // On POST re-display, read the hidden field (which contains the full phone string)
1278 if (GETPOSTISSET($htmlname)) {
1279 $fullPhone = (string) GETPOST($htmlname);
1280 } else {
1281 $fullPhone = (string) $phoneValue;
1282 }
1283
1284 // Split into code + number
1285 $parsed = dol_parse_phone($fullPhone);
1286
1287 // Resolve default phone code: parsed code if set, else from country hint
1288 $phonecode = !empty($parsed['code']) ? $parsed['code'] : dol_get_phone_code_from_country($this->db, $country_id_hint);
1289
1290 $selectedCode = $phonecode;
1291 $numberValue = $parsed['number'];
1292
1293 // Add back trunk prefix for display (e.g. "644986885" → "0644986885" for France)
1294 if ($numberValue !== '' && $selectedCode !== '') {
1295 $trunkPrefix = dol_get_trunk_prefix($this->db, $selectedCode);
1296 if ($trunkPrefix !== '' && strpos($numberValue, $trunkPrefix) !== 0) {
1297 $numberValue = $trunkPrefix.$numberValue;
1298 }
1299 }
1300
1301 // Build output: hidden field (POSTed value)
1302 $out = '<input type="hidden" name="'.dol_escape_htmltag($htmlname).'" id="'.dol_escape_htmltag($htmlname).'" value="'.dol_escape_htmltag($fullPhone).'">';
1303
1304 // Picto
1305 $out .= img_picto('', $picto, 'class="pictofixedwidth"');
1306
1307 // Phone code select (display-only name, not submitted as separate POST param)
1308 $out .= $this->selectPhoneCode($selectedCode, $codename, 'maxwidth75 phone_code_select', 0, $country_id_hint);
1309
1310 // Visible number input (no name — not POSTed)
1311 $out .= '<input type="tel" inputmode="numeric" pattern="[0-9]*" id="'.dol_escape_htmltag($htmlname).'_input" class="'.dol_escape_htmltag($morecss).'"';
1312 if ($maxlength > 0) {
1313 $out .= ' maxlength="'.$maxlength.'"';
1314 }
1315 $out .= ' value="'.dol_escape_htmltag($numberValue).'">';
1316
1317 // Per-field JS to sync hidden field
1318 $out .= $this->getPhoneInputFieldJs($htmlname, $codename);
1319
1320 // Shared JS for country-sync (output once per page)
1321 $out .= $this->getPhoneInputSharedJs($countrySelectorId);
1322
1323 return $out;
1324 }
1325
1336 private function getPhoneInputFieldJs($htmlname, $codename)
1337 {
1338 $hiddenId = dol_escape_js($htmlname);
1339 $inputId = dol_escape_js($htmlname).'_input';
1340 $selectId = 'select'.dol_escape_js($codename);
1341
1342 $out = "\n".'<script type="text/javascript">'."\n";
1343 $out .= 'jQuery(document).ready(function() {'."\n";
1344 $out .= ' function syncPhoneField_'.$hiddenId.'() {'."\n";
1345 $out .= ' var selectEl = jQuery("#'.$selectId.'");'."\n";
1346 $out .= ' var code = selectEl.val() || "";'."\n";
1347 $out .= ' var number = (jQuery("#'.$inputId.'").val() || "").replace(/[^0-9]/g, "");'."\n";
1348 $out .= ' if (code && number) {'."\n";
1349 $out .= ' var selOpt = selectEl[0] && selectEl[0].selectedOptions && selectEl[0].selectedOptions[0];'."\n";
1350 $out .= ' var trunkPrefix = selOpt ? (selOpt.getAttribute("data-trunk-prefix") || "") : "";'."\n";
1351 $out .= ' if (trunkPrefix !== "" && number.indexOf(trunkPrefix) === 0) {'."\n";
1352 $out .= ' number = number.substring(trunkPrefix.length);'."\n";
1353 $out .= ' }'."\n";
1354 $out .= ' jQuery("#'.$hiddenId.'").val(code + " " + number);'."\n";
1355 $out .= ' } else if (number) {'."\n";
1356 $out .= ' jQuery("#'.$hiddenId.'").val(number);'."\n";
1357 $out .= ' } else {'."\n";
1358 $out .= ' jQuery("#'.$hiddenId.'").val("");'."\n";
1359 $out .= ' }'."\n";
1360 $out .= ' }'."\n";
1361 $out .= ' jQuery("#'.$selectId.'").on("change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1362 $out .= ' jQuery("#'.$inputId.'").on("input change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1363 $out .= '});'."\n";
1364 $out .= '</script>'."\n";
1365
1366 return $out;
1367 }
1368
1378 private function getPhoneInputSharedJs($countrySelectorId)
1379 {
1380 if ($this->phoneInputSharedJsLoaded) {
1381 return '';
1382 }
1383 $this->phoneInputSharedJsLoaded = true;
1384
1385 $out = "\n".'<script type="text/javascript">'."\n";
1386 $out .= 'jQuery(document).ready(function() {'."\n";
1387 $out .= ' jQuery("#'.dol_escape_js($countrySelectorId).'").on("change", function() {'."\n";
1388 $out .= ' var country_id = jQuery(this).val();'."\n";
1389 $out .= ' if (country_id) {'."\n";
1390 $out .= ' jQuery.getJSON("'.DOL_URL_ROOT.'/core/ajax/getphonecode.php", {country_id: country_id, token: "'.currentToken().'"}, function(data) {'."\n";
1391 $out .= ' if (data.phone_code) {'."\n";
1392 $out .= ' jQuery(".phone_code_select").each(function() {'."\n";
1393 $out .= ' jQuery(this).val(data.phone_code).trigger("change");'."\n";
1394 $out .= ' });'."\n";
1395 $out .= ' }'."\n";
1396 $out .= ' });'."\n";
1397 $out .= ' }'."\n";
1398 $out .= ' });'."\n";
1399 $out .= '});'."\n";
1400 $out .= '</script>'."\n";
1401
1402 return $out;
1403 }
1404
1418 private function makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
1419 {
1420 dol_syslog(__METHOD__, LOG_DEBUG);
1421 global $langs, $form;
1422 if (empty($form)) {
1423 $form = new Form($this->db);
1424 }
1425 $htmltoenteralink = '';
1426 $i = 0;
1427
1428 // headers
1429 $htmltoenteralink .= '<tr class="liste_titre">';
1430 $htmltoenteralink .= '<td class="nowrap"></td>';
1431 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1432 $htmltoenteralink .= '<td>' . $langs->trans("RefCustomer") . '</td>';
1433 $htmltoenteralink .= '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
1434 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1435 $htmltoenteralink .= '</tr>';
1436
1437 // rows with data
1438 while ($i < $num) {
1439 $objp = $this->db->fetch_object($resqllist);
1440 $alreadylinked = false;
1441 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1442 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1443 $alreadylinked = true;
1444 }
1445 }
1446 $htmltoenteralink .= '<tr class="oddeven">';
1447 $htmltoenteralink .= '<td>';
1448 if ($alreadylinked) {
1449 $htmltoenteralink .= img_picto('', 'link');
1450 } else {
1451 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1452 }
1453 $htmltoenteralink .= '</td>';
1454 $htmltoenteralink .= '<td><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
1455 $htmltoenteralink .= '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
1456 $htmltoenteralink .= '<td class="right">';
1457 if ($possiblelink['label'] == 'LinkToContract') {
1458 $htmltoenteralink .= $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
1459 }
1460 $htmltoenteralink .= '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
1461 $htmltoenteralink .= '</td>';
1462 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1463 $htmltoenteralink .= '</tr>';
1464 $i++;
1465 }
1466
1467 return $htmltoenteralink;
1468 }
1469
1484 private function makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
1485 {
1486 dol_syslog(__METHOD__, LOG_DEBUG);
1487 global $langs, $form;
1488 require_once DOL_DOCUMENT_ROOT . '/eventorganization/class/conferenceorboothattendee.class.php';
1489 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1490 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
1491 $attendeestatic = new ConferenceOrBoothAttendee($this->db);
1492 $companystatic = new Societe($this->db);
1493 $projectstatic = new Project($this->db);
1494 if (empty($form)) {
1495 $form = new Form($this->db);
1496 }
1497 $htmltoenteralink = '';
1498 $i = 0;
1499
1500 // headers
1501 $htmltoenteralink .= '<tr class="liste_titre">';
1502 $htmltoenteralink .= '<td class="nowrap"></td>';
1503 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1504 $htmltoenteralink .= '<td>' . $langs->trans("Name") . '</td>';
1505 $htmltoenteralink .= '<td>' . $langs->trans("Email") . '</td>';
1506 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1507 $htmltoenteralink .= '<td>' . $langs->trans("Project") . '</td>';
1508 $htmltoenteralink .= '<td>' . $langs->trans("DateOfRegistration") . '</td>';
1509 $htmltoenteralink .= '</tr>';
1510
1511 // rows with data
1512 while ($i < $num) {
1513 $objp = $this->db->fetch_object($resqllist);
1514 $alreadylinked = false;
1515 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1516 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1517 $alreadylinked = true;
1518 }
1519 }
1520 $htmltoenteralink .= '<tr class="oddeven">';
1521 $htmltoenteralink .= '<td>';
1522 if ($alreadylinked) {
1523 $htmltoenteralink .= img_picto('', 'link');
1524 } else {
1525 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1526 }
1527 $htmltoenteralink .= '</td>';
1528 $fetchattendee = $attendeestatic->fetch($objp->rowid);
1529 if ($fetchattendee) {
1530 $htmltoenteralink .= '<td>' . $attendeestatic->getNomUrl(0). '</td>';
1531 } else {
1532 $htmltoenteralink .= '<td><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
1533 }
1534 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1535 $htmltoenteralink .= '<td>' . $objp->email . '</td>';
1536 $fetchcompany = $companystatic->fetch($objp->socid);
1537 if ($fetchcompany) {
1538 $htmltoenteralink .= '<td>' . $companystatic->getNomUrl(0). '</td>';
1539 } else {
1540 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1541 }
1542 $fetchcproject = $projectstatic->fetch($objp->fk_project);
1543 if ($fetchcproject) {
1544 $htmltoenteralink .= '<td>' . $projectstatic->getNomUrl(0). '</td>';
1545 } else {
1546 $htmltoenteralink .= '<td>' . $objp->fk_project . '</td>';
1547 }
1548 $htmltoenteralink .= '<td>' . $objp->date_subscription . '</td>';
1549 $htmltoenteralink .= '</tr>';
1550 $i++;
1551 }
1552
1553 return $htmltoenteralink;
1554 }
1555
1556 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1557
1571 public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1572 {
1573 // phpcs:enable
1574 global $conf, $langs;
1575
1576 $langs->load("dict");
1577
1578 $out = '';
1579 //$moreattrib = '';
1580 $incotermArray = array();
1581
1582 $sql = "SELECT rowid, code";
1583 $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1584 $sql .= " WHERE active > 0";
1585 $sql .= " ORDER BY code ASC";
1586
1587 dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1588 $resql = $this->db->query($sql);
1589 if ($resql) {
1590 if ($conf->use_javascript_ajax && !$forcecombo) {
1591 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1592 $out .= ajax_combobox($htmlname, $events);
1593 }
1594
1595 if (!empty($page)) {
1596 $out .= '<form method="post" action="' . $page . '">';
1597 $out .= '<input type="hidden" name="action" value="set_incoterms">';
1598 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1599 }
1600
1601 $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1602 $out .= '<option value="0">&nbsp;</option>';
1603 $num = $this->db->num_rows($resql);
1604 $i = 0;
1605 if ($num) {
1606 while ($i < $num) {
1607 $obj = $this->db->fetch_object($resql);
1608 $incotermArray[$i]['rowid'] = $obj->rowid;
1609 $incotermArray[$i]['code'] = $obj->code;
1610 $i++;
1611 }
1612
1613 foreach ($incotermArray as $row) {
1614 if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1615 $out .= '<option value="' . $row['rowid'] . '" selected>';
1616 } else {
1617 $out .= '<option value="' . $row['rowid'] . '">';
1618 }
1619
1620 if ($row['code']) {
1621 $out .= $row['code'];
1622 }
1623
1624 $out .= '</option>';
1625 }
1626 }
1627 $out .= '</select>';
1628 $out .= ajax_combobox($htmlname);
1629
1630 if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1631 $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1632 //$moreattrib .= ' autocomplete="off"';
1633 }
1634 $out .= '<input id="location_incoterms" class="maxwidthonsmartphone heightofcombo" type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1635
1636 if (!empty($page)) {
1637 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1638 }
1639 } else {
1640 dol_print_error($this->db);
1641 }
1642
1643 return $out;
1644 }
1645
1646 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1647
1661 public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
1662 {
1663 // phpcs:enable
1664 global $langs;
1665
1666 // If product & services are enabled or both disabled.
1667 if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1668 || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1669 if (empty($hidetext)) {
1670 print $langs->trans("Type").'...';
1671 }
1672
1673 print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1674 if ($showempty) {
1675 print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
1676 if ($selected == -1) {
1677 print ' selected';
1678 }
1679 print '>';
1680 if (is_numeric($showempty)) {
1681 print '&nbsp;';
1682 } else {
1683 print $showempty;
1684 }
1685 print '</option>';
1686 }
1687
1688 print '<option value="0"';
1689 if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1690 print ' selected';
1691 }
1692 print '>' . $langs->trans("Product");
1693 print '</option>';
1694
1695 print '<option value="1"';
1696 if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1697 print ' selected';
1698 }
1699 print '>' . $langs->trans("Service");
1700 print '</option>';
1701
1702 print '</select>';
1703
1704 if ($useajaxcombo) {
1705 print ajax_combobox('select_' . $htmlname);
1706 }
1707 //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1708 }
1709 if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1710 print $langs->trans("Service");
1711 print '<input type="hidden" name="' . $htmlname . '" value="1">';
1712 }
1713 if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1714 print $langs->trans("Product");
1715 print '<input type="hidden" name="' . $htmlname . '" value="0">';
1716 }
1717 if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1718 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
1719 }
1720 }
1721
1722 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1723
1729 public function load_cache_types_fees()
1730 {
1731 // phpcs:enable
1732 global $langs;
1733
1734 $num = count($this->cache_types_fees);
1735 if ($num > 0) {
1736 return 0; // Cache already loaded
1737 }
1738
1739 dol_syslog(__METHOD__, LOG_DEBUG);
1740
1741 $langs->load("trips");
1742
1743 $sql = "SELECT c.code, c.label";
1744 $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1745 $sql .= " WHERE active > 0";
1746
1747 $resql = $this->db->query($sql);
1748 if ($resql) {
1749 $num = $this->db->num_rows($resql);
1750 $i = 0;
1751
1752 while ($i < $num) {
1753 $obj = $this->db->fetch_object($resql);
1754
1755 // If a translation exists, we use is, otherwise, we take the label by default
1756 $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1757 $this->cache_types_fees[$obj->code] = $label;
1758 $i++;
1759 }
1760
1761 asort($this->cache_types_fees);
1762
1763 return $num;
1764 } else {
1765 dol_print_error($this->db);
1766 return -1;
1767 }
1768 }
1769
1770 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1771
1780 public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1781 {
1782 // phpcs:enable
1783 global $user, $langs;
1784
1785 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1786
1787 $this->load_cache_types_fees();
1788
1789 print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1790 if ($showempty) {
1791 print '<option value="-1"';
1792 if ($selected == -1) {
1793 print ' selected';
1794 }
1795 print '>&nbsp;</option>';
1796 }
1797
1798 foreach ($this->cache_types_fees as $key => $value) {
1799 print '<option value="' . $key . '"';
1800 if ($key == $selected) {
1801 print ' selected';
1802 }
1803 print '>';
1804 print $value;
1805 print '</option>';
1806 }
1807
1808 print '</select>';
1809 if ($user->admin) {
1810 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1811 }
1812 }
1813
1814
1815 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1816
1839 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)
1840 {
1841 // phpcs:enable
1842 global $conf, $langs;
1843
1844 $out = '';
1845
1846 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1847 if (is_null($ajaxoptions)) {
1848 $ajaxoptions = array();
1849 }
1850
1851 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1852
1853 // No immediate load of all database
1854 $placeholder = '';
1855 if ($selected && empty($selected_input_value)) {
1856 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1857 $societetmp = new Societe($this->db);
1858 $societetmp->fetch($selected);
1859 $selected_input_value = $societetmp->name;
1860 unset($societetmp);
1861 }
1862
1863 // mode 1
1864 $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 : '');
1865
1866 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1867 if (empty($hidelabel)) {
1868 $out .= $langs->trans("RefOrLabel") . ' : ';
1869 } elseif ($hidelabel == 1 && !is_numeric($showempty)) {
1870 $placeholder = $langs->trans($showempty);
1871 } elseif ($hidelabel > 1) {
1872 $placeholder = $langs->trans("RefOrLabel");
1873 if ($hidelabel == 2) {
1874 $out .= img_picto($langs->trans("Search"), 'search');
1875 }
1876 }
1877 $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" />';
1878 if ($hidelabel == 3) {
1879 $out .= img_picto($langs->trans("Search"), 'search');
1880 }
1881
1882 $out .= ajax_event($htmlname, $events);
1883
1884 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, getDolGlobalInt('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1885 } else {
1886 // Immediate load of all database
1887 $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1888 }
1889
1890 return $out;
1891 }
1892
1893
1894 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1895
1921 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 = '')
1922 {
1923 // phpcs:enable
1924
1925 global $conf, $langs;
1926
1927 $out = '';
1928
1929 $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1930 if ($nokeyifsocid && $socid > 0) {
1931 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1932 }
1933
1934 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1935 $ajaxoptions = array();
1936
1937 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1938
1939 // No immediate load of all database
1940 $placeholder = '';
1941 if ($selected && empty($selected_input_value)) {
1942 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1943 $contacttmp = new Contact($this->db);
1944 $contacttmp->fetch($selected);
1945 $selected_input_value = $contacttmp->getFullName($langs);
1946 unset($contacttmp);
1947 }
1948 if (!is_numeric($showempty)) {
1949 $placeholder = $showempty;
1950 }
1951
1952 // mode 1
1953 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1954
1955 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1956
1957 $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" />';
1958
1959 $out .= ajax_event($htmlname, $events);
1960
1961 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/contact/ajax/contact.php', $urloption, getDolGlobalInt('CONTACT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1962 } else {
1963 // Immediate load of all database
1964 $multiple = false;
1965 $disableifempty = 0;
1966 $options_only = 0;
1967 $limitto = '';
1968
1969 $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty);
1970 }
1971
1972 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1973
1974 return $out;
1975 }
1976
1977
1978 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1979
2003 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)
2004 {
2005 // phpcs:enable
2006 global $user, $langs;
2007 global $hookmanager;
2008
2009 $langs->loadLangs(array("companies", "suppliers"));
2010
2011 $out = '';
2012 $num = 0;
2013 $outarray = array();
2014
2015 if ($selected === '') {
2016 $selected = array();
2017 } elseif (!is_array($selected)) {
2018 $selected = array($selected);
2019 }
2020
2021 // Clean $filter that may contains sql conditions so sql code
2022 if (function_exists('testSqlAndScriptInject')) {
2023 if (testSqlAndScriptInject($filter, 3) > 0) {
2024 $filter = '';
2025 return 'SQLInjectionTryDetected';
2026 }
2027 }
2028
2029 if ($filter != '') { // If a filter was provided
2030 $errormsg = '';
2031 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2032
2033 // Redo clean $filter that may contains sql conditions so sql code
2034 if (function_exists('testSqlAndScriptInject')) {
2035 if (testSqlAndScriptInject($filter, 3) > 0) {
2036 $filter = '';
2037 return 'SQLInjectionTryDetected';
2038 }
2039 }
2040 }
2041
2042 // We search companies
2043 $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
2044 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2045 $sql .= ", s.address, s.zip, s.town";
2046 $sql .= ", dictp.code as country_code";
2047 }
2048 $sql .= " FROM " . $this->db->prefix() . "societe as s";
2049 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2050 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
2051 }
2052 if (!$user->hasRight('societe', 'client', 'voir')) {
2053 $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
2054 }
2055 $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
2056 if (!empty($user->socid)) {
2057 $sql .= " AND s.rowid = " . ((int) $user->socid);
2058 }
2059 if ($filter) {
2060 // $filter is safe because, it has been tested by testSqlAndScriptInject() and sanitized by forgeSQLFromUniversalSearchCriteria()
2061 $sqlwhere = $filter;
2062 $sql .= " AND (" . $sqlwhere . ")";
2063 }
2064 if (!$user->hasRight('societe', 'client', 'voir')) {
2065 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
2066 }
2067 if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
2068 $sql .= " AND s.status <> 0";
2069 }
2070 if (!empty($excludeids)) {
2071 $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
2072 }
2073 // Add where from hooks
2074 $parameters = array();
2075 $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
2076 $sql .= $hookmanager->resPrint;
2077 // Add criteria
2078 if ($filterkey && $filterkey != '') {
2079 $sql .= " AND (";
2080 $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
2081 // For natural search
2082 $search_crit = explode(' ', $filterkey);
2083 $i = 0;
2084 if (count($search_crit) > 1) {
2085 $sql .= "(";
2086 }
2087 foreach ($search_crit as $crit) {
2088 if ($i > 0) {
2089 $sql .= " AND ";
2090 }
2091 $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
2092 $i++;
2093 }
2094 if (count($search_crit) > 1) {
2095 $sql .= ")";
2096 }
2097 if (isModEnabled('barcode')) {
2098 $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2099 }
2100 $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2101 $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2102 $sql .= ")";
2103 }
2104 $sql .= $this->db->order("nom", "ASC");
2105 $sql .= $this->db->plimit($limit, 0);
2106
2107 // Build output string
2108 dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
2109 $resql = $this->db->query($sql);
2110 if ($resql) {
2111 // Construct $out and $outarray
2112 $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple' : '') . '>' . "\n";
2113
2114 $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
2115 if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
2116 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2117 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2118 if ($showempty && !is_numeric($showempty)) {
2119 $textifempty = $langs->trans($showempty);
2120 } else {
2121 $textifempty .= $langs->trans("All");
2122 }
2123 }
2124 if ($showempty) {
2125 $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
2126 }
2127
2128 $companytemp = new Societe($this->db);
2129
2130 $num = $this->db->num_rows($resql);
2131 $i = 0;
2132 if ($num) {
2133 while ($i < $num) {
2134 $obj = $this->db->fetch_object($resql);
2135 $label = '';
2136 if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
2137 if (($obj->client) && (!empty($obj->code_client))) {
2138 $label = $obj->code_client . ' - ';
2139 }
2140 if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
2141 $label .= $obj->code_fournisseur . ' - ';
2142 }
2143 $label .= ' ' . $obj->name;
2144 } else {
2145 $label = $obj->name;
2146 }
2147
2148 if (!empty($obj->name_alias)) {
2149 $label .= ' (' . $obj->name_alias . ')';
2150 }
2151
2152 if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
2153 $label .= ' - '.$obj->tva_intra;
2154 }
2155
2156 $labelhtml = $label;
2157
2158 if ($showtype) {
2159 $companytemp->id = $obj->rowid;
2160 $companytemp->client = $obj->client;
2161 $companytemp->fournisseur = $obj->fournisseur;
2162 $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
2163 if ($tmptype) {
2164 $labelhtml .= ' ' . $tmptype;
2165 }
2166
2167 if ($obj->client || $obj->fournisseur) {
2168 $label .= ' (';
2169 }
2170 if ($obj->client == 1 || $obj->client == 3) {
2171 $label .= $langs->trans("Customer");
2172 }
2173 if ($obj->client == 2 || $obj->client == 3) {
2174 $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
2175 }
2176 if ($obj->fournisseur) {
2177 $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
2178 }
2179 if ($obj->client || $obj->fournisseur) {
2180 $label .= ')';
2181 }
2182 }
2183
2184 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2185 $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
2186 if (!empty($obj->country_code)) {
2187 $s .= ', ' . $langs->trans('Country' . $obj->country_code);
2188 }
2189 $label .= $s;
2190 $labelhtml .= $s;
2191 }
2192
2193 if (empty($outputmode)) {
2194 if (in_array($obj->rowid, $selected)) {
2195 $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>';
2196 } else {
2197 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2198 }
2199 } else {
2200 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
2201 }
2202
2203 $i++;
2204 if (($i % 10) == 0) {
2205 $out .= "\n";
2206 }
2207 }
2208 }
2209 $out .= '</select>' . "\n";
2210 if (!$forcecombo) {
2211 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2212 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
2213 }
2214 } else {
2215 dol_print_error($this->db);
2216 }
2217
2218 $this->result = array('nbofthirdparties' => $num);
2219
2220 if ($outputmode) {
2221 return $outarray;
2222 }
2223 return $out;
2224 }
2225
2226
2252 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 = '')
2253 {
2254 global $conf, $user, $langs, $hookmanager, $action;
2255
2256 $langs->load('companies');
2257
2258 if (empty($htmlid)) {
2259 $htmlid = $htmlname;
2260 }
2261 $num = 0;
2262 $out = '';
2263 $outarray = array();
2264
2265 if ($selected === '') {
2266 $selected = array();
2267 } elseif (!is_array($selected)) {
2268 $selected = array((int) $selected);
2269 }
2270
2271 // Clean $filter that may contains sql conditions so sql code
2272 if (function_exists('testSqlAndScriptInject')) {
2273 if (testSqlAndScriptInject($filter, 3) > 0) {
2274 $filter = '';
2275 return 'SQLInjectionTryDetected';
2276 }
2277 }
2278
2279 if ($filter != '') { // If a filter was provided
2280 if (preg_match('/[\‍(\‍)]/', $filter)) {
2281 // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
2282 $errormsg = '';
2283 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2284
2285 // Redo clean $filter that may contains sql conditions so sql code
2286 if (function_exists('testSqlAndScriptInject')) {
2287 if (testSqlAndScriptInject($filter, 3) > 0) {
2288 $filter = '';
2289 return 'SQLInjectionTryDetected';
2290 }
2291 }
2292 } else {
2293 // If not, we do nothing. We already know that there is no parenthesis
2294 // TODO Disallow this case in a future by returning an error here.
2295 dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Filter Syntax.", LOG_WARNING);
2296 }
2297 }
2298
2299 if (!is_object($hookmanager)) {
2300 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2301 $hookmanager = new HookManager($this->db);
2302 }
2303
2304 // We search third parties
2305 $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";
2306 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2307 $sql .= ", s.nom as company, s.town AS company_town";
2308 }
2309 $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
2310 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2311 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = sp.fk_soc";
2312 }
2313 $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
2314 $sql .= " AND ((sp.fk_user_creat = ".((int) $user->id)." AND sp.priv = 1) OR sp.priv = 0)"; // check if this is a private contact
2315 if ($socid > 0 || $socid == -1) {
2316 $sql .= " AND sp.fk_soc = " . ((int) $socid);
2317 }
2318 if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
2319 $sql .= " AND sp.statut <> 0";
2320 }
2321 // filter user access
2322 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
2323 $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 .")";
2324 }
2325 if ($user->socid > 0) {
2326 $sql .= " AND sp.fk_soc = ".((int) $user->socid);
2327 }
2328 if ($filter) {
2329 // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
2330 // if not, by testSqlAndScriptInject() only.
2331 $sanitizedfilter = $filter;
2332 $sql .= " AND (" . $sanitizedfilter . ")";
2333 }
2334 // Add where from hooks
2335 $parameters = array();
2336 $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
2337 $sql .= $hookmanager->resPrint;
2338 $sql .= " ORDER BY sp.lastname ASC";
2339
2340 dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
2341 $resql = $this->db->query($sql);
2342 if ($resql) {
2343 $num = $this->db->num_rows($resql);
2344
2345 if ($htmlname != 'none' && !$options_only) {
2346 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
2347 }
2348
2349 if ($showempty && !is_numeric($showempty)) {
2350 $textforempty = $showempty;
2351 $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
2352 } else {
2353 if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
2354 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
2355 }
2356 if ($showempty == 2) {
2357 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
2358 }
2359 }
2360
2361 $i = 0;
2362 if ($num) {
2363 include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2364 $contactstatic = new Contact($this->db);
2365
2366 while ($i < $num) {
2367 $obj = $this->db->fetch_object($resql);
2368
2369 // Set email (or phones) and town extended infos
2370 $extendedInfos = '';
2371 if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2372 $extendedInfos = array();
2373 $email = trim($obj->email);
2374 if (!empty($email)) {
2375 $extendedInfos[] = $email;
2376 } else {
2377 $phone = trim($obj->phone);
2378 $phone_perso = trim($obj->phone_perso);
2379 $phone_mobile = trim($obj->phone_mobile);
2380 if (!empty($phone)) {
2381 $extendedInfos[] = $phone;
2382 }
2383 if (!empty($phone_perso)) {
2384 $extendedInfos[] = $phone_perso;
2385 }
2386 if (!empty($phone_mobile)) {
2387 $extendedInfos[] = $phone_mobile;
2388 }
2389 }
2390 $contact_town = trim($obj->contact_town);
2391 $company_town = trim($obj->company_town);
2392 if (!empty($contact_town)) {
2393 $extendedInfos[] = $contact_town;
2394 } elseif (!empty($company_town)) {
2395 $extendedInfos[] = $company_town;
2396 }
2397 $extendedInfos = implode(' - ', $extendedInfos);
2398 if (!empty($extendedInfos)) {
2399 $extendedInfos = ' - ' . $extendedInfos;
2400 }
2401 }
2402
2403 $contactstatic->id = $obj->rowid;
2404 $contactstatic->lastname = $obj->lastname;
2405 $contactstatic->firstname = $obj->firstname;
2406 if ($obj->statut == 1) {
2407 $tmplabel = '';
2408 if ($htmlname != 'none') {
2409 $disabled = 0;
2410 if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
2411 $disabled = 1;
2412 }
2413 if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
2414 $disabled = 1;
2415 }
2416 if (!empty($selected) && in_array($obj->rowid, $selected)) {
2417 $out .= '<option value="' . $obj->rowid . '"';
2418 if ($disabled) {
2419 $out .= ' disabled';
2420 }
2421 $out .= ' selected>';
2422
2423 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2424 if ($showfunction && $obj->poste) {
2425 $tmplabel .= ' (' . $obj->poste . ')';
2426 }
2427 if (($showsoc > 0) && $obj->company) {
2428 $tmplabel .= ' - (' . $obj->company . ')';
2429 }
2430
2431 $out .= $tmplabel;
2432 $out .= '</option>';
2433 } else {
2434 $out .= '<option value="' . $obj->rowid . '"';
2435 if ($disabled) {
2436 $out .= ' disabled';
2437 }
2438 $out .= '>';
2439
2440 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2441 if ($showfunction && $obj->poste) {
2442 $tmplabel .= ' (' . $obj->poste . ')';
2443 }
2444 if (($showsoc > 0) && $obj->company) {
2445 $tmplabel .= ' - (' . $obj->company . ')';
2446 }
2447
2448 $out .= $tmplabel;
2449 $out .= '</option>';
2450 }
2451 } else {
2452 if (in_array($obj->rowid, $selected)) {
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 }
2463 }
2464
2465 if ($tmplabel != '') {
2466 array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
2467 }
2468 }
2469 $i++;
2470 }
2471 } else {
2472 $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
2473 $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
2474 $out .= $labeltoshow;
2475 $out .= '</option>';
2476 }
2477
2478 $parameters = array(
2479 'socid' => $socid,
2480 'htmlname' => $htmlname,
2481 'resql' => $resql,
2482 'out' => &$out,
2483 'showfunction' => $showfunction,
2484 'showsoc' => $showsoc,
2485 );
2486
2487 $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2488
2489 if ($htmlname != 'none' && !$options_only) {
2490 $out .= '</select>';
2491 }
2492
2493 if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
2494 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2495 $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
2496 }
2497
2498 $this->num = $num;
2499
2500 if ($options_only === 2) {
2501 // Return array of options
2502 return $outarray;
2503 } else {
2504 return $out;
2505 }
2506 } else {
2507 dol_print_error($this->db);
2508 return -1;
2509 }
2510 }
2511
2512
2513 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2514
2525 public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2526 {
2527 // phpcs:enable
2528 global $langs, $conf;
2529
2530 // On recherche les remises
2531 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2532 $sql .= " re.description, re.fk_facture_source";
2533 $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2534 $sql .= " WHERE re.fk_soc = " . (int) $socid;
2535 $sql .= " AND re.entity = " . ((int) $conf->entity);
2536 if ($filter) {
2537 $sanitizedfilter = $filter;
2538 $sql .= " AND " . $sanitizedfilter;
2539 }
2540 $sql .= " ORDER BY re.description ASC";
2541
2542 dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
2543 $resql = $this->db->query($sql);
2544 if ($resql) {
2545 print '<select id="select_' . $htmlname . '" class="flat maxwidth200onsmartphone" name="' . $htmlname . '">';
2546 $num = $this->db->num_rows($resql);
2547
2548 $qualifiedlines = $num;
2549
2550 $i = 0;
2551 if ($num) {
2552 print '<option value="0">&nbsp;</option>';
2553 while ($i < $num) {
2554 $obj = $this->db->fetch_object($resql);
2555 $desc = dol_trunc($obj->description, 40);
2556 if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
2557 $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
2558 }
2559 if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
2560 $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
2561 }
2562 if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
2563 $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
2564 }
2565 if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
2566 $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
2567 }
2568
2569 $selectstring = '';
2570 if ($selected > 0 && $selected == $obj->rowid) {
2571 $selectstring = ' selected';
2572 }
2573
2574 $disabled = '';
2575 if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2576 $qualifiedlines--;
2577 $disabled = ' disabled';
2578 }
2579
2580 if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2581 $tmpfac = new Facture($this->db);
2582 if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2583 $desc = $desc . ' - ' . $tmpfac->ref;
2584 }
2585 }
2586
2587 print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2588 $i++;
2589 }
2590 }
2591 print '</select>';
2592 print ajax_combobox('select_' . $htmlname);
2593
2594 return $qualifiedlines;
2595 } else {
2596 dol_print_error($this->db);
2597 return -1;
2598 }
2599 }
2600
2601
2602 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2603
2619 public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2620 {
2621 // phpcs:enable
2622 print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2623 }
2624
2625 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2626
2651 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)
2652 {
2653 // phpcs:enable
2654 global $conf, $user, $langs, $hookmanager;
2655 global $action;
2656
2657 // Convert $selected into an int (in case it is an object)
2658 if (is_object($userselected)) {
2659 $selected = (int) $userselected->id;
2660 } elseif (is_numeric($userselected)) {
2661 $selected = (int) $userselected;
2662 } elseif (is_array($userselected)) {
2663 $selected = $userselected;
2664 } else {
2665 $selected = -1;
2666 }
2667
2668 // If no preselected user defined, we take current user
2669 if ((is_numeric($selected) && ((int) $selected < -4 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2670 $selected = $user->id;
2671 }
2672
2673 // Convert selected int into an array
2674 if (!is_array($selected)) {
2675 if ($selected === -1 || $selected === '') {
2676 $selected = array();
2677 } else {
2678 $selected = array($selected);
2679 }
2680 }
2681
2682 // Exclude some users in $excludeUsers string
2683 $excludeUsers = null;
2684 if (is_array($exclude)) {
2685 $excludeUsers = implode(",", $exclude);
2686 }
2687
2688 // Include some users in $includeUsers string
2689 $includeUsers = null;
2690 $includeUsersArray = array();
2691 if (is_array($include)) {
2692 $includeUsersArray = $include;
2693 } elseif ($include == 'hierarchy') {
2694 // Build list includeUsersArray to have only hierarchy
2695 $includeUsersArray = $user->getAllChildIds(0);
2696 } elseif ($include == 'hierarchyme') {
2697 // Build list includeUsersArray to have only hierarchy and current user
2698 $includeUsersArray = $user->getAllChildIds(1);
2699 }
2700 // Get list of allowed users
2701 /* 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
2702 * see all other users and element in other. For example in agenda, we can have permission to read all event of otherusers.
2703 * So we disable this.
2704 if (!$user->hasRight('user', 'user', 'lire')) {
2705 if (empty($includeUsersArray)) {
2706 $includeUsers = implode(",", $user->getAllChildIds(1));
2707 } else {
2708 $includeUsers = implode(",", array_intersect($includeUsersArray, $user->getAllChildIds(1)));
2709 }
2710 } else {
2711 $includeUsers = implode(",", $includeUsersArray);
2712 } */
2713 $includeUsers = implode(",", $includeUsersArray);
2714
2715 $num = 0;
2716
2717 $out = '';
2718 $outarray = array();
2719 $outarray2 = array();
2720
2721 // Do we want to show the label of entity into the combo list ?
2722 $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity) && !preg_match('/^search_/', $htmlname);
2723 $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2724
2725 // Forge request to select users
2726 $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";
2727 if ($showlabelofentity) {
2728 $sql .= ", e.label";
2729 }
2730 $sql .= " FROM " . $this->db->prefix() . "user as u";
2731 if ($showlabelofentity) {
2732 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2733 }
2734 // Condition here should be the same than into societe->getSalesRepresentatives().
2735 if ($userissuperadminentityone && $force_entity !== 'default') {
2736 if (!empty($force_entity)) {
2737 $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2738 } else {
2739 $sql .= " WHERE u.entity IS NOT NULL";
2740 }
2741 } else {
2742 if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2743 $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2744 } else {
2745 $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2746 }
2747 }
2748
2749 if (!empty($user->socid)) {
2750 $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2751 }
2752 if (is_array($exclude) && $excludeUsers) {
2753 $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2754 }
2755 if ($includeUsers) {
2756 $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2757 }
2758 if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2759 $sql .= " AND (u.statut <> 0";
2760 if (!empty($selected)) {
2761 $sql .= " OR rowid IN (".$this->db->sanitize(implode(',', $selected)).")"; // We must always keep the selected users to avoid to loose it/them when updating
2762 }
2763 $sql .= ")";
2764 }
2765 if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX')) {
2766 $sql .= " AND u.employee <> 0";
2767 }
2768 if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX')) {
2769 $sql .= " AND u.fk_soc IS NULL";
2770 }
2771 if (!empty($morefilter)) {
2772 $errormessage = '';
2773 $sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
2774 if ($errormessage) {
2775 $this->errors[] = $errormessage;
2776 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2777 if ($outputmode == 0) {
2778 return 'Error bad param $morefilter';
2779 } else {
2780 return array();
2781 }
2782 }
2783 }
2784
2785 //Add hook to filter on user (for example on usergroup define in custom modules)
2786 $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2787 if (!empty($reshook)) {
2788 $sql .= $hookmanager->resPrint;
2789 }
2790
2791 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2792 $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2793 } else {
2794 $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2795 }
2796
2797 dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2798
2799 $resql = $this->db->query($sql);
2800 if ($resql) {
2801 $num = $this->db->num_rows($resql);
2802 $i = 0;
2803 if ($num) {
2804 // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2805 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2806 if ($show_empty && !$multiple) {
2807 $textforempty = ' ';
2808 if (!empty($conf->use_javascript_ajax)) {
2809 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2810 }
2811 if (!is_numeric($show_empty)) {
2812 $textforempty = $show_empty;
2813 }
2814 $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2815
2816 $outarray[($show_empty < 0 ? $show_empty : -1)] = $textforempty;
2817 $outarray2[($show_empty < 0 ? $show_empty : -1)] = array(
2818 'id' => ($show_empty < 0 ? $show_empty : -1),
2819 'label' => $textforempty,
2820 'labelhtml' => $textforempty,
2821 'color' => '',
2822 'picto' => ''
2823 );
2824 }
2825 if ($showalso == 2 || $showalso == 3) {
2826 $out .= '<option value="-3"' . ((in_array(-3, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("MyTeam") . ' --</option>' . "\n";
2827
2828 $hasAtLeastOneSubordinate = (count($user->getAllChildIds(1)) > 1);
2829 if ($hasAtLeastOneSubordinate) {
2830 //$sql = "SELECT rowid FROM".MAIN_DB_PREFIX."user "
2831 $outarray[-3] = '-- ' . $langs->trans("MyTeam") . ' --';
2832 $outarray2[-3] = array(
2833 'id' => -3,
2834 'label' => '-- ' . $langs->trans("MyTeam") . ' --',
2835 'labelhtml' => '-- ' . $langs->trans("MyTeam") . ' --',
2836 'color' => '',
2837 'picto' => ''
2838 );
2839 }
2840 }
2841 if ($showalso == 1 || $showalso == 3) {
2842 $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2843
2844 $outarray[-2] = '-- ' . $langs->trans("Everybody") . ' --';
2845 $outarray2[-2] = array(
2846 'id' => -2,
2847 'label' => '-- ' . $langs->trans("Everybody") . ' --',
2848 'labelhtml' => '-- ' . $langs->trans("Everybody") . ' --',
2849 'color' => '',
2850 'picto' => ''
2851 );
2852 }
2853 if ($showalso == 4) {
2854 $out .= '<option value="-4"' . ((in_array(-4, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("AllProjectContacts") . ' --</option>' . "\n";
2855
2856 $outarray[-4] = '-- ' . $langs->trans("AllProjectContacts") . ' --';
2857 $outarray2[-4] = array(
2858 'id' => -4,
2859 'label' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2860 'labelhtml' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2861 'color' => '',
2862 'picto' => ''
2863 );
2864 }
2865
2866 $userstatic = new User($this->db);
2867
2868 while ($i < $num) {
2869 $obj = $this->db->fetch_object($resql);
2870
2871 $userstatic->id = $obj->rowid;
2872 $userstatic->lastname = $obj->lastname;
2873 $userstatic->firstname = $obj->firstname;
2874 $userstatic->photo = $obj->photo;
2875 $userstatic->status = $obj->status;
2876 $userstatic->entity = $obj->entity;
2877 $userstatic->admin = $obj->admin;
2878 $userstatic->gender = $obj->gender;
2879
2880 $disableline = '';
2881 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2882 $disableline = ($enableonlytext ? $enableonlytext : '1');
2883 }
2884
2885 $labeltoshow = '';
2886 $labeltoshowhtml = '';
2887
2888 // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2889 $fullNameMode = 0;
2890 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2891 $fullNameMode = 1; //Firstname+lastname
2892 }
2893 $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2894 $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2895 if (empty($obj->firstname) && empty($obj->lastname)) {
2896 $labeltoshow .= $obj->login;
2897 $labeltoshowhtml .= $obj->login;
2898 }
2899
2900 // Complete name with a more info string like: ' (info1 - info2 - ...)'
2901 $moreinfo = '';
2902 $moreinfohtml = '';
2903 if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2904 $moreinfo .= ($moreinfo ? ' - ' : ' (');
2905 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2906 $moreinfo .= $obj->login;
2907 $moreinfohtml .= $obj->login;
2908 }
2909 if ($showstatus >= 0) {
2910 if ($obj->status == 1 && $showstatus == 1) {
2911 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2912 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2913 }
2914 if ($obj->status == 0 && $showstatus == 1) {
2915 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2916 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2917 }
2918 }
2919 if ($showlabelofentity) {
2920 if (empty($obj->entity)) {
2921 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2922 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2923 } else {
2924 if ($obj->entity != $conf->entity) {
2925 $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2926 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2927 }
2928 }
2929 }
2930 $moreinfo .= (!empty($moreinfo) ? ')' : '');
2931 $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2932 if (!empty($disableline) && $disableline != '1') {
2933 // Add text from $enableonlytext parameter
2934 $moreinfo .= ' - ' . $disableline;
2935 $moreinfohtml .= ' - ' . $disableline;
2936 }
2937 $labeltoshow .= $moreinfo;
2938 $labeltoshowhtml .= $moreinfohtml;
2939
2940 $out .= '<option value="' . $obj->rowid . '"';
2941 if (!empty($disableline)) {
2942 $out .= ' disabled';
2943 }
2944 if (in_array($obj->rowid, $selected)) {
2945 $out .= ' selected';
2946 }
2947 $out .= ' data-html="';
2948
2949 $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2950 if ($showstatus >= 0 && $obj->status == 0) {
2951 $outhtml .= '<strike class="opacitymediumxxx">';
2952 }
2953 $outhtml .= $labeltoshowhtml;
2954 if ($showstatus >= 0 && $obj->status == 0) {
2955 $outhtml .= '</strike>';
2956 }
2957 $labeltoshowhtml = $outhtml;
2958
2959 $out .= dol_escape_htmltag($outhtml);
2960 $out .= '">';
2961 $out .= $labeltoshow;
2962 $out .= '</option>';
2963
2964 $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2965 $outarray2[$userstatic->id] = array(
2966 'id' => $userstatic->id,
2967 'label' => $labeltoshow,
2968 'labelhtml' => $labeltoshowhtml,
2969 'color' => '',
2970 'picto' => ''
2971 );
2972
2973 $i++;
2974 }
2975 } else {
2976 $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2977 $out .= '<option value="">' . $langs->trans("None") . '</option>';
2978 }
2979 $out .= '</select>';
2980
2981 if ($num && !$forcecombo) {
2982 // Enhance with select2
2983 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2984 $out .= ajax_combobox($htmlname);
2985 }
2986 } else {
2987 dol_print_error($this->db);
2988 }
2989
2990 $this->num = $num;
2991
2992 if ($outputmode == 2) {
2993 return $outarray2;
2994 } elseif ($outputmode) {
2995 return $outarray;
2996 }
2997
2998 return $out;
2999 }
3000
3001
3002 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3026 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)
3027 {
3028 // phpcs:enable
3029 global $langs, $user;
3030
3031 $userstatic = new User($this->db);
3032 $out = '';
3033
3034 if (!empty($_SESSION['assignedtouser'])) {
3035 $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
3036 if (!is_array($assignedtouser)) {
3037 $assignedtouser = array();
3038 }
3039 } else {
3040 $assignedtouser = array();
3041 }
3042 $nbassignetouser = count($assignedtouser);
3043
3044 //if ($nbassignetouser && $action != 'view') $out .= '<br>';
3045 if ($nbassignetouser) {
3046 $out .= '<ul class="attendees">';
3047 }
3048 $i = 0;
3049 $ownerid = 0;
3050 foreach ($assignedtouser as $key => $value) {
3051 if ($value['id'] == $ownerid) {
3052 continue;
3053 }
3054
3055 $out .= '<li>';
3056
3057 $userstatic->fetch($value['id']);
3058 $out .= $userstatic->getNomUrl(-4);
3059
3060 if ($i == 0) {
3061 $ownerid = $value['id'];
3062 $out .= ' (' . $langs->trans("Owner") . ')';
3063 }
3064 // Add picto to delete owner/assignee
3065 if ($nbassignetouser > 1 && $action != 'view') {
3066 $canremoveassignee = 1;
3067 if ($i == 0) {
3068 // We are on the owner of the event
3069 if (!$canremoveowner) {
3070 $canremoveassignee = 0;
3071 }
3072 if (!$user->hasRight('agenda', 'allactions', 'create')) {
3073 $canremoveassignee = 0; // Can't remove the owner
3074 }
3075 } else {
3076 // We are not on the owner of the event but on a secondary assignee
3077 }
3078 if ($canremoveassignee) {
3079 // If user has all permission, he should be ableto remove a assignee.
3080 // If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
3081 $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 . '">';
3082 }
3083 }
3084 // Show my availability
3085 if ($showproperties) {
3086 if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
3087 $out .= '<div class="myavailability inline-block">';
3088 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3089 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ':</span>';
3090 $out .= '</span>';
3091 $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>';
3092 $out .= '</div>';
3093 }
3094 }
3095 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3096 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3097
3098 $out .= '</li>';
3099 $i++;
3100 }
3101 if ($nbassignetouser) {
3102 $out .= '</ul>';
3103 }
3104
3105 // Method with no ajax
3106 if ($action != 'view') {
3107 // Section to add another user
3108 $out .= '<div class="divadduser'.$htmlname.'">';
3109 $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
3110 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3111 $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
3112 $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
3113 $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
3114 $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
3115 $out .= '});';
3116 $out .= '})</script>';
3117 $out .= img_picto('', 'user', 'class="pictofixedwidth"');
3118 $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter, 0, '', 'minwidth200');
3119 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3120 $out .= $langs->trans("Add").'</button>';
3121 $out .= '</div>';
3122 //$out .= '<br>';
3123 }
3124
3125 return $out;
3126 }
3127
3128 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3148 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())
3149 {
3150 // phpcs:enable
3151 global $langs;
3152
3153 require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php';
3154 require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php';
3155 $formresources = new FormResource($this->db);
3156 $resourcestatic = new Dolresource($this->db);
3157
3158 $out = '';
3159 if (!empty($_SESSION['assignedtoresource'])) {
3160 $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
3161 if (!is_array($assignedtoresource)) {
3162 $assignedtoresource = array();
3163 }
3164 } else {
3165 $assignedtoresource = array();
3166 }
3167 $nbassignetoresource = count($assignedtoresource);
3168
3169 //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
3170 if ($nbassignetoresource) {
3171 $out .= '<ul class="attendees">';
3172 }
3173 $i = 0;
3174
3175 foreach ($assignedtoresource as $key => $value) {
3176 $out .= '<li>';
3177 $resourcestatic->fetch($value['id']);
3178 $out .= $resourcestatic->getNomUrl(-1);
3179 if ($nbassignetoresource >= 1 && $action != 'view') {
3180 $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 . '">';
3181 }
3182 // Show my availability
3183 if ($showproperties) {
3184 if (is_array($listofresourceid) && count($listofresourceid)) {
3185 $out .= '<div class="myavailability inline-block">';
3186 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3187 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ': </span>';
3188 $out .= '</span>';
3189 $out .= ' <input title="'.$langs->trans("Availability").'" id="transparencyresource'.$value['id'].'" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofresourceid[$value['id']]['transparency'] ? ' checked' : '') . '><label for="transparencyresource'.$value['id'].'">' . $langs->trans("Busy") . '</label>';
3190 $out .= '</div>';
3191 }
3192 }
3193 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3194 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3195
3196 $out .= '</li>';
3197 $i++;
3198 }
3199 if ($nbassignetoresource) {
3200 $out .= '</ul>';
3201 }
3202
3203 // Method with no ajax
3204 if ($action != 'view') {
3205 $out .= '<input type="hidden" class="removedassignedresourcehidden" name="removedassignedresource" value="">';
3206 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3207 $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
3208 $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
3209 $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
3210 $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
3211 $out .= '});';
3212 $out .= '})</script>';
3213
3214 $events = array();
3215 if ($nbassignetoresource) {
3216 //$out .= img_picto('', 'add', 'class="pictofixedwidth"');
3217 } else {
3218 $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
3219 }
3220 $out .= $formresources->select_resource_list(0, $htmlname, '', 1, 1, 0, $events, '', 2, 0, 'minwidth200');
3221 //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
3222 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3223 $out .= $langs->trans("Add");
3224 $out .= '</button>';
3225 $out .= '<br>';
3226 }
3227
3228 return $out;
3229 }
3230
3231 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3232
3262 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)
3263 {
3264 // phpcs:enable
3265 global $langs, $conf;
3266
3267 $out = '';
3268
3269 // check parameters
3270 $price_level = (!empty($price_level) ? $price_level : 0);
3271 if (is_null($ajaxoptions)) {
3272 $ajaxoptions = array();
3273 }
3274
3275 if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
3276 if (isModEnabled("product") && !isModEnabled('service')) {
3277 $filtertype = '0';
3278 } elseif (!isModEnabled('product') && isModEnabled("service")) {
3279 $filtertype = '1';
3280 }
3281 }
3282
3283 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3284 $placeholder = (is_numeric($showempty) ? '' : 'placeholder="'.dolPrintHTML($showempty).'"');
3285
3286 if ($selected && empty($selected_input_value)) {
3287 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3288 $producttmpselect = new Product($this->db);
3289 $producttmpselect->fetch($selected);
3290 $selected_input_value = $producttmpselect->ref;
3291 unset($producttmpselect);
3292 }
3293 // handle case where product or service module is disabled + no filter specified
3294 if ($filtertype == '') {
3295 if (!isModEnabled('product')) { // when product module is disabled, show services only
3296 $filtertype = 1;
3297 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3298 $filtertype = 0;
3299 }
3300 }
3301 // mode=1 means customers products
3302 $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;
3303 if ((int) $warehouseId > 0) {
3304 $urloption .= '&warehouseid=' . (int) $warehouseId;
3305 }
3306
3307 if (isModEnabled('variants') && is_array($selected_combinations)) {
3308 // Code to automatically insert with javascript the select of attributes under the select of product
3309 // when a parent of variant has been selected.
3310 // Note: Samecode than for product input using select
3311 $htmltag = 'input';
3312 $out .= '
3313 <!-- script to auto show attributes select tags if a variant was selected -->
3314 <script nonce="' . getNonce() . '">
3315 // auto show attributes fields
3316 selected = ' . json_encode($selected_combinations) . ';
3317 combvalues = {};
3318
3319 jQuery(document).ready(function () {
3320
3321 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3322 if (jQuery(this).val() == \'free\') {
3323 jQuery(\'div#attributes_box\').empty();
3324 }
3325 });
3326
3327 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3328
3329 if (!jQuery(this).val()) {
3330 jQuery(\'div#attributes_box\').empty();
3331 return;
3332 }
3333
3334 console.log("A change has started. We get variants fields to inject html select");
3335
3336 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3337 id: jQuery(this).val()
3338 }, function (data) {
3339 jQuery(\'div#attributes_box\').empty();
3340
3341 jQuery.each(data, function (key, val) {
3342
3343 combvalues[val.id] = val.values;
3344
3345 var span = jQuery(document.createElement(\'div\')).css({
3346 \'display\': \'table-row\'
3347 });
3348
3349 span.append(
3350 jQuery(document.createElement(\'div\')).text(val.label).css({
3351 \'font-weight\': \'bold\',
3352 \'display\': \'table-cell\'
3353 })
3354 );
3355
3356 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3357 \'margin-left\': \'15px\',
3358 \'white-space\': \'pre\'
3359 }).append(
3360 jQuery(document.createElement(\'option\')).val(\'\')
3361 );
3362
3363 jQuery.each(combvalues[val.id], function (key, val) {
3364 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3365
3366 if (selected[val.fk_product_attribute] == val.id) {
3367 tag.attr(\'selected\', \'selected\');
3368 }
3369
3370 html.append(tag);
3371 });
3372
3373 span.append(html);
3374 jQuery(\'div#attributes_box\').append(span);
3375 });
3376 })
3377 });
3378
3379 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3380 });
3381 </script>
3382 ';
3383 }
3384
3385 if (empty($hidelabel)) {
3386 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3387 } elseif ($hidelabel > 1) {
3388 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3389 if ($hidelabel == 2) {
3390 $out .= img_picto($langs->trans("Search"), 'search');
3391 }
3392 }
3393
3394 $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" />';
3395 if ($hidelabel == 3) {
3396 $out .= img_picto($langs->trans("Search"), 'search');
3397 }
3398
3399 $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);
3400 } else {
3401 $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase, $warehouseId);
3402
3403 if (isModEnabled('variants') && is_array($selected_combinations)) {
3404 // Code to automatically insert with javascript the select of attributes under the select of product
3405 // when a parent of variant has been selected.
3406 // Note: Samecode than for product input using Ajax
3407 $htmltag = 'select';
3408 $out .= '
3409 <!-- script to auto show attributes select tags if a variant was selected -->
3410 <script nonce="' . getNonce() . '">
3411 // auto show attributes fields
3412 selected = ' . json_encode($selected_combinations) . ';
3413 combvalues = {};
3414
3415 jQuery(document).ready(function () {
3416
3417 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3418 if (jQuery(this).val() == \'free\') {
3419 jQuery(\'div#attributes_box\').empty();
3420 }
3421 });
3422
3423 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3424
3425 if (!jQuery(this).val()) {
3426 jQuery(\'div#attributes_box\').empty();
3427 return;
3428 }
3429
3430 console.log("A change has started. We get variants fields to inject html select");
3431
3432 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3433 id: jQuery(this).val()
3434 }, function (data) {
3435 jQuery(\'div#attributes_box\').empty();
3436
3437 jQuery.each(data, function (key, val) {
3438
3439 combvalues[val.id] = val.values;
3440
3441 var span = jQuery(document.createElement(\'div\')).css({
3442 \'display\': \'table-row\'
3443 });
3444
3445 span.append(
3446 jQuery(document.createElement(\'div\')).text(val.label).css({
3447 \'font-weight\': \'bold\',
3448 \'display\': \'table-cell\'
3449 })
3450 );
3451
3452 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3453 \'margin-left\': \'15px\',
3454 \'white-space\': \'pre\'
3455 }).append(
3456 jQuery(document.createElement(\'option\')).val(\'\')
3457 );
3458
3459 jQuery.each(combvalues[val.id], function (key, val) {
3460 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3461
3462 if (selected[val.fk_product_attribute] == val.id) {
3463 tag.attr(\'selected\', \'selected\');
3464 }
3465
3466 html.append(tag);
3467 });
3468
3469 span.append(html);
3470 jQuery(\'div#attributes_box\').append(span);
3471 });
3472 })
3473 });
3474
3475 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3476 });
3477 </script>
3478 ';
3479 }
3480 }
3481
3482 if (empty($nooutput)) {
3483 print $out;
3484 } else {
3485 return $out;
3486 }
3487 }
3488
3489 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3490
3506 public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
3507 {
3508 // phpcs:enable
3509
3510 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3511
3512 $error = 0;
3513 $out = '';
3514
3515 if (!$forcecombo) {
3516 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3517 $events = array();
3518 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("BOM_USE_SEARCH_TO_SELECT"));
3519 }
3520
3521 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3522
3523 $sql = 'SELECT b.rowid, b.ref, b.label as bomLabel, p.label as productLabel';
3524 $sql .= ' FROM ' . $this->db->prefix() . 'bom_bom as b';
3525 $sql .= ' INNER JOIN ' . $this->db->prefix() . 'product as p ON b.fk_product = p.rowid';
3526 $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
3527 if (!empty($status)) {
3528 $sql .= ' AND status = ' . (int) $status;
3529 }
3530 if (!empty($type)) {
3531 $sql .= ' AND bomtype = ' . (int) $type;
3532 }
3533 if (!empty($TProducts)) {
3534 $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
3535 }
3536 if (!empty($limit)) {
3537 $sql .= ' LIMIT ' . (int) $limit;
3538 }
3539 $resql = $this->db->query($sql);
3540 if ($resql) {
3541 if ($showempty) {
3542 $out .= '<option value="-1"';
3543 if (empty($selected)) {
3544 $out .= ' selected';
3545 }
3546 $out .= '>&nbsp;</option>';
3547 }
3548 while ($obj = $this->db->fetch_object($resql)) {
3549 $out .= '<option value="' . $obj->rowid . '"';
3550 if ($obj->rowid == $selected) {
3551 $out .= 'selected';
3552 }
3553 $out .= '>' . $obj->ref . ' - ' . $obj->productLabel . ' - ' . $obj->bomLabel . '</option>';
3554 }
3555 } else {
3556 $error++;
3557 dol_print_error($this->db);
3558 }
3559 $out .= '</select>';
3560 if (empty($nooutput)) {
3561 print $out;
3562 } else {
3563 return $out;
3564 }
3565 }
3566
3567 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3568
3595 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)
3596 {
3597 // phpcs:enable
3598 global $langs;
3599 global $hookmanager;
3600
3601 $out = '';
3602 $outarray = array();
3603
3604 // Units
3605 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3606 $langs->load('other');
3607 }
3608
3609 $warehouseStatusArray = array();
3610 if (!empty($warehouseStatus)) {
3611 require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
3612 if (preg_match('/warehouseclosed/', $warehouseStatus)) {
3613 $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
3614 }
3615 if (preg_match('/warehouseopen/', $warehouseStatus)) {
3616 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
3617 }
3618 if (preg_match('/warehouseinternal/', $warehouseStatus)) {
3619 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
3620 }
3621 }
3622
3623 $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";
3624 if (count($warehouseStatusArray)) {
3625 $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
3626 } else {
3627 $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", '0', "p.stock") . " AS stock";
3628 }
3629
3630 $sql = "SELECT ";
3631
3632 // Add select from hooks
3633 $parameters = array();
3634 $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
3635 if (empty($reshook)) {
3636 $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
3637 } else {
3638 $sql .= $hookmanager->resPrint;
3639 }
3640
3641 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3642 // Take randomly the first category of product to allow a sort on it. Bugged feature !
3643 $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
3644 FROM " . $this->db->prefix() . "categorie_product
3645 WHERE " . $this->db->prefix() . "categorie_product.fk_product = p.rowid
3646 LIMIT 1
3647 ) AS categorie_product_id";
3648 }
3649
3650 // Price by customer
3651 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3652 $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
3653 $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';
3654 $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref, custdiscount_percent";
3655 }
3656 // Units
3657 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3658 $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";
3659 $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';
3660 }
3661
3662 // Multilang : we add translation
3663 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3664 $sql .= ", pl.label as label_translated";
3665 $sql .= ", pl.description as description_translated";
3666 $selectFields .= ", label_translated";
3667 $selectFields .= ", description_translated";
3668 }
3669 // Price by quantity
3670 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3671 $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
3672 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3673 $sql .= " AND price_level = " . ((int) $price_level);
3674 }
3675 $sql .= " ORDER BY date_price";
3676 $sql .= " DESC LIMIT 1) as price_rowid";
3677 $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
3678 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3679 $sql .= " AND price_level = " . ((int) $price_level);
3680 }
3681 $sql .= " ORDER BY date_price";
3682 $sql .= " DESC LIMIT 1) as price_by_qty";
3683 $selectFields .= ", price_rowid, price_by_qty";
3684 }
3685
3686 //$sqlfields = $sql; // $sql fields to remove for count total
3687
3688 $sql .= " FROM ".$this->db->prefix()."product as p";
3689
3690 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
3691 $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
3692 }
3693
3694 // Add from (left join) from hooks
3695 $parameters = array(
3696 'socid' => $socid,
3697 );
3698 $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
3699 $sql .= $hookmanager->resPrint;
3700
3701 if (count($warehouseStatusArray)) {
3702 // Return line if product is inside the selected stock. If not, e.* and p.* will be null so we will count 0.
3703 // Replace this with a AND EXISTS ? Not possible as we need the ps.reel field for the SUM or 0 if no link.
3704 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps ON ps.fk_product = p.rowid";
3705 $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e ON ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
3706 $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')';
3707 }
3708
3709 // Price by customer (Add field pcp for the older price for couple product/thirdparty.
3710 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3711 $now = dol_now();
3712 $sql .= " LEFT JOIN (";
3713 $sql .= " SELECT pcp1.*";
3714 $sql .= " FROM " . $this->db->prefix() . "product_customer_price AS pcp1";
3715 $sql .= " LEFT JOIN (";
3716 $sql .= " SELECT fk_soc, fk_product, MIN(date_begin) AS date_begin";
3717 $sql .= " FROM " . $this->db->prefix() . "product_customer_price";
3718 $sql .= " WHERE fk_soc = " . ((int) $socid);
3719 $sql .= " AND date_begin <= '" . $this->db->idate($now) . "'";
3720 $sql .= " AND (date_end IS NULL OR '" . $this->db->idate($now) . "' <= date_end)";
3721 $sql .= " GROUP BY fk_soc, fk_product";
3722 $sql .= " ) AS pcp2 ON pcp1.fk_soc = pcp2.fk_soc AND pcp1.fk_product = pcp2.fk_product AND pcp1.date_begin = pcp2.date_begin";
3723 $sql .= " WHERE pcp2.fk_soc IS NOT NULL";
3724 $sql .= " ) AS pcp ON pcp.fk_soc = " . ((int) $socid) . " AND pcp.fk_product = p.rowid";
3725 }
3726 // Units : we add unit properties with a link on the primary key of unit
3727 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3728 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units as u ON u.rowid = p.fk_unit";
3729 }
3730 // Multilang : we add translation fields with a link on unique key fk_product/lang.
3731 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3732 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid";
3733 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3734 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3735 $soc = new Societe($this->db);
3736 $result = $soc->fetch($socid);
3737 if ($result > 0 && !empty($soc->default_lang)) {
3738 $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3739 } else {
3740 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3741 }
3742 } else {
3743 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3744 }
3745 }
3746
3747 // Add WHERE conditions
3748 $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3749 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3750 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD_BUT_ALLOW_SEARCH_IN_EAN13')) {
3751 if (strlen($filterkey) != 13) {
3752 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3753 }
3754 } else {
3755 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3756 }
3757 }
3758 if ($finished == 0) {
3759 $sql .= " AND p.finished = " . ((int) $finished);
3760 } elseif ($finished == 1) {
3761 $sql .= " AND p.finished = ".((int) $finished);
3762 }
3763 if ($status >= 0) {
3764 $sql .= " AND p.tosell = ".((int) $status);
3765 }
3766 if ($status_purchase >= 0) {
3767 $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3768 }
3769 // Filter by product type
3770 if (strval($filtertype) != '') {
3771 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3772 } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3773 $sql .= " AND p.fk_product_type = 1";
3774 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3775 $sql .= " AND p.fk_product_type = 0";
3776 }
3777
3778 if ((int) $warehouseId > 0) {
3779 $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)";
3780 }
3781
3782 // Add where from hooks
3783 $parameters = array(
3784 'filterkey' => &$filterkey,
3785 'socid' => $socid,
3786 );
3787 $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3788 $sql .= $hookmanager->resPrint;
3789 // Add criteria on ref/label
3790 if ($filterkey != '') {
3791 $sqlSupplierSearch = '';
3792
3793 $sql .= ' AND (';
3794 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3795 // For natural search
3796 $search_crit = explode(' ', $filterkey);
3797 $i = 0;
3798 if (count($search_crit) > 1) {
3799 $sql .= "(";
3800 }
3801 foreach ($search_crit as $crit) {
3802 if ($i > 0) {
3803 $sql .= " AND ";
3804 }
3805 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3806 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3807 $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3808 }
3809 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3810 $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3811 }
3812 if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3813 $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3814 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3815 $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3816 }
3817 }
3818
3819 // include search in supplier ref
3820 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3821 $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' AND ' : '';
3822 $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3823 }
3824 $sql .= ")";
3825 $i++;
3826 }
3827 if (count($search_crit) > 1) {
3828 $sql .= ")";
3829 }
3830 if (isModEnabled('barcode')) {
3831 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3832 }
3833
3834 // include search in supplier ref
3835 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3836 $sql .= " OR EXISTS (SELECT pfp.fk_product FROM " . $this->db->prefix() . "product_fournisseur_price as pfp WHERE p.rowid = pfp.fk_product";
3837 $sql .= " AND (";
3838 $sql .= $sqlSupplierSearch;
3839 $sql .= "))";
3840 }
3841
3842 $sql .= ')';
3843 }
3844 if (count($warehouseStatusArray)) {
3845 $sql .= " GROUP BY " . $this->db->sanitize($selectFields, 0, 0, 1); // To have the SUM on ps.reel working in the select.
3846 }
3847
3848 // Sort by category
3849 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3850 $sql .= " ORDER BY categorie_product_id ".(getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1 ? "ASC" : "DESC");
3851 } else {
3852 $sql .= $this->db->order("p.ref");
3853 }
3854
3855 $limit = getDolGlobalInt('SEARCH_LIMIT_AJAX') ?: $limit; // SEARCH_LIMIT_AJAX is a hidden option that has priority on visible option PRODUIT_LIMIT_SIZE if set.
3856 $sql .= $this->db->plimit($limit, 0);
3857
3858 /* The fast and low memory method to get and count full list converts the sql into a sql count */
3859 /*
3860 $nbtotalofrecords = 0;
3861 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
3862 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
3863
3864 $resql = $this->db->query($sqlforcount);
3865 if ($resql) {
3866 $objforcount = $this->db->fetch_object($resql);
3867 $nbtotalofrecords = $objforcount->nbtotalofrecords;
3868 } else {
3869 dol_print_error($this->db);
3870 }
3871 */
3872
3873 // Build output string
3874 dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
3875
3876 // If we have no $limit parameter, this request may hang dur to high number of lines returned.
3877 // This should not happen because this method should not be called directly, iIt is called by select_produit() that always add a $limit parameter.
3878 $result = $this->db->query($sql);
3879
3880 if ($result) {
3881 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3882 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3883 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3884
3885 $num = $this->db->num_rows($result);
3886
3887 $events = array();
3888
3889 if (!$forcecombo) {
3890 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3891 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3892 }
3893
3894 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3895
3896 $textifempty = '';
3897 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3898 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3899 if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3900 if ($showempty && !is_numeric($showempty)) {
3901 $textifempty = $langs->trans($showempty);
3902 } else {
3903 $textifempty .= $langs->trans("All");
3904 }
3905 } else {
3906 if ($showempty && !is_numeric($showempty)) {
3907 $textifempty = $langs->trans($showempty);
3908 }
3909 }
3910 if ($showempty) {
3911 $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3912 }
3913
3914 $i = 0;
3915 while ($num && $i < $num) {
3916 $opt = '';
3917 $optJson = array();
3918 $objp = $this->db->fetch_object($result);
3919
3920 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
3921 $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3922 $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3923 $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3924 $sql .= " ORDER BY quantity ASC";
3925
3926 dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3927 $result2 = $this->db->query($sql);
3928 if ($result2) {
3929 $nb_prices = $this->db->num_rows($result2);
3930 $j = 0;
3931 while ($nb_prices && $j < $nb_prices) {
3932 $objp2 = $this->db->fetch_object($result2);
3933
3934 $objp->price_by_qty_rowid = $objp2->rowid;
3935 $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3936 $objp->price_by_qty_quantity = $objp2->quantity;
3937 $objp->price_by_qty_unitprice = $objp2->unitprice;
3938 $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3939 // For backward compatibility
3940 $objp->quantity = $objp2->quantity;
3941 $objp->price = $objp2->price;
3942 $objp->unitprice = $objp2->unitprice;
3943 $objp->remise_percent = $objp2->remise_percent;
3944
3945 //$objp->tva_tx is not overwritten by $objp2 value
3946 //$objp->default_vat_code is not overwritten by $objp2 value
3947
3948 $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3949 '@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';
3950 $j++;
3951
3952 // Add new entry
3953 // "key" value of json key array is used by jQuery automatically as selected value
3954 // "label" value of json key array is used by jQuery automatically as text for combo box
3955 $out .= $opt;
3956 array_push($outarray, $optJson);
3957 }
3958 }
3959 } else {
3960 if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3961 $price_product = new Product($this->db);
3962 $price_product->fetch($objp->rowid, '', '', '1');
3963
3964 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3965 $priceparser = new PriceParser($this->db);
3966 $price_result = $priceparser->parseProduct($price_product);
3967 if ($price_result >= 0) {
3968 $objp->price = $price_result;
3969 $objp->unitprice = $price_result;
3970 //Calculate the VAT
3971 $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3972 $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3973 }
3974 }
3975 if (getDolGlobalInt('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES') && !empty($objp->custprice)) {
3976 $price_level = '';
3977 }
3978 $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
3979 // Add new entry
3980 // "key" value of json key array is used by jQuery automatically as selected value
3981 // "label" value of json key array is used by jQuery automatically as text for combo box
3982 $out .= $opt;
3983 array_push($outarray, $optJson);
3984 }
3985
3986 $i++;
3987 }
3988
3989 $out .= '</select>';
3990
3991 $this->db->free($result);
3992
3993 if (empty($outputmode)) {
3994 return $out;
3995 }
3996
3997 return $outarray;
3998 } else {
3999 dol_print_error($this->db);
4000 }
4001
4002 return '';
4003 }
4004
4020 protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
4021 {
4022 global $langs, $conf, $user;
4023 global $hookmanager;
4024
4025 $outkey = '';
4026 $outval = '';
4027 $outref = '';
4028 $outlabel = '';
4029 $outlabel_translated = '';
4030 $outdesc = '';
4031 $outdesc_translated = '';
4032 $outbarcode = '';
4033 $outorigin = '';
4034 $outtype = '';
4035 $outprice_ht = '';
4036 $outprice_ttc = '';
4037 $outpricebasetype = '';
4038 $outtva_tx = '';
4039 $outdefault_vat_code = '';
4040 $outqty = 1;
4041 $outdiscount = '0';
4042
4043 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4044
4045 $label = $objp->label;
4046 if (!empty($objp->label_translated)) {
4047 $label = $objp->label_translated;
4048 }
4049 if (!empty($filterkey) && $filterkey != '') {
4050 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4051 }
4052
4053 $outkey = $objp->rowid;
4054 $outref = $objp->ref;
4055 $outrefcust = empty($objp->custref) ? '' : $objp->custref;
4056 $outlabel = $objp->label;
4057 $outdesc = $objp->description;
4058 if (getDolGlobalInt('MAIN_MULTILANGS')) {
4059 $outlabel_translated = $objp->label_translated;
4060 $outdesc_translated = $objp->description_translated;
4061 }
4062 $outbarcode = $objp->barcode;
4063 $outorigin = $objp->fk_country;
4064 $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
4065
4066 $outtype = $objp->fk_product_type;
4067 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4068 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4069
4070 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4071 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
4072 }
4073
4074 // Units
4075 $outvalUnits = '';
4076 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4077 if (!empty($objp->unit_short)) {
4078 $outvalUnits .= ' - ' . $objp->unit_short;
4079 }
4080 }
4081 if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
4082 if (!empty($objp->weight) && $objp->weight_units !== null) {
4083 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4084 $outvalUnits .= ' - ' . $unitToShow;
4085 }
4086 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4087 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4088 $outvalUnits .= ' - ' . $unitToShow;
4089 }
4090 if (!empty($objp->surface) && $objp->surface_units !== null) {
4091 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4092 $outvalUnits .= ' - ' . $unitToShow;
4093 }
4094 if (!empty($objp->volume) && $objp->volume_units !== null) {
4095 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4096 $outvalUnits .= ' - ' . $unitToShow;
4097 }
4098 }
4099 if ($outdurationvalue && $outdurationunit) {
4100 $da = array(
4101 'h' => $langs->trans('Hour'),
4102 'd' => $langs->trans('Day'),
4103 'w' => $langs->trans('Week'),
4104 'm' => $langs->trans('Month'),
4105 'y' => $langs->trans('Year')
4106 );
4107 if (isset($da[$outdurationunit])) {
4108 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4109 }
4110 }
4111
4112 // Set stocktag (stock too low or not or unknown)
4113 $stocktag = 0;
4114 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4115 if ($user->hasRight('stock', 'lire')) {
4116 if ($objp->stock > 0) {
4117 $stocktag = 1;
4118 } elseif ($objp->stock <= 0) {
4119 $stocktag = -1;
4120 }
4121 }
4122 }
4123
4124 // Set $labeltoshow
4125 $labeltoshow = '';
4126 $labeltoshow .= $objp->ref;
4127 if (!empty($objp->custref)) {
4128 $labeltoshow .= ' (' . $objp->custref . ')';
4129 }
4130 if ($outbarcode) {
4131 $labeltoshow .= ' (' . $outbarcode . ')';
4132 }
4133 $labeltoshow .= ' - ' . dol_trunc($label, $maxlengtharticle);
4134 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4135 $labeltoshow .= ' (' . getCountry($outorigin, '1') . ')';
4136 }
4137
4138 // Set $labltoshowhtml
4139 $labeltoshowhtml = '';
4140 $labeltoshowhtml .= $objp->ref;
4141 if (!empty($objp->custref)) {
4142 $labeltoshowhtml .= ' (' . $objp->custref . ')';
4143 }
4144 if (!empty($filterkey) && $filterkey != '') {
4145 $labeltoshowhtml = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $labeltoshowhtml, 1);
4146 }
4147 if ($outbarcode) {
4148 $labeltoshowhtml .= ' (' . $outbarcode . ')';
4149 }
4150 $labeltoshowhtml .= ' - ' . dol_trunc($label, $maxlengtharticle);
4151 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4152 $labeltoshowhtml .= ' (' . getCountry($outorigin, '1') . ')';
4153 }
4154
4155 // Stock
4156 $labeltoshowstock = '';
4157 $labeltoshowhtmlstock = '';
4158 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4159 if ($user->hasRight('stock', 'lire')) {
4160 $labeltoshowstock .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4161
4162 if ($objp->stock > 0) {
4163 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_ok">';
4164 } elseif ($objp->stock <= 0) {
4165 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_too_low">';
4166 }
4167 $labeltoshowhtmlstock .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4168 $labeltoshowhtmlstock .= '</span>';
4169
4170 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4171 $langs->load("stocks");
4172
4173 $tmpproduct = new Product($this->db);
4174 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4175 $tmpproduct->load_virtual_stock();
4176 $virtualstock = $tmpproduct->stock_theorique;
4177
4178 $labeltoshowstock .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4179
4180 $labeltoshowhtmlstock .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4181 if ($virtualstock > 0) {
4182 $labeltoshowhtmlstock .= '<span class="product_line_stock_ok">';
4183 } elseif ($virtualstock <= 0) {
4184 $labeltoshowhtmlstock .= '<span class="product_line_stock_too_low">';
4185 }
4186 $labeltoshowhtmlstock .= $virtualstock;
4187 $labeltoshowhtmlstock .= '</span>';
4188
4189 unset($tmpproduct);
4190 }
4191 }
4192 }
4193
4194 // Price
4195 $found = 0;
4196 $labeltoshowprice = '';
4197 $labeltoshowhtmlprice = '';
4198 // If we need a particular price level (from 1 to n)
4199 if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4200 $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
4201 $sql .= " FROM " . $this->db->prefix() . "product_price";
4202 $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
4203 $sql .= " AND entity IN (" . getEntity('productprice') . ")";
4204 $sql .= " AND price_level = " . ((int) $price_level);
4205 $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
4206 $sql .= " LIMIT 1";
4207
4208 dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
4209 $result2 = $this->db->query($sql);
4210 if ($result2) {
4211 $objp2 = $this->db->fetch_object($result2);
4212 if ($objp2) {
4213 $found = 1;
4214 if ($objp2->price_base_type == 'HT') {
4215 $labeltoshowprice .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4216 $labeltoshowhtmlprice .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4217 } else {
4218 $labeltoshowprice .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4219 $labeltoshowhtmlprice .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4220 }
4221 $outprice_ht = price($objp2->price);
4222 $outprice_ttc = price($objp2->price_ttc);
4223 $outpricebasetype = $objp2->price_base_type;
4224 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
4225 $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
4226 $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
4227 } else {
4228 $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
4229 $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
4230 }
4231 }
4232 } else {
4233 dol_print_error($this->db);
4234 }
4235 }
4236
4237 // Price by quantity
4238 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
4239 $found = 1;
4240 $outqty = $objp->quantity;
4241 $outdiscount = $objp->remise_percent;
4242 if ($objp->quantity == 1) {
4243 $labeltoshowprice .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
4244 $labeltoshowhtmlprice .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
4245 $labeltoshowprice .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4246 $labeltoshowhtmlprice .= $langs->transnoentities("Unit");
4247 } else {
4248 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4249 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4250 $labeltoshowprice .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4251 $labeltoshowhtmlprice .= $langs->transnoentities("Units");
4252 }
4253
4254 $outprice_ht = price($objp->unitprice);
4255 $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
4256 $outpricebasetype = $objp->price_base_type;
4257 $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
4258 $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
4259 }
4260 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
4261 $labeltoshowprice .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4262 $labeltoshowhtmlprice .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4263 }
4264 if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
4265 $labeltoshowprice .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4266 $labeltoshowhtmlprice .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4267 }
4268
4269 // Price by customer
4270 if (empty($hidepriceinlabel) && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4271 if (!empty($objp->idprodcustprice)) {
4272 $found = 1;
4273
4274 if ($objp->custprice_base_type == 'HT') {
4275 $labeltoshowprice .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4276 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4277 } else {
4278 $labeltoshowprice .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4279 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4280 }
4281
4282 $outprice_ht = price($objp->custprice);
4283 $outprice_ttc = price($objp->custprice_ttc);
4284 $outpricebasetype = $objp->custprice_base_type;
4285 $outtva_tx = $objp->custtva_tx;
4286 $outdefault_vat_code = $objp->custdefault_vat_code;
4287 $outdiscount = $objp->custdiscount_percent;
4288 }
4289 }
4290
4291 // If level no defined or multiprice not found, we used the default price
4292 if (empty($hidepriceinlabel) && !$found) {
4293 if ($objp->price_base_type == 'HT') {
4294 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4295 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4296 } else {
4297 $labeltoshowprice .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4298 $labeltoshowhtmlprice .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4299 }
4300 $outprice_ht = price($objp->price);
4301 $outprice_ttc = price($objp->price_ttc);
4302 $outpricebasetype = $objp->price_base_type;
4303 $outtva_tx = $objp->tva_tx;
4304 $outdefault_vat_code = $objp->default_vat_code;
4305 }
4306
4307 // Build options
4308 $opt = '<option value="' . $objp->rowid . '"';
4309 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
4310 if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
4311 $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 . '"';
4312 }
4313 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
4314 $opt .= ' data-labeltrans="' . $outlabel_translated . '"';
4315 $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
4316 }
4317
4318 if ($stocktag == 1) {
4319 $opt .= ' class="product_line_stock_ok" data-html="'.$labeltoshowhtml.$outvalUnits.$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4320 //$opt .= ' class="product_line_stock_ok"';
4321 }
4322 if ($stocktag == -1) {
4323 $opt .= ' class="product_line_stock_too_low" data-html="'.$labeltoshowhtml.$outvalUnits.$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4324 //$opt .= ' class="product_line_stock_too_low"';
4325 }
4326
4327 $opt .= '>';
4328
4329 // Ref, barcode, country
4330 $opt .= $labeltoshow;
4331 $outval .= $labeltoshowhtml;
4332
4333 // Units
4334 $opt .= $outvalUnits;
4335 $outval .= $outvalUnits;
4336
4337 // Price
4338 $opt .= $labeltoshowprice;
4339 $outval .= $labeltoshowhtmlprice;
4340
4341 // Stock
4342 $opt .= $labeltoshowstock;
4343 $outval .= $labeltoshowhtmlstock;
4344
4345
4346 $parameters = array('objp' => $objp);
4347 $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
4348 if (empty($reshook)) {
4349 $opt .= $hookmanager->resPrint;
4350 } else {
4351 $opt = $hookmanager->resPrint;
4352 }
4353
4354 $opt .= "</option>\n";
4355 $optJson = array(
4356 'key' => $outkey,
4357 'value' => $outref,
4358 'label' => $outval,
4359 'label2' => $outlabel,
4360 'desc' => $outdesc,
4361 'type' => $outtype,
4362 'price_ht' => price2num($outprice_ht),
4363 'price_ttc' => price2num($outprice_ttc),
4364 'price_ht_locale' => price(price2num($outprice_ht)),
4365 'price_ttc_locale' => price(price2num($outprice_ttc)),
4366 'pricebasetype' => $outpricebasetype,
4367 'tva_tx' => $outtva_tx,
4368 'default_vat_code' => $outdefault_vat_code,
4369 'qty' => $outqty,
4370 'discount' => $outdiscount,
4371 'duration_value' => $outdurationvalue,
4372 'duration_unit' => $outdurationunit,
4373 'pbq' => $outpbq,
4374 'labeltrans' => $outlabel_translated,
4375 'desctrans' => $outdesc_translated,
4376 'ref_customer' => $outrefcust
4377 );
4378 }
4379
4380 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4381
4398 public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $notused = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '', $nooutput = 0)
4399 {
4400 // phpcs:enable
4401 global $langs, $conf;
4402 global $price_level, $status, $finished;
4403
4404 if (!isset($status)) {
4405 $status = 1;
4406 }
4407
4408 $selected_input_value = '';
4409 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
4410 if ((int) $selected > 0) {
4411 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4412 $producttmpselect = new Product($this->db);
4413 $producttmpselect->fetch((int) $selected);
4414 $selected_input_value = $producttmpselect->ref;
4415 unset($producttmpselect);
4416 }
4417
4418 // mode=2 means suppliers products
4419 $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
4420
4421 $s = ($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="'.$morecss.'" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
4422
4423 $s .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalInt('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
4424 } else {
4425 $s = $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $notused, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, getDolGlobalInt('SUPPLIER_SHOW_STOCK_IN_PRODUCTS_COMBO'), $placeholder);
4426 }
4427
4428 if ($nooutput) {
4429 return $s;
4430 } else {
4431 print $s;
4432 }
4433 }
4434
4435 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4436
4455 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 = '')
4456 {
4457 // phpcs:enable
4458 global $langs, $conf, $user;
4459 global $hookmanager;
4460
4461 $out = '';
4462 $outarray = array();
4463
4464 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4465
4466 $langs->load('stocks');
4467 // Units
4468 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4469 $langs->load('other');
4470 }
4471
4472 $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,";
4473 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
4474 $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
4475 $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
4476 $sql .= ", pfp.supplier_reputation";
4477 // if we use supplier description of the products
4478 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4479 $sql .= ", pfp.desc_fourn as description";
4480 } else {
4481 $sql .= ", p.description";
4482 }
4483 // Units
4484 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4485 $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";
4486 }
4487
4488 // Add select from hooks
4489 $parameters = [];
4490 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
4491 $sql .= $hookmanager->resPrint;
4492
4493 $sql .= " FROM " . $this->db->prefix() . "product as p";
4494
4495 // Add join from hooks
4496 $parameters = [];
4497 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
4498 $sql .= $hookmanager->resPrint;
4499
4500 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
4501 if ($socid > 0) {
4502 $sql .= " AND pfp.fk_soc = " . ((int) $socid);
4503 }
4504 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4505 // Units
4506 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4507 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
4508 }
4509 $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
4510 if ($statut != -1) {
4511 $sql .= " AND p.tobuy = " . ((int) $statut);
4512 }
4513 if (strval($filtertype) != '') {
4514 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
4515 }
4516
4517 // Add where from hooks
4518 $parameters = array();
4519 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
4520 $sql .= $hookmanager->resPrint;
4521 // Add criteria on ref/label
4522 if ($filterkey != '') {
4523 $sql .= ' AND (';
4524 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
4525 // For natural search
4526 $search_crit = explode(' ', $filterkey);
4527 $i = 0;
4528 if (count($search_crit) > 1) {
4529 $sql .= "(";
4530 }
4531 foreach ($search_crit as $crit) {
4532 if ($i > 0) {
4533 $sql .= " AND ";
4534 }
4535 $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) . "%'";
4536 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4537 $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
4538 }
4539 $sql .= ")";
4540 $i++;
4541 }
4542 if (count($search_crit) > 1) {
4543 $sql .= ")";
4544 }
4545 if (isModEnabled('barcode')) {
4546 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4547 $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4548 }
4549 $sql .= ')';
4550 }
4551 $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
4552 $sql .= $this->db->plimit($limit, 0);
4553
4554 // Build output string
4555
4556 dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
4557 $result = $this->db->query($sql);
4558 if ($result) {
4559 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4560 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
4561
4562 $num = $this->db->num_rows($result);
4563
4564 //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
4565 $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
4566 if (!$selected) {
4567 $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4568 } else {
4569 $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4570 }
4571
4572 $i = 0;
4573 while ($i < $num) {
4574 $objp = $this->db->fetch_object($result);
4575
4576 if (is_null($objp->idprodfournprice)) {
4577 // There is no supplier price found, we will use the vat rate for sale
4578 $objp->tva_tx = $objp->tva_tx_sale;
4579 $objp->default_vat_code = $objp->default_vat_code_sale;
4580 }
4581
4582 $outkey = $objp->idprodfournprice; // id in table of price
4583 if (!$outkey && $alsoproductwithnosupplierprice) {
4584 $outkey = 'idprod_' . $objp->rowid; // id of product
4585 }
4586
4587 $outref = $objp->ref;
4588 $outbarcode = $objp->barcode;
4589 $outqty = 1;
4590 $outdiscount = 0;
4591 $outtype = $objp->fk_product_type;
4592 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4593 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4594
4595 // Units
4596 $outvalUnits = '';
4597 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4598 if (!empty($objp->unit_short)) {
4599 $outvalUnits .= ' - ' . $objp->unit_short;
4600 }
4601 if (!empty($objp->weight) && $objp->weight_units !== null) {
4602 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4603 $outvalUnits .= ' - ' . $unitToShow;
4604 }
4605 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4606 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4607 $outvalUnits .= ' - ' . $unitToShow;
4608 }
4609 if (!empty($objp->surface) && $objp->surface_units !== null) {
4610 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4611 $outvalUnits .= ' - ' . $unitToShow;
4612 }
4613 if (!empty($objp->volume) && $objp->volume_units !== null) {
4614 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4615 $outvalUnits .= ' - ' . $unitToShow;
4616 }
4617 if ($outdurationvalue && $outdurationunit) {
4618 $da = array(
4619 'h' => $langs->trans('Hour'),
4620 'd' => $langs->trans('Day'),
4621 'w' => $langs->trans('Week'),
4622 'm' => $langs->trans('Month'),
4623 'y' => $langs->trans('Year')
4624 );
4625 if (isset($da[$outdurationunit])) {
4626 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4627 }
4628 }
4629 }
4630
4631 $objRef = $objp->ref;
4632 if ($filterkey && $filterkey != '') {
4633 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
4634 }
4635 $objRefFourn = $objp->ref_fourn;
4636 if ($filterkey && $filterkey != '') {
4637 $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
4638 }
4639 $label = $objp->label;
4640 if ($filterkey && $filterkey != '') {
4641 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4642 }
4643
4644 switch ($objp->fk_product_type) {
4646 $picto = 'product';
4647 break;
4649 $picto = 'service';
4650 break;
4651 default:
4652 $picto = '';
4653 break;
4654 }
4655
4656 if (empty($picto)) {
4657 $optlabel = '';
4658 } else {
4659 $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
4660 }
4661
4662 $optlabel .= $objp->ref;
4663 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4664 $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
4665 }
4666 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4667 $optlabel .= ' (' . $outbarcode . ')';
4668 }
4669 $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4670
4671 $outvallabel = $objRef;
4672 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4673 $outvallabel .= ' (' . $objRefFourn . ')';
4674 }
4675 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4676 $outvallabel .= ' (' . $outbarcode . ')';
4677 }
4678 $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4679
4680 // Units
4681 $optlabel .= $outvalUnits;
4682 $outvallabel .= $outvalUnits;
4683
4684 if (!empty($objp->idprodfournprice)) {
4685 $outqty = $objp->quantity;
4686 $outdiscount = $objp->remise_percent;
4687 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4688 $prod_supplier = new ProductFournisseur($this->db);
4689 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4690 $prod_supplier->id = $objp->fk_product;
4691 $prod_supplier->fourn_qty = $objp->quantity;
4692 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4693 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4694
4695 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4696 $priceparser = new PriceParser($this->db);
4697 $price_result = $priceparser->parseProductSupplier($prod_supplier);
4698 if ($price_result >= 0) {
4699 $objp->fprice = $price_result;
4700 if ($objp->quantity >= 1) {
4701 $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
4702 }
4703 }
4704 }
4705 if ($objp->quantity == 1) {
4706 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4707 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
4708 $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4709 $outvallabel .= $langs->transnoentities("Unit");
4710 } else {
4711 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4712 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4713 $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4714 $outvallabel .= ' ' . $langs->transnoentities("Units");
4715 }
4716
4717 if ($objp->quantity != 1) {
4718 $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
4719 $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
4720 }
4721 if ($objp->remise_percent >= 1) {
4722 $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4723 $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4724 }
4725 if ($objp->duration) {
4726 $optlabel .= " - " . $objp->duration;
4727 $outvallabel .= " - " . $objp->duration;
4728 }
4729 if (!$socid) {
4730 $optlabel .= " - " . dol_trunc($objp->name, 8);
4731 $outvallabel .= " - " . dol_trunc($objp->name, 8);
4732 }
4733 if ($objp->supplier_reputation) {
4734 //TODO dictionary
4735 $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
4736
4737 $optlabel .= " - " . $reputations[$objp->supplier_reputation];
4738 $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
4739 }
4740 } else {
4741 $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
4742 $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
4743 }
4744
4745 if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4746 $novirtualstock = ($showstockinlist == 2);
4747
4748 if ($user->hasRight('stock', 'lire')) {
4749 $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4750
4751 if ($objp->stock > 0) {
4752 $optlabel .= ' - <span class="product_line_stock_ok">';
4753 } elseif ($objp->stock <= 0) {
4754 $optlabel .= ' - <span class="product_line_stock_too_low">';
4755 }
4756 $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
4757 $optlabel .= '</span>';
4758 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4759 $langs->load("stocks");
4760
4761 $tmpproduct = new Product($this->db);
4762 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4763 $tmpproduct->load_virtual_stock();
4764 $virtualstock = $tmpproduct->stock_theorique;
4765
4766 $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4767
4768 $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4769 if ($virtualstock > 0) {
4770 $optlabel .= '<span class="product_line_stock_ok">';
4771 } elseif ($virtualstock <= 0) {
4772 $optlabel .= '<span class="product_line_stock_too_low">';
4773 }
4774 $optlabel .= $virtualstock;
4775 $optlabel .= '</span>';
4776
4777 unset($tmpproduct);
4778 }
4779 }
4780 }
4781
4782 $optstart = '<option value="' . $outkey . '"';
4783 if ($selected && preg_match('/^idprod_/', (string) $selected) && (string) $selected == 'idprod_'.$objp->rowid) {
4784 $optstart .= ' selected';
4785 } elseif ($selected && (string) $selected == (string) $objp->idprodfournprice) {
4786 $optstart .= ' selected';
4787 }
4788
4789 if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
4790 $optstart .= ' disabled';
4791 }
4792
4793 if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
4794 $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
4795 $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
4796 $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
4797 $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"'; // the price with numeric international format
4798 $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"'; // the price formatted in user language
4799 $optstart .= ' data-discount="' . dol_escape_htmltag((string) $outdiscount) . '"';
4800 $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"'; // the rate with numeric international format
4801 $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"'; // the rate formatted in user language
4802 $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
4803 $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
4804 if (isModEnabled('multicurrency')) {
4805 $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
4806 $optstart .= ' data-multicurrency-unitprice="' . dol_escape_htmltag(price2num($objp->multicurrency_unitprice)) . '"'; // the price with numeric international format
4807 }
4808 }
4809 $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
4810
4811 // set $parameters to call hook
4812 $outarrayentry = array(
4813 'key' => $outkey,
4814 'value' => $outref,
4815 'label' => $outvallabel,
4816 'labelhtml' => $optlabel,
4817 'qty' => $outqty,
4818 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4819 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4820 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4821 'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
4822 'tva_tx' => price2num($objp->tva_tx),
4823 'default_vat_code' => $objp->default_vat_code,
4824 'supplier_ref' => $objp->ref_fourn,
4825 'discount' => $outdiscount,
4826 'type' => $outtype,
4827 'duration_value' => $outdurationvalue,
4828 'duration_unit' => $outdurationunit,
4829 'disabled' => empty($objp->idprodfournprice),
4830 'description' => $objp->description
4831 );
4832 if (isModEnabled('multicurrency')) {
4833 $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4834 $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4835 }
4836 $parameters = array(
4837 'objp' => &$objp,
4838 'optstart' => &$optstart,
4839 'optlabel' => &$optlabel,
4840 'outvallabel' => &$outvallabel,
4841 'outarrayentry' => &$outarrayentry,
4842 'fk_soc' => $socid
4843 );
4844 $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4845
4846
4847 // Add new entry
4848 // "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
4849 // "label" value of json key array is used by jQuery automatically as text for combo box
4850 $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4851 $outarraypush = array(
4852 'key' => $outkey,
4853 'value' => $outref,
4854 'label' => $outvallabel,
4855 'labelhtml' => $optlabel,
4856 'qty' => $outqty,
4857 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4858 'price_qty_ht_locale' => price($objp->fprice),
4859 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4860 'price_unit_ht_locale' => price($objp->unitprice),
4861 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4862 'tva_tx_formated' => price($objp->tva_tx),
4863 'tva_tx' => price2num($objp->tva_tx),
4864 'default_vat_code' => $objp->default_vat_code,
4865 'supplier_ref' => $objp->ref_fourn,
4866 'discount' => $outdiscount,
4867 'type' => $outtype,
4868 'duration_value' => $outdurationvalue,
4869 'duration_unit' => $outdurationunit,
4870 'disabled' => empty($objp->idprodfournprice),
4871 'description' => $objp->description
4872 );
4873 if (isModEnabled('multicurrency')) {
4874 $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4875 $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4876 }
4877 array_push($outarray, $outarraypush);
4878
4879 // Example of var_dump $outarray
4880 // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4881 // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
4882 // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4883 //}
4884 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4885 //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
4886 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4887
4888 $i++;
4889 }
4890 $out .= '</select>';
4891
4892 $this->db->free($result);
4893
4894 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4895 $out .= ajax_combobox($htmlname);
4896 } else {
4897 dol_print_error($this->db);
4898 }
4899
4900 if (empty($outputmode)) {
4901 return $out;
4902 }
4903 return $outarray;
4904 }
4905
4906 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4907
4916 public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4917 {
4918 // phpcs:enable
4919 global $langs, $conf;
4920
4921 $langs->load('stocks');
4922
4923 $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4924 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4925 $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4926 $sql .= " FROM " . $this->db->prefix() . "product as p";
4927 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4928 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4929 $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4930 $sql .= " AND p.tobuy = 1";
4931 $sql .= " AND s.fournisseur = 1";
4932 $sql .= " AND p.rowid = " . ((int) $productid);
4933 if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4934 $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4935 } else {
4936 $sql .= " ORDER BY pfp.unitprice - pfp.unitprice * pfp.remise_percent / 100 ASC";
4937 }
4938
4939 dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4940 $result = $this->db->query($sql);
4941
4942 if ($result) {
4943 $num = $this->db->num_rows($result);
4944
4945 $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4946
4947 if (!$num) {
4948 $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4949 } else {
4950 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4951 $form .= '<option value="0">&nbsp;</option>';
4952
4953 $i = 0;
4954 while ($i < $num) {
4955 $objp = $this->db->fetch_object($result);
4956
4957 $opt = '<option value="' . $objp->idprodfournprice . '"';
4958 //if there is only one supplier, preselect it
4959 if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
4960 $opt .= ' selected';
4961 }
4962 $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
4963
4964 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4965 $prod_supplier = new ProductFournisseur($this->db);
4966 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4967 $prod_supplier->id = $productid;
4968 $prod_supplier->fourn_qty = $objp->quantity;
4969 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4970 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4971
4972 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4973 $priceparser = new PriceParser($this->db);
4974 $price_result = $priceparser->parseProductSupplier($prod_supplier);
4975 if ($price_result >= 0) {
4976 $objp->fprice = $price_result;
4977 if ($objp->quantity >= 1) {
4978 $objp->unitprice = $objp->fprice / $objp->quantity;
4979 }
4980 }
4981 }
4982 if ($objp->quantity == 1) {
4983 $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4984 }
4985
4986 $opt .= $objp->quantity . ' ';
4987
4988 if ($objp->quantity == 1) {
4989 $opt .= $langs->trans("Unit");
4990 } else {
4991 $opt .= $langs->trans("Units");
4992 }
4993 if ($objp->quantity > 1) {
4994 $opt .= " - ";
4995 $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");
4996 }
4997 if ($objp->duration) {
4998 $opt .= " - " . $objp->duration;
4999 }
5000 $opt .= "</option>\n";
5001
5002 $form .= $opt;
5003 $i++;
5004 }
5005 }
5006
5007 $form .= '</select>';
5008 $this->db->free($result);
5009 return $form;
5010 } else {
5011 dol_print_error($this->db);
5012 return '';
5013 }
5014 }
5015
5016
5017 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5024 {
5025 // phpcs:enable
5026 global $langs, $hookmanager;
5027
5028 $num = count($this->cache_conditions_paiements);
5029 if ($num > 0) {
5030 return 0; // Cache already loaded
5031 }
5032
5033 dol_syslog(__METHOD__, LOG_DEBUG);
5034
5035 $this->cache_conditions_paiements = array();
5036
5037 $sql = "SELECT rowid, code, libelle as label, deposit_percent, entity";
5038 $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
5039 $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
5040 $sql .= " AND active > 0";
5041 $sql .= " ORDER BY sortorder";
5042
5043 $resql = $this->db->query($sql);
5044 if ($resql) {
5045 $num = $this->db->num_rows($resql);
5046 $i = 0;
5047 while ($i < $num) {
5048 $obj = $this->db->fetch_object($resql);
5049
5050 // If a translation exists, we use it, otherwise, we take the label by default
5051 $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5052
5053 $this->cache_conditions_paiements[$obj->rowid]['code'] = (string) $obj->code;
5054 $this->cache_conditions_paiements[$obj->rowid]['label'] = (string) $label;
5055 $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = (string) $obj->deposit_percent;
5056 $this->cache_conditions_paiements[$obj->rowid]['entity'] = (int) $obj->entity;
5057
5058 $i++;
5059 }
5060
5061 $parameters = array('context' => 'paymentterm');
5062 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5063 if (empty($reshook)) {
5064 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5065 $this->cache_conditions_paiements = array_merge($this->cache_conditions_paiements, $hookmanager->resArray);
5066 }
5067 } else {
5068 $this->cache_conditions_paiements = $hookmanager->resArray;
5069 }
5070
5071 //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
5072
5073 return $num;
5074 } else {
5075 dol_print_error($this->db);
5076 return -1;
5077 }
5078 }
5079
5080 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5081
5088 {
5089 // phpcs:enable
5090 $factureRec = new FactureRec($this->db);
5091
5092 $this->cache_rule_for_lines_dates = $factureRec->fields['rule_for_lines_dates']['arrayofkeyval'];
5093
5094 if (empty($this->cache_rule_for_lines_dates)) {
5095 return -1;
5096 }
5097
5098 return 1;
5099 }
5100
5101 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5102
5108 public function load_cache_availability()
5109 {
5110 // phpcs:enable
5111 global $langs;
5112
5113 $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
5114 if ($num > 0) {
5115 return 0; // Cache already loaded
5116 }
5117
5118 dol_syslog(__METHOD__, LOG_DEBUG);
5119
5120 $this->cache_availability = array();
5121
5122 $langs->load('propal');
5123
5124 $sql = "SELECT rowid, code, label, position";
5125 $sql .= " FROM " . $this->db->prefix() . 'c_availability';
5126 $sql .= " WHERE active > 0";
5127
5128 $resql = $this->db->query($sql);
5129 if ($resql) {
5130 $num = $this->db->num_rows($resql);
5131 $i = 0;
5132 while ($i < $num) {
5133 $obj = $this->db->fetch_object($resql);
5134
5135 // If a translation exists, we use is, otherwise, we take the label by default
5136 $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5137 $this->cache_availability[$obj->rowid]['code'] = (string) $obj->code;
5138 $this->cache_availability[$obj->rowid]['label'] = (string) $label;
5139 $this->cache_availability[$obj->rowid]['position'] = (int) $obj->position;
5140 $i++;
5141 }
5142
5143 // @phan-suppress-next-line PhanTypeMismatchProperty PhanTypeMismatchDimFetch
5144 $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
5145
5146 return $num;
5147 } else {
5148 dol_print_error($this->db);
5149 return -1;
5150 }
5151 }
5152
5164 public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '', $noouput = 0)
5165 {
5166 global $langs, $user;
5167
5168 $this->load_cache_availability();
5169
5170 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5171
5172 $out = '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5173 if ($addempty) {
5174 $out .= '<option value="-1">'.(is_numeric($addempty) ? '&nbsp;' : $langs->trans($addempty)).'</option>';
5175 }
5176 foreach ($this->cache_availability as $id => $arrayavailability) {
5177 if ($selected == $id) {
5178 $out .= '<option value="' . $id . '" selected>';
5179 } else {
5180 $out .= '<option value="' . $id . '">';
5181 }
5182 $out .= dol_escape_htmltag($arrayavailability['label']);
5183 $out .= '</option>';
5184 }
5185 $out .= '</select>';
5186 if ($user->admin) {
5187 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5188 }
5189 $out .= ajax_combobox($htmlname);
5190
5191 if ($noouput) {
5192 return $out;
5193 } else {
5194 print $out;
5195 return '';
5196 }
5197 }
5198
5204 public function loadCacheInputReason()
5205 {
5206 global $langs;
5207
5208 $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
5209 if ($num > 0) {
5210 return 0; // Cache already loaded
5211 }
5212
5213 $sql = "SELECT rowid, code, label";
5214 $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
5215 $sql .= " WHERE active > 0";
5216
5217 $resql = $this->db->query($sql);
5218 if ($resql) {
5219 $num = $this->db->num_rows($resql);
5220 $i = 0;
5222 $tmparray = array();
5223 while ($i < $num) {
5224 $obj = $this->db->fetch_object($resql);
5225
5226 // If a translation exists, we use is, otherwise, we take the label by default
5227 $label = ($obj->label != '-' ? (string) $obj->label : '');
5228 if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
5229 $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
5230 }
5231 if ($langs->trans($obj->code) != $obj->code) {
5232 $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
5233 }
5234
5235 $tmparray[(int) $obj->rowid]
5236 = array(
5237 'id' => (int) $obj->rowid,
5238 'code' => (string) $obj->code,
5239 'label' => $label,
5240 );
5241 $i++;
5242 }
5243
5244 $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
5245
5246 unset($tmparray);
5247 return $num;
5248 } else {
5249 dol_print_error($this->db);
5250 return -1;
5251 }
5252 }
5253
5266 public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
5267 {
5268 global $langs, $user;
5269
5270 $this->loadCacheInputReason();
5271
5272 print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5273 if ($addempty) {
5274 print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
5275 }
5276 foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
5277 if ($arraydemandreason['code'] == $exclude) {
5278 continue;
5279 }
5280
5281 if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
5282 print '<option value="' . $arraydemandreason['id'] . '" selected>';
5283 } else {
5284 print '<option value="' . $arraydemandreason['id'] . '">';
5285 }
5286 $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
5287 print $langs->trans($label);
5288 print '</option>';
5289 }
5290 print '</select>';
5291 if ($user->admin && empty($notooltip)) {
5292 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5293 }
5294 print ajax_combobox('select_' . $htmlname);
5295 }
5296
5297 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5298
5305 {
5306 // phpcs:enable
5307 global $langs, $hookmanager;
5308
5309 $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
5310 if ($num > 0) {
5311 return $num; // Cache already loaded
5312 }
5313
5314 dol_syslog(__METHOD__, LOG_DEBUG);
5315
5316 $this->cache_types_paiements = array();
5317
5318 $sql = "SELECT id, code, libelle as label, type, entity, active";
5319 $sql .= " FROM " . $this->db->prefix() . "c_paiement";
5320 $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
5321
5322 $resql = $this->db->query($sql);
5323 if ($resql) {
5324 $num = $this->db->num_rows($resql);
5325 $i = 0;
5326 while ($i < $num) {
5327 $obj = $this->db->fetch_object($resql);
5328
5329 // If a translation exists, we use is, otherwise, we take the label by default
5330 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5331 $this->cache_types_paiements[$obj->id]['id'] = (int) $obj->id;
5332 $this->cache_types_paiements[$obj->id]['code'] = (string) $obj->code;
5333 $this->cache_types_paiements[$obj->id]['label'] = (string) $label;
5334 $this->cache_types_paiements[$obj->id]['type'] = (int) $obj->type;
5335 $this->cache_types_paiements[$obj->id]['entity'] = (int) $obj->entity;
5336 $this->cache_types_paiements[$obj->id]['active'] = (int) $obj->active;
5337 $i++;
5338 }
5339
5340 $parameters = array('context' => 'paymenttype');
5341 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5342 if (empty($reshook)) {
5343 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5344 $this->cache_types_paiements = array_merge($this->cache_types_paiements, $hookmanager->resArray);
5345 }
5346 } else {
5347 $this->cache_types_paiements = $hookmanager->resArray;
5348 }
5349
5350 $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
5351
5352 return $num;
5353 } else {
5354 dol_print_error($this->db);
5355 return -1;
5356 }
5357 }
5358
5359
5360 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5361
5380 public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
5381 {
5382 // phpcs:enable
5383 $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
5384 if (empty($noprint)) {
5385 print $out;
5386 } else {
5387 return $out;
5388 }
5389 }
5390
5391
5408 public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
5409 {
5410 global $langs, $user;
5411
5412 $out = '';
5413 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5414
5416
5417 // Set default value if not already set by caller
5418 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
5419 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
5420 $selected = getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID');
5421 }
5422
5423 $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5424 if ($addempty) {
5425 $out .= '<option value="0">&nbsp;</option>';
5426 }
5427
5428 $selectedDepositPercent = null;
5429
5430 foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
5431 if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
5432 continue;
5433 }
5434
5435 if ($selected == $id) {
5436 $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
5437 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
5438 } else {
5439 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
5440 }
5441 $label = $arrayconditions['label'];
5442
5443 if (!empty($arrayconditions['deposit_percent'])) {
5444 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
5445 }
5446
5447 $out .= $label;
5448 $out .= '</option>';
5449 }
5450 $out .= '</select>';
5451 if ($user->admin && empty($noinfoadmin)) {
5452 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5453 }
5454 $out .= ajax_combobox($htmlname);
5455
5456 if ($deposit_percent >= 0) {
5457 $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
5458 $out .= $langs->trans('DepositPercent') . ' : ';
5459 $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
5460 $out .= '</span>';
5461 $out .= '
5462 <script nonce="' . getNonce() . '">
5463 $(document).ready(function () {
5464 $("#' . $htmlname . '").change(function () {
5465 let $selected = $(this).find("option:selected");
5466 let depositPercent = $selected.attr("data-deposit_percent");
5467
5468 if (depositPercent.length > 0) {
5469 $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
5470 } else {
5471 $("#' . $htmlname . '_deposit_percent_container").hide();
5472 }
5473
5474 return true;
5475 });
5476 });
5477 </script>';
5478 }
5479
5480 return $out;
5481 }
5482
5483
5492 public function getSelectRuleForLinesDates($selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0)
5493 {
5494 global $langs;
5495
5496 $out = '';
5497
5499
5500 $out .= '<select id="' . $htmlname . '" class="flat selectbillingterm" name="' . $htmlname . '">';
5501 if ($addempty) {
5502 $out .= '<option value="-1">&nbsp;</option>';
5503 }
5504
5505
5506 foreach ($this->cache_rule_for_lines_dates as $rule_for_lines_dates_key => $rule_for_lines_dates_name) {
5507 if ($selected == $rule_for_lines_dates_key) {
5508 $out .= '<option value="' . $rule_for_lines_dates_key . '" selected>';
5509 } else {
5510 $out .= '<option value="' . $rule_for_lines_dates_key . '">';
5511 }
5512
5513 $out .= $langs->trans($rule_for_lines_dates_name);
5514 $out .= '</option>';
5515 }
5516 $out .= '</select>';
5517
5518 $out .= ajax_combobox($htmlname);
5519
5520 return $out;
5521 }
5522
5523
5524 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5525
5542 public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
5543 {
5544 // phpcs:enable
5545 global $langs, $user;
5546
5547 $out = '';
5548
5549 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
5550
5551 $filterarray = array();
5552 if ($filtertype == 'CRDT') {
5553 $filterarray = array(0, 2, 3);
5554 } elseif ($filtertype == 'DBIT') {
5555 $filterarray = array(1, 2, 3);
5556 } elseif ($filtertype != '' && $filtertype != '-1') {
5557 $filterarray = explode(',', $filtertype);
5558 }
5559
5561
5562 // Set default value if not already set by caller
5563 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
5564 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
5565 $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
5566 }
5567
5568 $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5569 if ($empty) {
5570 $out .= '<option value="">&nbsp;</option>';
5571 }
5572 foreach ($this->cache_types_paiements as $id => $arraytypes) {
5573 // If not good status
5574 if ($active >= 0 && $arraytypes['active'] != $active) {
5575 continue;
5576 }
5577
5578 // We skip of the user requested to filter on specific payment methods
5579 if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
5580 continue;
5581 }
5582
5583 // We discard empty lines if showempty is on because an empty line has already been output.
5584 if ($empty && empty($arraytypes['code'])) {
5585 continue;
5586 }
5587
5588 if ($format == 0) {
5589 $out .= '<option value="' . $id . '" data-code="'.$arraytypes['code'].'"';
5590 } elseif ($format == 1) {
5591 $out .= '<option value="' . $arraytypes['code'] . '"';
5592 } elseif ($format == 2) {
5593 $out .= '<option value="' . $arraytypes['code'] . '"';
5594 } elseif ($format == 3) {
5595 $out .= '<option value="' . $id . '"';
5596 }
5597 // Print attribute selected or not
5598 if ($format == 1 || $format == 2) {
5599 if ($selected == $arraytypes['code']) {
5600 $out .= ' selected';
5601 }
5602 } else {
5603 if ($selected == $id) {
5604 $out .= ' selected';
5605 }
5606 }
5607 $out .= '>';
5608 $value = '';
5609 if ($format == 0) {
5610 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5611 } elseif ($format == 1) {
5612 $value = $arraytypes['code'];
5613 } elseif ($format == 2) {
5614 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5615 } elseif ($format == 3) {
5616 $value = $arraytypes['code'];
5617 }
5618 $out .= $value ? $value : '&nbsp;';
5619 $out .= '</option>';
5620 }
5621 $out .= '</select>';
5622 if ($user->admin && !$noadmininfo) {
5623 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5624 }
5625 $out .= ajax_combobox('select' . $htmlname);
5626
5627 if (empty($nooutput)) {
5628 print $out;
5629 } else {
5630 return $out;
5631 }
5632 }
5633
5634
5643 public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
5644 {
5645 global $langs;
5646
5647 $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5648 $options = array(
5649 'HT' => $langs->trans("HT"),
5650 'TTC' => $langs->trans("TTC")
5651 );
5652 foreach ($options as $id => $value) {
5653 if ($selected == $id) {
5654 $return .= '<option value="' . $id . '" selected>' . $value;
5655 } else {
5656 $return .= '<option value="' . $id . '">' . $value;
5657 }
5658 $return .= '</option>';
5659 }
5660 $return .= '</select>';
5661 if ($addjscombo) {
5662 $return .= ajax_combobox('select_' . $htmlname);
5663 }
5664
5665 return $return;
5666 }
5667
5668 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5669
5676 {
5677 // phpcs:enable
5678 global $langs;
5679
5680 $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
5681 if ($num > 0) {
5682 return $num; // Cache already loaded
5683 }
5684
5685 dol_syslog(__METHOD__, LOG_DEBUG);
5686
5687 $this->cache_transport_mode = array();
5688
5689 $sql = "SELECT rowid, code, label, active";
5690 $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
5691 $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
5692
5693 $resql = $this->db->query($sql);
5694 if ($resql) {
5695 $num = $this->db->num_rows($resql);
5696 $i = 0;
5697 while ($i < $num) {
5698 $obj = $this->db->fetch_object($resql);
5699
5700 // If traduction exist, we use it else we take the default label
5701 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5702 $this->cache_transport_mode[(int) $obj->rowid]
5703 = array(
5704 'rowid' => (int) $obj->rowid,
5705 'code' => (string) $obj->code,
5706 'label' => (string) $label,
5707 'active' => (int) $obj->active,
5708 );
5709 $i++;
5710 }
5711
5712 $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
5713
5714 return $num;
5715 } else {
5716 dol_print_error($this->db);
5717 return -1;
5718 }
5719 }
5720
5734 public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
5735 {
5736 global $langs, $user;
5737
5738 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
5739
5741
5742 print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5743 if ($empty) {
5744 print '<option value="">&nbsp;</option>';
5745 }
5746 foreach ($this->cache_transport_mode as $id => $arraytypes) {
5747 // If not good status
5748 if ($active >= 0 && $arraytypes['active'] != $active) {
5749 continue;
5750 }
5751
5752 // We discard empty line if showempty is on because an empty line has already been output.
5753 if ($empty && empty($arraytypes['code'])) {
5754 continue;
5755 }
5756
5757 if ($format == 0) {
5758 print '<option value="' . $id . '"';
5759 } elseif ($format == 1) {
5760 print '<option value="' . $arraytypes['code'] . '"';
5761 } elseif ($format == 2) {
5762 print '<option value="' . $arraytypes['code'] . '"';
5763 } elseif ($format == 3) {
5764 print '<option value="' . $id . '"';
5765 }
5766 // If text is selected, we compare with code, else with id
5767 if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
5768 print ' selected';
5769 } elseif ($selected == $id) {
5770 print ' selected';
5771 }
5772 print '>';
5773 $value = '';
5774 if ($format == 0) {
5775 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5776 } elseif ($format == 1) {
5777 $value = $arraytypes['code'];
5778 } elseif ($format == 2) {
5779 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5780 } elseif ($format == 3) {
5781 $value = $arraytypes['code'];
5782 }
5783 print $value ? $value : '&nbsp;';
5784 print '</option>';
5785 }
5786 print '</select>';
5787
5788 print ajax_combobox("select".$htmlname);
5789
5790 if ($user->admin && !$noadmininfo) {
5791 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5792 }
5793 }
5794
5807 public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
5808 {
5809 global $langs, $user;
5810
5811 $langs->loadLangs(array("admin", "sendings"));
5812
5813 $sql = "SELECT rowid, code, libelle as label";
5814 $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
5815 $sql .= " WHERE active > 0";
5816 if ($filtre) {
5817 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
5818 }
5819 $sql .= " ORDER BY libelle ASC";
5820
5821 dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
5822
5823 $result = $this->db->query($sql);
5824 if ($result) {
5825 $num = $this->db->num_rows($result);
5826 $i = 0;
5827 if ($num) {
5828 print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5829 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5830 print '<option value="-1">&nbsp;</option>';
5831 }
5832 while ($i < $num) {
5833 $obj = $this->db->fetch_object($result);
5834 if ($selected == $obj->rowid) {
5835 print '<option value="' . $obj->rowid . '" selected>';
5836 } else {
5837 print '<option value="' . $obj->rowid . '">';
5838 }
5839 print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
5840 print '</option>';
5841 $i++;
5842 }
5843 print "</select>";
5844 if ($user->admin && empty($noinfoadmin)) {
5845 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5846 }
5847
5848 print ajax_combobox('select' . $htmlname);
5849 } else {
5850 print $langs->trans("NoShippingMethodDefined");
5851 }
5852 } else {
5853 dol_print_error($this->db);
5854 }
5855 }
5856
5866 public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
5867 {
5868 global $langs;
5869
5870 $langs->load("sendings");
5871
5872 if ($htmlname != "none") {
5873 print '<form method="POST" action="' . $page . '">';
5874 print '<input type="hidden" name="action" value="setshippingmethod">';
5875 print '<input type="hidden" name="token" value="' . newToken() . '">';
5876 $this->selectShippingMethod($selected, $htmlname, '', $addempty);
5877 print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
5878 print '</form>';
5879 } else {
5880 if ($selected) {
5881 $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
5882 print $langs->trans("SendingMethod" . strtoupper($code));
5883 } else {
5884 print "&nbsp;";
5885 }
5886 }
5887 }
5888
5897 public function selectSituationInvoices($selected = '', $socid = 0)
5898 {
5899 global $langs;
5900
5901 $langs->load('bills');
5902
5903 $opt = '';
5904
5905 $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
5906 $sql .= ' FROM ' . $this->db->prefix() . 'facture';
5907 $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
5908 $sql .= ' AND situation_counter >= 1';
5909 $sql .= ' AND fk_soc = ' . (int) $socid;
5910 $sql .= ' AND type <> 2';
5911 $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
5912 $resql = $this->db->query($sql);
5913
5914 $nbSituationInvoiceForThirdparty = 0;
5915
5916 if ($resql && $this->db->num_rows($resql) > 0) {
5917 // Last seen cycle
5918 $ref = 0;
5919 while ($obj = $this->db->fetch_object($resql)) {
5920 //Same cycle ?
5921 if ($obj->situation_cycle_ref != $ref) {
5922 // Just seen this cycle
5923 $ref = $obj->situation_cycle_ref;
5924 //not final ?
5925 if ($obj->situation_final != 1) {
5926 //Not prov?
5927 if (substr($obj->ref, 1, 4) != 'PROV') {
5928 $nbSituationInvoiceForThirdparty++;
5929
5930 if ($selected == $obj->rowid) {
5931 $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
5932 } else {
5933 $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
5934 }
5935 }
5936 }
5937 }
5938 }
5939 } else {
5940 dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
5941 }
5942
5943 if ($nbSituationInvoiceForThirdparty > 0) {
5944 $opt = '<option class="minwidth100" value="" selected>&nbsp;</option>'.$opt;
5945 } else {
5946 $opt = '<option class="minwidth100" value="-1" selected>'.$langs->trans('NoSituations').'</option>';
5947 }
5948
5949 return $opt;
5950 }
5951
5961 public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
5962 {
5963 global $langs;
5964
5965 $langs->load('products');
5966
5967 $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
5968
5969 $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
5970 $sql .= ' WHERE active > 0';
5971 if (!empty($unit_type)) {
5972 $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
5973 }
5974 $sql .= " ORDER BY sortorder";
5975
5976 $resql = $this->db->query($sql);
5977 if ($resql && $this->db->num_rows($resql) > 0) {
5978 if ($showempty) {
5979 $return .= '<option value="-1"></option>';
5980 }
5981
5982 while ($res = $this->db->fetch_object($resql)) {
5983 $unitLabel = $res->label;
5984 if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
5985 $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
5986 }
5987
5988 if ($selected == $res->rowid) {
5989 $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
5990 } else {
5991 $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
5992 }
5993 }
5994 $return .= '</select>';
5995
5996 $return .= ajax_combobox($htmlname);
5997 }
5998 return $return;
5999 }
6000
6001 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6002
6018 public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0, $addentrynone = 0)
6019 {
6020 // phpcs:enable
6021 global $langs;
6022
6023 $out = '';
6024
6025 $langs->loadLangs(array("admin", "banks"));
6026 $num = 0;
6027
6028 $sql = "SELECT rowid, label, bank, clos as status, currency_code";
6029 $sql .= " FROM " . $this->db->prefix() . "bank_account";
6030 $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
6031 if ($status != 2) {
6032 $sql .= " AND clos = " . (int) $status;
6033 }
6034 if ($filtre) {
6035 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6036 }
6037 $sql .= " ORDER BY label";
6038
6039 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6040 $result = $this->db->query($sql);
6041 if ($result) {
6042 $num = $this->db->num_rows($result);
6043 $i = 0;
6044
6045 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6046
6047 if ($num == 0) {
6048 if ($status == 0) {
6049 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
6050 } else {
6051 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6052 }
6053 } else {
6054 if (!empty($useempty) && !is_numeric($useempty)) {
6055 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6056 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6057 $out .= '<option value="-1">&nbsp;</option>';
6058 }
6059 }
6060
6061 while ($i < $num) {
6062 $obj = $this->db->fetch_object($result);
6063
6064 $labeltoshow = trim($obj->label);
6065 $labeltoshowhtml = trim($obj->label);
6066 if ($showcurrency) {
6067 $labeltoshow .= ' (' . $obj->currency_code . ')';
6068 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $obj->currency_code . ')</span>';
6069 }
6070 if ($status == 2 && $obj->status == 1) {
6071 $labeltoshow .= ' (' . $langs->trans("Closed") . ')';
6072 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $langs->trans("Closed") . ')</span>';
6073 }
6074
6075 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6076 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'" selected>';
6077 } else {
6078 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'">';
6079 }
6080 $out .= $labeltoshow;
6081 $out .= '</option>';
6082 $i++;
6083 }
6084
6085 if (!empty($addentrynone)) {
6086 $out .= '<option value="-2"'.($selected == -2 ? ' selected="selected"' : '').' data-html="'.dolPrintHTMLForAttribute('<span class="opacitymedium">'.$langs->trans("None").'</span>').'">'.$langs->trans("None").'</option>';
6087 }
6088
6089 $out .= "</select>";
6090 $out .= ajax_combobox('select' . $htmlname);
6091 } else {
6092 dol_print_error($this->db);
6093 }
6094
6095 // Output or return
6096 if (empty($nooutput)) {
6097 print $out;
6098 } else {
6099 return $out;
6100 }
6101
6102 return $num;
6103 }
6104
6118 public function selectRib($selected = '', $htmlname = 'ribcompanyid', $filtre = '', $useempty = 0, $moreattrib = '', $showibanbic = 0, $morecss = '', $nooutput = 0)
6119 {
6120 // phpcs:enable
6121 global $langs;
6122
6123 $out = '';
6124
6125 $langs->loadLangs(array("admin", "banks"));
6126 $num = 0;
6127
6128 $sql = "SELECT rowid, label, bank, status, iban_prefix, bic, default_rib";
6129 $sql .= " FROM " . $this->db->prefix() . "societe_rib";
6130 $sql .= " WHERE type = 'ban'";
6131 if ($filtre) {
6132 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6133 }
6134 $sql .= " ORDER BY label";
6135 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6136 $result = $this->db->query($sql);
6137 if ($result) {
6138 $num = $this->db->num_rows($result);
6139 $i = 0;
6140
6141 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6142
6143 if ($num == 0) {
6144 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6145 } else {
6146 if (!empty($useempty) && !is_numeric($useempty)) {
6147 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6148 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6149 $out .= '<option value="-1">&nbsp;</option>';
6150 }
6151 }
6152
6153 while ($i < $num) {
6154 $obj = $this->db->fetch_object($result);
6155 $iban = dolDecrypt($obj->iban_prefix);
6156 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6157 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '" selected>';
6158 } else {
6159 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '">';
6160 }
6161 $out .= trim($obj->label);
6162 if ($showibanbic) {
6163 $out .= ' (' . $iban . '/' .$obj->bic. ')' . ($obj->default_rib ? ' ['.$langs->trans("ByDefault").']' : '');
6164 }
6165 $out .= '</option>';
6166 $i++;
6167 }
6168 $out .= "</select>";
6169 $out .= ajax_combobox('select' . $htmlname);
6170 } else {
6171 dol_print_error($this->db);
6172 }
6173
6174 // Output or return
6175 if (empty($nooutput)) {
6176 print $out;
6177 } else {
6178 return $out;
6179 }
6180
6181 return $num;
6182 }
6183
6195 public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
6196 {
6197 global $langs;
6198
6199 $langs->load("admin");
6200 $num = 0;
6201
6202 $sql = "SELECT rowid, name, fk_country, status, entity";
6203 $sql .= " FROM " . $this->db->prefix() . "establishment";
6204 $sql .= " WHERE 1=1";
6205 if ($status != 2) {
6206 $sql .= " AND status = " . (int) $status;
6207 }
6208 if ($filtre) {
6209 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6210 }
6211 $sql .= " ORDER BY name";
6212
6213 dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
6214 $result = $this->db->query($sql);
6215 if ($result) {
6216 $num = $this->db->num_rows($result);
6217 $i = 0;
6218 if ($num) {
6219 print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6220 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6221 print '<option value="-1">&nbsp;</option>';
6222 }
6223
6224 while ($i < $num) {
6225 $obj = $this->db->fetch_object($result);
6226 if ($selected == $obj->rowid) {
6227 print '<option value="' . $obj->rowid . '" selected>';
6228 } else {
6229 print '<option value="' . $obj->rowid . '">';
6230 }
6231 print trim($obj->name);
6232 if ($status == 2 && $obj->status == 1) {
6233 print ' (' . $langs->trans("Closed") . ')';
6234 }
6235 print '</option>';
6236 $i++;
6237 }
6238 print "</select>";
6239 } else {
6240 if ($status == 0) {
6241 print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
6242 } else {
6243 print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
6244 }
6245 }
6246
6247 return $num;
6248 } else {
6249 dol_print_error($this->db);
6250 return -1;
6251 }
6252 }
6253
6263 public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
6264 {
6265 global $langs;
6266 if ($htmlname != "none") {
6267 print '<form method="POST" action="' . $page . '">';
6268 print '<input type="hidden" name="action" value="setbankaccount">';
6269 print '<input type="hidden" name="token" value="' . newToken() . '">';
6270 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
6271 $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
6272 if ($nbaccountfound > 0) {
6273 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6274 }
6275 print '</form>';
6276 } else {
6277 $langs->load('banks');
6278
6279 if ($selected) {
6280 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
6281 $bankstatic = new Account($this->db);
6282 $result = $bankstatic->fetch((int) $selected);
6283 if ($result) {
6284 print $bankstatic->getNomUrl(1);
6285 }
6286 } else {
6287 print "&nbsp;";
6288 }
6289 }
6290 }
6291
6303 public function formRib($page, $selected = '', $htmlname = 'ribcompanyid', $filtre = '', $addempty = 0, $showibanbic = 0)
6304 {
6305 global $langs;
6306 if ($htmlname != "none") {
6307 print '<form method="POST" action="' . $page . '">';
6308 print '<input type="hidden" name="action" value="setbankaccountcustomer">';
6309 print '<input type="hidden" name="token" value="' . newToken() . '">';
6310 $nbaccountfound = $this->selectRib($selected, $htmlname, $filtre, $addempty, '', $showibanbic);
6311 if ($nbaccountfound > 0) {
6312 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6313 }
6314 print '</form>';
6315 } else {
6316 $langs->load('banks');
6317
6318 if ($selected) {
6319 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
6320 $bankstatic = new CompanyBankAccount($this->db);
6321 $result = $bankstatic->fetch((int) $selected);
6322 if ($result) {
6323 print $bankstatic->label;
6324 if ($showibanbic) {
6325 print ' (' . $bankstatic->iban . '/' .$bankstatic->bic. ')';
6326 }
6327 }
6328 } else {
6329 print "&nbsp;";
6330 }
6331 }
6332 }
6333
6343 public function selectCategories($categtype, $htmlname, $object = null)
6344 {
6345 global $langs;
6346
6347 $out = '';
6348
6349 $cate_arbo = $this->select_all_categories($categtype, '', '', 64, 0, 3);
6350
6351 $arrayselected = array();
6352 if (GETPOSTISARRAY($htmlname)) {
6353 $arrayselected = GETPOST($htmlname, 'array:int');
6354 } elseif (is_object($object) && $object->id > 0) {
6355 $c = new Categorie($this->db);
6356 $cats = $c->containing($object->id, $categtype);
6357 $arrayselected = array();
6358 foreach ($cats as $cat) {
6359 $arrayselected[] = $cat->id;
6360 }
6361 }
6362
6363 $out .= img_picto('', 'category', 'class="pictofixedwidth"');
6364 $out .= $this->multiselectarray($htmlname, $cate_arbo, $arrayselected, 0, 0, 'minwidth100 widthcentpercentminusxx', 0, 0);
6365
6366 if (!getDolGlobalString('CATEGORY_EDIT_IN_MENU_NOT_IN_POPUP')) {
6367 // Add html code to add the edit button and go back
6368 $jsonclose = 'doJsCodeAfterPopupClose'.$htmlname.'()';
6369 $urltoopen = '/categories/categorie_list.php?type='.urlencode($categtype).'&nosearch=1';
6370
6371 $s = dolButtonToOpenUrlInDialogPopup($htmlname, $langs->transnoentitiesnoconv("Categories"), img_picto('', 'add', 'class="editfielda"'), $urltoopen, '', '', '', $jsonclose);
6372 $out .= $s;
6373 // Add js code to add the edit button and go back
6374 $out .= '<!-- Add js code to open the popup for category/edit/add -->'."\n";
6375 $out .= '<script>function doJsCodeAfterPopupClose'.$htmlname.'() {
6376 console.log("doJsCodeAfterPopupClose'.$htmlname.' has been called, we refresh the combo content + refresh select2...");
6377
6378 // Call an ajax to reload values and update the select
6379 // $("#'.dol_escape_js($htmlname).'").append(new Option("Option 4", "4"));
6380
6381 // Refresh select2 to take account of new values (enough for small change)
6382
6383 $.ajax({
6384 url: \''.DOL_URL_ROOT.'/core/ajax/fetchCategories.php\',
6385 data: {
6386 action: \'getCategories\',
6387 type: \''.dol_escape_htmltag($categtype).'\'
6388 },
6389 type: \'GET\',
6390 dataType: \'json\',
6391 success: function (data) {
6392 var $select = $(\'#'.dol_escape_js($htmlname).'\');
6393 var selectedValues = $select.val(); // This is an array of selected values
6394 console.log(selectedValues);
6395 $select.empty();
6396 $.each(data, function (index, item) {
6397 $select.append(\'<option value="\' + item.id + \'" data-html="\' + item.htmlforattribute + \'">\' + item.htmlforoption + \'</option>\');
6398 });
6399 $select.val(selectedValues);
6400 },
6401 error: function (xhr, status, error) {
6402 console.log("Error when loading ajax page : " + error);
6403 }
6404 });
6405
6406 $("#'.dol_escape_js($htmlname).'").trigger("change");
6407 // Alternative if change in select is complex
6408 /*
6409 $("#'.dol_escape_js($htmlname).'").select2("destroy");
6410 $("#'.dol_escape_js($htmlname).'").select2();
6411 */
6412 }</script>';
6413 }
6414
6415 return $out;
6416 }
6417
6418
6419 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6439 public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
6440 {
6441 // phpcs:enable
6442 global $langs;
6443
6444 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
6445
6446 $cat = new Categorie($this->db);
6447
6448 if (is_numeric($type)) {
6449 $type = array_search($type, $cat->MAP_ID); // For backward compatibility
6450 }
6451
6452 $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
6453
6454 $outarray = array();
6455 $outarrayrichhtml = array();
6456
6457
6458 $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6459 $num = 0;
6460 if (is_array($cate_arbo)) {
6461 $num = count($cate_arbo);
6462
6463 if (!$num) {
6464 $langs->load("categories");
6465 $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
6466 } else {
6467 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6468 $output .= '<option value="-1">&nbsp;</option>';
6469 }
6470 foreach ($cate_arbo as $key => $value) {
6471 if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
6472 $add = 'selected ';
6473 } else {
6474 $add = '';
6475 }
6476
6477 $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth"'.(empty($cate_arbo[$key]['color']) ? '' : ' style="color: #' . $cate_arbo[$key]['color'] . '"'));
6478 $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
6479
6480 $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
6481
6482 $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
6483
6484 $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
6485 $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
6486 $output .= '>';
6487 // The visible (truncated) label is rendered via data-html in
6488 // templateResult of the select2 combobox; the bare option text
6489 // must keep the full label so that the select2 search matcher
6490 // (ajax_combobox in core/lib/ajax.lib.php) can find a hit on
6491 // characters that lie outside the truncated portion.
6492 $output .= dol_escape_htmltag($cate_arbo[$key]['fulllabel']);
6493 $output .= '</option>';
6494
6495 $cate_arbo[$key]['data-html'] = $labeltoshow;
6496 }
6497 }
6498 }
6499 $output .= '</select>';
6500 $output .= "\n";
6501
6502 $this->num = $num;
6503
6504 if ($outputmode == 2) {
6505 // TODO: handle error when $cate_arbo is not an array
6506 return $cate_arbo;
6507 } elseif ($outputmode == 1) {
6508 return $outarray;
6509 } elseif ($outputmode == 3) {
6510 return $outarrayrichhtml;
6511 }
6512 return $output;
6513 }
6514
6523 public function getHelpBlock($content, $icon = 'fa-question-circle')
6524 {
6525 global $langs;
6526
6527 // Sanitize content (assuming it might contain HTML, but escaping text nodes if needed)
6528 // We trust the caller to pass safe HTML or translated strings.
6529
6530 $html = '<details class="dolibarr-help-block" style="margin-top:8px;">';
6531 $html .= '<summary style="cursor:pointer; color:#0056b3; font-weight:normal; list-style:none; font-size:0.9em; display:flex; align-items:center;">';
6532 $html .= '<span class="fa ' . $icon . '" style="margin-right:6px;"></span>';
6533 $html .= $langs->trans("Help"); // Standardized title
6534 $html .= '</summary>';
6535 $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;">';
6536 $html .= $content;
6537 $html .= '</div>';
6538 $html .= '</details>';
6539
6540 return $html;
6541 }
6542
6543 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6544
6563 public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
6564 {
6565 // phpcs:enable
6566 dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
6567 print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
6568 }
6569
6599 public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 600, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No', $helpContent = '')
6600 {
6601 global $langs, $conf;
6602
6603 $more = '';
6604 $formconfirm = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
6605
6606 $inputok = array();
6607 $inputko = array();
6608
6609 // Clean parameters
6610 $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
6611 if ($conf->browser->layout == 'phone') {
6612 $width = '95%';
6613 }
6614
6615 // Set height automatically if not defined
6616 if (empty($height)) {
6617 $height = 185;
6618 if (is_array($formquestion)) {
6619 $height += (count($formquestion) * 40);
6620 }
6621 if ($question) {
6622 $height += dol_nboflines_bis($question, 80) * 40;
6623 }
6624 }
6625
6626 if (is_array($formquestion) && !empty($formquestion)) {
6627 // First add hidden fields and value
6628 foreach ($formquestion as $key => $input) {
6629 if (is_array($input) && !empty($input)) {
6630 if ($input['type'] == 'hidden') {
6631 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6632 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6633
6634 $more .= '<input type="hidden" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . dol_escape_htmltag($input['value']) . '" class="' . $morecss . '"' . $moreattr . '>' . "\n";
6635 }
6636 }
6637 }
6638
6639 // Now add questions
6640 $moreonecolumn = '';
6641 $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
6642 foreach ($formquestion as $key => $input) {
6643 if (is_array($input) && !empty($input)) {
6644 $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
6645 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6646 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6647
6648 if ($input['type'] == 'text' || $input['type'] == 'input') { // traditional input
6649 $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";
6650 } elseif ($input['type'] == 'password') {
6651 $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";
6652 } elseif ($input['type'] == 'textarea') {
6653 $moreonecolumn .= '<div class="margintoponly">';
6654 $moreonecolumn .= $input['label'] . '<br>';
6655 $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
6656 $moreonecolumn .= $input['value'];
6657 $moreonecolumn .= '</textarea>';
6658 $moreonecolumn .= '</div>';
6659 } elseif (in_array($input['type'], ['select', 'multiselect'])) {
6660 if (empty($morecss)) {
6661 $morecss = 'minwidth100';
6662 }
6663
6664 $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
6665 $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
6666 $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
6667 $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
6668 $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
6669 $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
6670 $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
6671
6672 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
6673 if (!empty($input['label'])) {
6674 $more .= $input['label'] . '</div><div class="tagtd left">';
6675 }
6676 if ($input['type'] == 'select') {
6677 $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);
6678 } else {
6679 $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);
6680 }
6681 $more .= '</div></div>' . "\n";
6682 } elseif ($input['type'] == 'checkbox') {
6683 $more .= '<div class="tagtr">';
6684 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
6685 $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
6686 if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
6687 $more .= ' checked';
6688 }
6689 if (is_bool($input['value']) && $input['value']) {
6690 $more .= ' checked';
6691 }
6692 if (isset($input['disabled'])) {
6693 $more .= ' disabled';
6694 }
6695 $more .= ' /></div>';
6696 $more .= '</div>' . "\n";
6697 } elseif ($input['type'] == 'radio') {
6698 $i = 0;
6699 foreach ($input['values'] as $selkey => $selval) {
6700 $more .= '<div class="tagtr">';
6701 if (isset($input['label'])) {
6702 if ($i == 0) {
6703 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6704 } else {
6705 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
6706 }
6707 }
6708 $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;
6709 if (!empty($input['disabled'])) {
6710 $more .= ' disabled';
6711 }
6712 if (isset($input['default']) && $input['default'] === $selkey) {
6713 $more .= ' checked="checked"';
6714 }
6715 $more .= ' /> ';
6716 $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
6717 $more .= '</div></div>' . "\n";
6718 $i++;
6719 }
6720 } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
6721 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6722 $more .= '<div class="tagtd">';
6723 $addnowlink = (empty($input['datenow']) ? 0 : 1);
6724 $h = $m = 0;
6725 if ($input['type'] == 'datetime') {
6726 $h = isset($input['hours']) ? $input['hours'] : 1;
6727 $m = isset($input['minutes']) ? $input['minutes'] : 1;
6728 }
6729 $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
6730 $more .= '</div></div>'."\n";
6731 $formquestion[] = array('name' => $input['name'].'day');
6732 $formquestion[] = array('name' => $input['name'].'month');
6733 $formquestion[] = array('name' => $input['name'].'year');
6734 $formquestion[] = array('name' => $input['name'].'hour');
6735 $formquestion[] = array('name' => $input['name'].'min');
6736 } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
6737 $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
6738 if (!empty($input['label'])) {
6739 $more .= $input['label'] . '</div><div class="tagtd">';
6740 }
6741 if (!empty($input['value'])) {
6742 $more .= $input['value'];
6743 }
6744 $more .= '</div></div>' . "\n";
6745 } elseif ($input['type'] == 'onecolumn') {
6746 $moreonecolumn .= '<div class="margintoponly">';
6747 $moreonecolumn .= $input['value'];
6748 $moreonecolumn .= '</div>' . "\n";
6749 } elseif ($input['type'] == 'hidden') {
6750 // Do nothing more, already added by a previous loop
6751 } elseif ($input['type'] == 'separator') {
6752 $more .= '<br>';
6753 } else {
6754 $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
6755 }
6756 }
6757 }
6758 $more .= '</div>' . "\n";
6759 $more .= $moreonecolumn;
6760 }
6761
6762 // JQUERY method dialog is broken with smartphone, we use standard HTML.
6763 // 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
6764 // See page product/card.php for example
6765 if (!empty($conf->dol_use_jmobile)) {
6766 $useajax = 0;
6767 }
6768 if (empty($conf->use_javascript_ajax)) {
6769 $useajax = 0;
6770 }
6771
6772 if ($useajax) {
6773 $autoOpen = true;
6774 $dialogconfirm = 'dialog-confirm';
6775 $button = '';
6776 if (!is_numeric($useajax)) {
6777 $button = $useajax;
6778 $useajax = 1;
6779 $autoOpen = false;
6780 $dialogconfirm .= '-' . $button;
6781 }
6782 $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
6783 $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
6784
6785 // Add input fields into list of fields to read during submit (inputok and inputko)
6786 if (is_array($formquestion)) {
6787 foreach ($formquestion as $key => $input) {
6788 //print "xx ".$key." rr ".is_array($input)."<br>\n";
6789 // Add name of fields to propagate with the GET when submitting the form with button OK.
6790 if (is_array($input) && isset($input['name'])) {
6791 if (strpos($input['name'], ',') > 0) {
6792 $inputok = array_merge($inputok, explode(',', $input['name']));
6793 } else {
6794 array_push($inputok, $input['name']);
6795 }
6796 }
6797 // Add name of fields to propagate with the GET when submitting the form with button KO.
6798 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
6799 if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
6800 array_push($inputko, $input['name']);
6801 }
6802 }
6803 }
6804
6805 // Show JQuery confirm box.
6806 // Add 'flex-direction: column' and 'justify-content: space-between' to push content to top and buttons to bottom
6807 $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
6808 $formconfirm .= '<div style="display: flex; flex-direction: column; height: 100%;">';
6809 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6810 $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
6811 }
6812 if (!empty($more)) {
6813 $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
6814 }
6815 // NEW: Add help block if content provided
6816 if (!empty($helpContent)) {
6817 $formconfirm .= '<div style="text-align:left; margin-top:12px; padding-top:8px; border-top:1px solid #eee; clear:both;">';
6818 $formconfirm .= $this->getHelpBlock($helpContent);
6819 $formconfirm .= '</div>';
6820 }
6821 if (!empty($question)) {
6822 $formconfirm .= '<div class="confirmmessage" style="padding-top: 15px;">';
6823 $formconfirm .= img_help(0, '') . ' ' . $question;
6824 $formconfirm .= '</div>';
6825 }
6826 $formconfirm .= '</div>';
6827 $formconfirm .= '</div>' . "\n";
6828
6829 $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
6830 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
6831 $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
6832 $formconfirm .= 'jQuery(document).ready(function() {
6833 $(function() {
6834 $( "#' . $dialogconfirm . '" ).dialog({
6835 autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
6836 if ($newselectedchoice == 'no') {
6837 $formconfirm .= '
6838 open: function() {
6839 $(this).parent().find("button.ui-button:eq(2)").focus();
6840 },';
6841 }
6842
6843 $jsforcursor = '';
6844 if ($useajax == 1) {
6845 $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
6846 $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
6847 }
6848
6849 $postconfirmas = 'GET';
6850
6851 $formconfirm .= '
6852 resizable: false,
6853 height: \'' . dol_escape_js($height) . '\',
6854 width: \'' . dol_escape_js($width) . '\',
6855 modal: true,
6856 closeOnEscape: false,
6857 buttons: {
6858 "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
6859 var options = "token=' . urlencode(newToken()) . '";
6860 var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
6861 var page = \'' . dol_escape_js(!empty($page) ? $page : '') . '\';
6862 var pageyes = \'' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '\';
6863
6864 if (inputok.length > 0) {
6865 $.each(inputok, function(i, inputname) {
6866 var more = "";
6867 var inputvalue;
6868 if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
6869 inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
6870 } else {
6871 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6872 inputvalue = $("#" + inputname + more).val();
6873 }
6874 if (typeof inputvalue == "undefined") { inputvalue=""; }
6875 console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
6876 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6877 });
6878 }
6879 var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
6880 if (pageyes.length > 0) {';
6881 if ($postconfirmas == 'GET') {
6882 $formconfirm .= 'location.href = urljump;';
6883 } else {
6884 $formconfirm .= $jsforcursor;
6885 $formconfirm .= 'var post = $.post(
6886 pageyes,
6887 options,
6888 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6889 );';
6890 }
6891 $formconfirm .= '
6892 console.log("after post ok");
6893 }
6894 $(this).dialog("close");
6895 },
6896 "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
6897 var options = "token=' . urlencode(newToken()) . '";
6898 var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
6899 var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
6900 var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
6901 if (inputko.length > 0) {
6902 $.each(inputko, function(i, inputname) {
6903 var more = "";
6904 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6905 var inputvalue = $("#" + inputname + more).val();
6906 if (typeof inputvalue == "undefined") { inputvalue=""; }
6907 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6908 });
6909 }
6910 var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
6911 //alert(urljump);
6912 if (pageno.length > 0) {';
6913 if ($postconfirmas == 'GET') {
6914 $formconfirm .= 'location.href = urljump;';
6915 } else {
6916 $formconfirm .= $jsforcursor;
6917 $formconfirm .= 'var post = $.post(
6918 pageno,
6919 options,
6920 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6921 );';
6922 }
6923 $formconfirm .= '
6924 console.log("after post ko");
6925 }
6926 $(this).dialog("close");
6927 }
6928 }
6929 }
6930 );
6931
6932 var button = "' . $button . '";
6933 if (button.length > 0) {
6934 $( "#" + button ).click(function() {
6935 $("#' . $dialogconfirm . '").dialog("open");
6936 });
6937 }
6938 });
6939 });
6940 </script>';
6941 $formconfirm .= "<!-- end ajax formconfirm -->\n";
6942 } else {
6943 $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
6944
6945 if (empty($disableformtag)) {
6946 $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftnoright">' . "\n";
6947 }
6948
6949 $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
6950 $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
6951
6952 $formconfirm .= '<div class="valid">' . "\n";
6953
6954 // Line title
6955 $formconfirm .= '<div class="validtitre">';
6956 $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
6957 $formconfirm .= '</div>' . "\n";
6958
6959 // Line text
6960 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6961 $formconfirm .= '<div class="valid">' . $formquestion['text'] . '</div>' . "\n";
6962 }
6963
6964 // Line form fields
6965 if ($more) {
6966 $formconfirm .= '<div>' . "\n";
6967 $formconfirm .= $more;
6968 $formconfirm .= '</div>' . "\n";
6969 }
6970
6971 // NEW: Help block row (between form fields and question)
6972 if (!empty($helpContent)) {
6973 $formconfirm .= '<div style="padding-top:8px; border-top:1px solid #888;">';
6974 $formconfirm .= $this->getHelpBlock($helpContent);
6975 $formconfirm .= '</div>' . "\n";
6976 }
6977
6978 // Let's add a row that acts as a spacer.
6979 $formconfirm .= '<div style="padding-top: 20px;"></div>' . "\n";
6980
6981 // Question row
6982 $formconfirm .= '<div class="inline-block">' . $question . '</div>';
6983
6984 $formconfirm .= '<div class="inline-block">';
6985 $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
6986 $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
6987 $formconfirm .= '</div>';
6988
6989 $formconfirm .= '</div>';
6990
6991 if (empty($disableformtag)) {
6992 $formconfirm .= "</form>\n";
6993 }
6994 $formconfirm .= '<br>';
6995
6996 if (!empty($conf->use_javascript_ajax)) {
6997 $formconfirm .= '<!-- code to disable button to avoid double clic -->';
6998 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
6999 $formconfirm .= '
7000 $(document).ready(function () {
7001 $(".confirmvalidatebutton").on("click", function() {
7002 console.log("We click on button confirmvalidatebutton");
7003 $(this).attr("disabled", "disabled");
7004 setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
7005 //console.log($(this).closest("form"));
7006 $(this).closest("form").submit();
7007 });
7008 });
7009 ';
7010 $formconfirm .= '</script>' . "\n";
7011 }
7012
7013 $formconfirm .= "<!-- end formconfirm -->\n";
7014 }
7015
7016 return $formconfirm;
7017 }
7018
7019 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7020
7037 public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '', $option = '')
7038 {
7039 // phpcs:enable
7040 global $langs;
7041
7042 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
7043 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
7044
7045 $out = '';
7046
7047 $formproject = new FormProjets($this->db);
7048
7049 $langs->load("project");
7050 if ($htmlname != "none") {
7051 $out .= '<form method="post" action="' . $page . '">';
7052 $out .= '<input type="hidden" name="action" value="classin">';
7053 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7054 $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
7055 $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7056 $out .= '</form>';
7057 } else {
7058 $out .= '<span class="project_head_block">';
7059 if ($selected instanceof Project) {
7060 $out .= $selected->getNomUrl(0, $option, 1);
7061 } elseif (is_numeric($selected)) {
7062 $projet = new Project($this->db);
7063 $projet->fetch((int) $selected);
7064 $out .= $projet->getNomUrl(0, $option, 1);
7065 } else {
7066 $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
7067 }
7068 $out .= '</span>';
7069 }
7070
7071 if (empty($nooutput)) {
7072 print $out;
7073 return '';
7074 }
7075 return $out;
7076 }
7077
7078 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7079
7095 public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
7096 {
7097 // phpcs:enable
7098 global $langs;
7099
7100 $selected = (int) $selected;
7101
7102 $out = '';
7103
7104 if ($htmlname != "none") {
7105 $out .= '<form method="POST" action="' . $page . '">';
7106 $out .= '<input type="hidden" name="action" value="setconditions">';
7107 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7108 if ($type) {
7109 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7110 }
7111 $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
7112 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7113 $out .= '</form>';
7114 } else {
7115 if ($selected) {
7116 $this->load_cache_conditions_paiements();
7117
7118 if (isset($this->cache_conditions_paiements[$selected])) {
7119 $label = $this->cache_conditions_paiements[$selected]['label'];
7120
7121 if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
7122 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
7123 }
7124
7125 $out .= $label;
7126 } else {
7127 $langs->load('errors');
7128 $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
7129 }
7130 } else {
7131 $out .= '&nbsp;';
7132 }
7133 }
7134
7135 if (empty($nooutput)) {
7136 print $out;
7137 return '';
7138 }
7139 return $out;
7140 }
7141
7142 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7143
7154 public function form_rule_for_lines_dates($page, $selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0, $nooutput = 0): string
7155 {
7156 // phpcs:enable
7157 global $langs;
7158
7159 $out = '';
7160
7161 if ($htmlname != 'none') {
7162 $out .= '<form method="POST" action="' . $page . '">';
7163 $out .= '<input type="hidden" name="action" value="setruleforlinesdates">';
7164 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7165 $out .= $this->getSelectRuleForLinesDates($selected, $htmlname, $addempty);
7166 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7167 $out .= '</form>';
7168 } else {
7169 if (isset($selected)) {
7170 $this->load_cache_rule_for_lines_dates();
7171 if (isset($this->cache_rule_for_lines_dates[$selected])) {
7172 $label = $this->cache_rule_for_lines_dates[$selected];
7173 $out .= $langs->trans($label);
7174 }
7175 } else {
7176 $out .= '&nbsp;';
7177 }
7178 }
7179
7180 if (empty($nooutput)) {
7181 print $out;
7182 return '';
7183 }
7184
7185 return $out;
7186 }
7187
7188 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7189
7199 public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
7200 {
7201 dol_syslog(__METHOD__, LOG_DEBUG);
7202 // phpcs:enable
7203 global $langs;
7204 if ($htmlname != "none") {
7205 print '<form method="post" action="' . $page . '">';
7206 print '<input type="hidden" name="action" value="setavailability">';
7207 print '<input type="hidden" name="token" value="' . newToken() . '">';
7208 print $this->selectAvailabilityDelay($selected, $htmlname, '', $addempty, '', 1);
7209 print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7210 print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
7211 print '</form>';
7212 } else {
7213 if ($selected) {
7214 $this->load_cache_availability();
7215 // @phan-suppress-next-line PhanTypeMismatchProperty
7216 if (isset($this->cache_availability[$selected])) {
7217 print $this->cache_availability[$selected]['label'];
7218 } else {
7219 print "&nbsp;";
7220 }
7221 } else {
7222 print "&nbsp;";
7223 }
7224 }
7225 }
7226
7238 public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0, $morecss = '')
7239 {
7240 global $langs;
7241 if ($htmlname != "none") {
7242 print '<form method="post" action="' . $page . '">';
7243 print '<input type="hidden" name="action" value="setdemandreason">';
7244 print '<input type="hidden" name="token" value="' . newToken() . '">';
7245 $this->selectInputReason($selected, $htmlname, '-1', $addempty, $morecss);
7246 print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7247 print '</form>';
7248 } else {
7249 if ($selected) {
7250 $this->loadCacheInputReason();
7251 foreach ($this->cache_demand_reason as $key => $val) {
7252 if ($val['id'] == $selected) {
7253 print $val['label'];
7254 break;
7255 }
7256 }
7257 } else {
7258 print "&nbsp;";
7259 }
7260 }
7261 }
7262
7263 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7264
7278 public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
7279 {
7280 // phpcs:enable
7281 global $langs;
7282
7283 $ret = '';
7284
7285 if ($htmlname != "none") {
7286 $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7287 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
7288 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
7289 if ($type) {
7290 $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7291 }
7292 $ret .= '<table class="nobordernopadding">';
7293 $ret .= '<tr><td>';
7294 $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
7295 $ret .= '</td>';
7296 $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7297 $ret .= '</tr></table></form>';
7298 } else {
7299 if ($displayhour) {
7300 $ret .= dol_print_date($selected, 'dayhour');
7301 } else {
7302 $ret .= dol_print_date($selected, 'day');
7303 }
7304 }
7305
7306 if (empty($nooutput)) {
7307 print $ret;
7308 }
7309 return $ret;
7310 }
7311
7312
7313 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7314
7325 public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
7326 {
7327 // phpcs:enable
7328 global $langs;
7329
7330 if ($htmlname != "none") {
7331 print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7332 print '<input type="hidden" name="action" value="set' . $htmlname . '">';
7333 print '<input type="hidden" name="token" value="' . newToken() . '">';
7334 print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
7335 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7336 print '</form>';
7337 } else {
7338 if ($selected) {
7339 require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
7340 $theuser = new User($this->db);
7341 $theuser->fetch((int) $selected);
7342 print $theuser->getNomUrl(1);
7343 } else {
7344 print "&nbsp;";
7345 }
7346 }
7347 }
7348
7349
7350 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7351
7365 public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
7366 {
7367 // phpcs:enable
7368 global $langs;
7369
7370 $out = '';
7371 if ($htmlname != "none") {
7372 $out .= '<form method="POST" action="' . $page . '">';
7373 $out .= '<input type="hidden" name="action" value="setmode">';
7374 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7375 if ($type) {
7376 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7377 }
7378 $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
7379 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7380 $out .= '</form>';
7381 } else {
7382 if ($selected) {
7383 $this->load_cache_types_paiements();
7384 $out .= $this->cache_types_paiements[$selected]['label'];
7385 } else {
7386 $out .= "&nbsp;";
7387 }
7388 }
7389
7390 if ($nooutput) {
7391 return $out;
7392 } else {
7393 print $out;
7394 }
7395 return '';
7396 }
7397
7408 public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
7409 {
7410 global $langs;
7411 if ($htmlname != "none") {
7412 print '<form method="POST" action="' . $page . '">';
7413 print '<input type="hidden" name="action" value="settransportmode">';
7414 print '<input type="hidden" name="token" value="' . newToken() . '">';
7415 $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
7416 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7417 print '</form>';
7418 } else {
7419 if ($selected) {
7420 $this->load_cache_transport_mode();
7421 print $this->cache_transport_mode[$selected]['label'];
7422 } else {
7423 print "&nbsp;";
7424 }
7425 }
7426 }
7427
7428 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7429
7438 public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
7439 {
7440 // phpcs:enable
7441 global $langs;
7442 if ($htmlname != "none") {
7443 print '<form method="POST" action="' . $page . '">';
7444 print '<input type="hidden" name="action" value="setmulticurrencycode">';
7445 print '<input type="hidden" name="token" value="' . newToken() . '">';
7446 print $this->selectMultiCurrency($selected, $htmlname, 0);
7447 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7448 print '</form>';
7449 } else {
7450 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
7451 print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
7452 }
7453 }
7454
7455 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7456
7467 public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '', $rate_direct = 0.0)
7468 {
7469 // phpcs:enable
7470 global $langs, $conf;
7471
7472 if ($htmlname != "none") {
7473 print '<form method="POST" action="' . $page . '">';
7474 print '<input type="hidden" name="action" value="setmulticurrencyrate">';
7475 print '<input type="hidden" name="token" value="' . newToken() . '">';
7476 print '<input type="text" class="maxwidth75" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" spellcheck="false" /> ';
7477 print '<select name="calculation_mode" id="calculation_mode">';
7478 print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
7479 print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
7480 print '</select> ';
7481 print ajax_combobox("calculation_mode");
7482 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7483 print '</form>';
7484 } else {
7485 if (!empty($rate)) {
7486 print price($rate, 1, $langs, 0, 0);
7487 if ($currency && $rate != 1) {
7493 if (getDolGlobalString('MULTICURRENCY_USE_RATE_DIRECT')) {
7494 print ' &nbsp; <span class="opacitymedium">(' . price($rate_direct, 1, $langs, 0, 0) . ' ' . $conf->currency . ' = 1 ' . $currency . ')</span>';
7495 } else {
7496 print ' &nbsp; <span class="opacitymedium">(' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')</span>';
7497 }
7498 }
7499 } else {
7500 print 1;
7501 }
7502 }
7503 }
7504
7505 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7506
7524 public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0, $filterabsolutediscount = 0, $filtercreditnote = 0)
7525 {
7526 // phpcs:enable
7527 global $conf, $langs;
7528
7529 if ($htmlname != "none") {
7530 print '<form method="post" action="' . $page . '" class="inline-block">';
7531 print '<input type="hidden" name="action" value="setabsolutediscount">';
7532 print '<input type="hidden" name="token" value="' . newToken() . '">';
7533 print '<div class="inline-block">';
7534 if (!empty($discount_type)) {
7535 if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
7536 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
7537 $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7538 } else {
7539 $translationKey = 'HasCreditNoteFromSupplier';
7540 }
7541 } else {
7542 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7543 $translationKey = 'HasAbsoluteDiscountFromSupplier';
7544 } else {
7545 $translationKey = 'HasCreditNoteFromSupplier';
7546 }
7547 }
7548 } else {
7549 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
7550 if (!$filter || $filter == "fk_facture_source IS NULL") {
7551 $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7552 } else {
7553 $translationKey = 'CompanyHasCreditNote';
7554 }
7555 } else {
7556 if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7557 $translationKey = 'CompanyHasAbsoluteDiscount';
7558 } else {
7559 $translationKey = 'CompanyHasCreditNote';
7560 }
7561 }
7562 }
7563 print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
7564 if (empty($hidelist)) {
7565 print ' ';
7566 }
7567 print '</div>';
7568 if (empty($hidelist)) {
7569 print '<div class="inline-block" style="padding-right: 10px">';
7570 $newfilter = 'discount_type = ' . intval($discount_type);
7571 if (!empty($discount_type)) {
7572 $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
7573 } else {
7574 $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
7575 }
7576 if ($filter) {
7577 $sanitizedfilter = $filter;
7578 $newfilter .= ' AND (' . $sanitizedfilter . ')';
7579 }
7580 // output the combo of discounts
7581 $nbqualifiedlines = $this->select_remises((string) $selected, $htmlname, $newfilter, $socid, $maxvalue);
7582 if ($nbqualifiedlines > 0) {
7583 print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
7584 if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7585 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7586 }
7587 if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7588 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7589 }
7590
7591 print '>';
7592 }
7593 print '</div>';
7594 }
7595 if ($more) {
7596 print '<div class="inline-block">';
7597 print $more;
7598 print '</div>';
7599 }
7600 print '</form>';
7601 } else {
7602 if ($selected) {
7603 print $selected;
7604 } else {
7605 print "0";
7606 }
7607 }
7608 }
7609
7610
7611 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7612
7622 public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
7623 {
7624 // phpcs:enable
7625 global $langs;
7626
7627 if ($htmlname != "none") {
7628 print '<form method="post" action="' . $page . '">';
7629 print '<input type="hidden" name="action" value="set_contact">';
7630 print '<input type="hidden" name="token" value="' . newToken() . '">';
7631 print '<table class="nobordernopadding">';
7632 print '<tr><td>';
7633 print $this->selectcontacts($societe->id, $selected, $htmlname);
7634 $num = $this->num;
7635 if ($num == 0) {
7636 $addcontact = (getDolGlobalString('SOCIETE_ADDRESSES_MANAGEMENT') ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
7637 print '<a href="' . DOL_URL_ROOT . '/contact/card.php?socid=' . $societe->id . '&action=create&backtoreferer=1">' . $addcontact . '</a>';
7638 }
7639 print '</td>';
7640 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7641 print '</tr></table></form>';
7642 } else {
7643 if ($selected) {
7644 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
7645 $contact = new Contact($this->db);
7646 $contact->fetch((int) $selected);
7647 print $contact->getFullName($langs);
7648 } else {
7649 print "&nbsp;";
7650 }
7651 }
7652 }
7653
7654 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7655
7672 public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
7673 {
7674 // phpcs:enable
7675 global $langs;
7676
7677 $out = '';
7678 if ($htmlname != "none") {
7679 $limit = getDolGlobalInt('THIRDPARTY_LIMIT_SIZE');
7680
7681 $out .= '<form method="post" action="' . $page . '">';
7682 $out .= '<input type="hidden" name="action" value="set_thirdparty">';
7683 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7684 $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, $limit, 'minwidth100', '', '', 1, array(), false, $excludeids);
7685 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7686 $out .= '</form>';
7687 } else {
7688 if ($selected) {
7689 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
7690 $soc = new Societe($this->db);
7691 $soc->fetch((int) $selected);
7692 $out .= $soc->getNomUrl(0, '');
7693 } else {
7694 $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
7695 }
7696 }
7697
7698 if ($nooutput) {
7699 return $out;
7700 } else {
7701 print $out;
7702 }
7703
7704 return '';
7705 }
7706
7707 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7708
7717 public function select_currency($selected = '', $htmlname = 'currency_id')
7718 {
7719 // phpcs:enable
7720 print $this->selectCurrency($selected, $htmlname);
7721 }
7722
7732 public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
7733 {
7734 global $langs, $user;
7735
7736 $langs->loadCacheCurrencies('');
7737
7738 $out = '';
7739
7740 if ($selected == 'euro' || $selected == 'euros') {
7741 $selected = 'EUR'; // Pour compatibilite
7742 }
7743
7744 $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
7745 if ($useempty) {
7746 $out .= '<option value="-1" selected></option>';
7747 }
7748 foreach ($langs->cache_currencies as $code_iso => $currency) {
7749 $labeltoshow = $currency['label'];
7750 if ($mode == 1) {
7751 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
7752 } elseif ($mode == 2) {
7753 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso.' - '.$langs->getCurrencySymbol($code_iso) . ')</span>';
7754 } else {
7755 $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
7756 }
7757
7758 if ($selected && $selected == $code_iso) {
7759 $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7760 } else {
7761 $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7762 }
7763 $out .= dol_string_nohtmltag($labeltoshow);
7764 $out .= '</option>';
7765 }
7766 $out .= '</select>';
7767 if ($user->admin) {
7768 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
7769 }
7770
7771 // Make select dynamic
7772 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7773 $out .= ajax_combobox($htmlname);
7774
7775 return $out;
7776 }
7777
7790 public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = 'maxwidth200 widthcentpercentminusx')
7791 {
7792 global $conf, $langs;
7793
7794 $langs->loadCacheCurrencies(''); // Load ->cache_currencies
7795
7796 $TCurrency = array();
7797
7798 $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
7799 $sql .= " WHERE entity IN ('" . getEntity('multicurrency') . "')";
7800 if ($filter) {
7801 $sql .= forgeSQLFromUniversalSearchCriteria($filter);
7802 }
7803 $resql = $this->db->query($sql);
7804 if ($resql) {
7805 while ($obj = $this->db->fetch_object($resql)) {
7806 $TCurrency[$obj->code] = $obj->code;
7807 }
7808 }
7809
7810 $out = '';
7811 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7812 if ($useempty) {
7813 $out .= '<option value="">&nbsp;</option>';
7814 }
7815 // If company current currency not in table, we add it into list. Should always be available.
7816 if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
7817 $TCurrency[$conf->currency] = $conf->currency;
7818 }
7819 if (count($TCurrency) > 0) {
7820 foreach ($langs->cache_currencies as $code_iso => $currency) {
7821 if (isset($TCurrency[$code_iso])) {
7822 if (!empty($selected) && $selected == $code_iso) {
7823 $out .= '<option value="' . $code_iso . '" selected="selected">';
7824 } else {
7825 $out .= '<option value="' . $code_iso . '">';
7826 }
7827
7828 $out .= $currency['label'];
7829 $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
7830 $out .= '</option>';
7831 }
7832 }
7833 }
7834
7835 $out .= '</select>';
7836
7837 // Make select dynamic
7838 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7839 $out .= ajax_combobox($htmlname);
7840
7841 return $out;
7842 }
7843
7844 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7845
7852 public function load_cache_vatrates($country_code)
7853 {
7854 // phpcs:enable
7855 global $langs, $user;
7856
7857 $num = count($this->cache_vatrates);
7858 if ($num > 0) {
7859 return $num; // Cache already loaded
7860 }
7861
7862 dol_syslog(__METHOD__, LOG_DEBUG);
7863
7864 $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";
7865 $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
7866 $sql .= " WHERE t.fk_pays = c.rowid";
7867 $sql .= " AND t.active > 0";
7868 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7869 $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
7870 $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
7871
7872 $resql = $this->db->query($sql);
7873 if ($resql) {
7874 $num = $this->db->num_rows($resql);
7875 if ($num) {
7876 for ($i = 0; $i < $num; $i++) {
7877 $obj = $this->db->fetch_object($resql);
7878
7879 $tmparray = array();
7880 $tmparray['rowid'] = (int) $obj->rowid;
7881 $tmparray['type_vat'] = ($obj->type_vat <= 0 ? 0 : $obj->type_vat); // Some version have type_vat corrupted with value -1
7882 $tmparray['code'] = $obj->code;
7883 $tmparray['txtva'] = $obj->taux;
7884 $tmparray['nprtva'] = $obj->recuperableonly;
7885 $tmparray['localtax1'] = $obj->localtax1;
7886 $tmparray['localtax1_type'] = $obj->localtax1_type;
7887 $tmparray['localtax2'] = $obj->localtax2;
7888 $tmparray['localtax2_type'] = $obj->localtax1_type;
7889 $tmparray['einvoice_vatex'] = $obj->einvoice_vatex;
7890
7891 $tmparray['label'] = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
7892 $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
7893 $positiverates = '';
7894 if ($obj->taux) {
7895 $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
7896 }
7897 if ($obj->localtax1) {
7898 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
7899 }
7900 if ($obj->localtax2) {
7901 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
7902 }
7903 if (empty($positiverates)) {
7904 $positiverates = '0';
7905 }
7906 $tmparray['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
7907
7908 $this->cache_vatrates[$obj->rowid] = $tmparray;
7909 }
7910
7911 return $num;
7912 } else {
7913 $this->error = '<span class="error">';
7914 $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
7915 $reg = array();
7916 if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
7917 $langs->load("errors");
7918 $new_country_code = $reg[1];
7919 $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_country', 'code', 'rowid');
7920 $this->error .= '<br>'.$langs->trans("ErrorFixThisHere", DOL_URL_ROOT.'/admin/dict.php?id=10'.($country_id > 0 ? '&countryidforinsert='.$country_id : ''));
7921 }
7922 $this->error .= '</span>';
7923 return -1;
7924 }
7925 } else {
7926 $this->error = '<span class="error">' . $this->db->error() . '</span>';
7927 return -2;
7928 }
7929 }
7930
7931 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7932
7955 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)
7956 {
7957 // phpcs:enable
7958 global $langs, $mysoc, $hookmanager;
7959
7960 $langs->load('errors');
7961
7962 $return = '';
7963 // Bypass the default method
7964 $hookmanager->initHooks(array('commonobject'));
7965 $info_bits == 1 ? $is_npr = 1 : $is_npr = 0;
7966 $parameters = array(
7967 'htmlname' => $htmlname,
7968 'selectedrate' => $selectedrate,
7969 'seller' => $societe_vendeuse,
7970 'buyer' => $societe_acheteuse,
7971 'idprod' => $idprod,
7972 'is_npr' => $is_npr,
7973 'type' => $type,
7974 'options_only' => $options_only,
7975 'mode' => $mode,
7976 'type_vat' => $type_vat
7977 );
7978 $reshook = $hookmanager->executeHooks('load_tva', $parameters);
7979 if ($reshook > 0) {
7980 return $hookmanager->resPrint;
7981 } elseif ($reshook === 0) {
7982 $return .= $hookmanager->resPrint;
7983 }
7984
7985 // Define defaultnpr, defaultttx and defaultcode
7986 $defaultnpr = ($info_bits & 0x01);
7987 $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
7988 $defaulttx = str_replace('*', '', $selectedrate);
7989 $defaultcode = '';
7990 $reg = array();
7991 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7992 $defaultcode = $reg[1];
7993 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7994 }
7995 //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
7996
7997 // Check parameters
7998 if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
7999 if ($societe_vendeuse->id == $mysoc->id) {
8000 $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
8001 } else {
8002 $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
8003 }
8004 return $return;
8005 }
8006
8007 //var_dump($societe_acheteuse);
8008 //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";
8009 //exit;
8010
8011 // Define list of countries to use to search VAT rates to show
8012 // First we defined code_country to use to find list
8013 if (is_object($societe_vendeuse)) {
8014 $code_country = "'" . $societe_vendeuse->country_code . "'";
8015 } else {
8016 $code_country = "'" . $mysoc->country_code . "'"; // Pour compatibilite ascendente
8017 }
8018
8019 if ($societe_vendeuse == $mysoc && getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) { // If option to have vat for end customer for services is on
8020 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8021 // If SERVICE_ARE_ECOMMERCE_200238EC=1 combo list vat rate of purchaser and seller countries
8022 // If SERVICE_ARE_ECOMMERCE_200238EC=2 combo list only the vat rate of the purchaser country
8023 $selectVatComboMode = getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC');
8024 if (is_object($societe_vendeuse) && is_object($societe_acheteuse) && isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()) {
8025 // We also add the buyer country code
8026 if (is_numeric($type)) {
8027 if ($type == 1) { // We know product is a service
8028 switch ($selectVatComboMode) {
8029 case '1':
8030 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8031 break;
8032 case '2':
8033 $code_country = "'" . $societe_acheteuse->country_code . "'";
8034 break;
8035 }
8036 }
8037 } elseif (!$idprod) { // We don't know type of product
8038 switch ($selectVatComboMode) {
8039 case '1':
8040 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8041 break;
8042 case '2':
8043 $code_country = "'" . $societe_acheteuse->country_code . "'";
8044 break;
8045 }
8046 } else {
8047 $prodstatic = new Product($this->db);
8048 $prodstatic->fetch($idprod);
8049 if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
8050 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8051 }
8052 }
8053 }
8054 }
8055
8056 // Now we load the list of VAT
8057 $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
8058
8059 // Keep only the VAT qualified for $type_vat
8060 $arrayofvatrates = array();
8061 foreach ($this->cache_vatrates as $cachevalue) {
8062 if (empty($cachevalue['type_vat']) || $cachevalue['type_vat'] == $type_vat) {
8063 $arrayofvatrates[] = $cachevalue;
8064 }
8065 }
8066
8067 $num = count($arrayofvatrates);
8068 if ($num > 0) {
8069 // Define the vat rate to preselect (if defaulttx not forced so is -1 or '')
8070 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8071 // Define a default thirdparty to use if the seller or buyer is not defined
8072 $tmpthirdparty = new Societe($this->db);
8073 $tmpthirdparty->country_code = $mysoc->country_code;
8074
8075 $defaulttx = get_default_tva(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8076 $defaultnpr = get_default_npr(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8077
8078 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8079 $defaultcode = $reg[1];
8080 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8081 }
8082 if (empty($defaulttx)) {
8083 $defaultnpr = 0;
8084 }
8085 }
8086
8087 // If we fails to find a default vat rate, we take the last one in list
8088 // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
8089 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8090 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8091 // We take the last one found in list
8092 $defaulttx = $arrayofvatrates[$num - 1]['txtva'];
8093 } else {
8094 // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
8095 $defaulttx = '';
8096 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8097 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8098 }
8099 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8100 $defaultcode = $reg[1];
8101 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8102 }
8103 }
8104 }
8105
8106 // Disabled is true if the seller is not subject to VAT
8107 $disabled = false;
8108 $title = '';
8109 if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && empty($societe_vendeuse->tva_assuj)) {
8110 // 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
8111 // EXPENSEREPORT_OVERRIDE_VAT is a strange option that allow to override/enable VAT regardless of sellet vat option - needed for expense report if
8112 // expense report used for business expenses instead of using supplier invoices (but this is a very bad idea !)
8113 if (!getDolGlobalString('EXPENSEREPORT_OVERRIDE_VAT')) {
8114 $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
8115 $disabled = true;
8116 }
8117 }
8118
8119 if (!$options_only) {
8120 $return .= '<select class="flat valignmiddle minwidth75imp maxwidth100 right" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
8121 }
8122
8123 $selectedfound = false;
8124 foreach ($arrayofvatrates as $rate) {
8125 // Keep only 0 if seller is not subject to VAT
8126 if ($disabled && $rate['txtva'] != 0) {
8127 continue;
8128 }
8129
8130 // Define key to use into select list
8131 $key = $rate['txtva'];
8132 $key .= $rate['nprtva'] ? '*' : '';
8133 if ($mode > 0 && $rate['code']) {
8134 $key .= ' (' . $rate['code'] . ')';
8135 }
8136 if ($mode < 0) {
8137 $key = $rate['rowid'];
8138 }
8139
8140 $return .= '<option value="' . $key . '" data-vatid="'.$rate['rowid'].'"';
8141 if (!$selectedfound) {
8142 if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
8143 if ($defaultcode == $rate['code']) {
8144 $return .= ' selected';
8145 $selectedfound = true;
8146 }
8147 } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
8148 $return .= ' selected';
8149 $selectedfound = true;
8150 }
8151 }
8152 $return .= '>';
8153
8154 // Show label of VAT
8155 if ($mysoc->country_code == 'IN' || getDolGlobalString('MAIN_VAT_LABEL_IS_POSITIVE_RATES')) {
8156 // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
8157 $return .= $rate['labelpositiverates'];
8158 } else {
8159 // Simple label
8160 $return .= vatrate($rate['label']);
8161 }
8162
8163 //$return.=($rate['code']?' '.$rate['code']:'');
8164 $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
8165
8166 $return .= '</option>';
8167 }
8168
8169 if (!$options_only) {
8170 $return .= '</select>';
8171 //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
8172 }
8173 } else {
8174 $return .= $this->error;
8175 }
8176
8177 $this->num = $num;
8178 return $return;
8179 }
8180
8181
8182 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8183
8208 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 = '')
8209 {
8210 // phpcs:enable
8211 dol_syslog(__METHOD__ . ': using select_date is deprecated. Use selectDate instead.', LOG_WARNING);
8212 $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
8213 if (!empty($nooutput)) {
8214 return $retstring;
8215 }
8216 print $retstring;
8217
8218 return '';
8219 }
8220
8236 public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
8237 {
8238 global $langs;
8239
8240 $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
8241 if ($forcenewline) {
8242 $ret .= '<br>';
8243 }
8244 $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
8245 return $ret;
8246 }
8247
8276 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 = '')
8277 {
8278 global $conf, $langs;
8279
8280 if ($gm === 'auto') {
8281 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
8282 }
8283
8284 $retstring = '';
8285
8286 if ($prefix == '') {
8287 $prefix = 're';
8288 }
8289 if ($h == '') {
8290 $h = 0;
8291 }
8292 if ($m == '') {
8293 $m = 0;
8294 }
8295 $emptydate = 0;
8296 $emptyhours = 0;
8297 if ($stepminutes <= 0 || $stepminutes > 30) {
8298 $stepminutes = 1;
8299 }
8300 if ($empty == 1) {
8301 $emptydate = 1;
8302 $emptyhours = 1;
8303 }
8304 if ($empty == 2) {
8305 $emptydate = 0;
8306 $emptyhours = 1;
8307 }
8308 $orig_set_time = $set_time;
8309
8310 if ($set_time === '' && $emptydate == 0) {
8311 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8312 if ($gm == 'tzuser' || $gm == 'tzuserrel') {
8313 $set_time = dol_now($gm);
8314 } else {
8315 $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
8316 }
8317 }
8318
8319 // Analysis of the preselected date
8320 $reg = array();
8321 $shour = '';
8322 $smin = '';
8323 $ssec = '';
8324 if (!empty($set_time) && preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', (string) $set_time, $reg)) { // deprecated usage
8325 // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
8326 $syear = (!empty($reg[1]) ? $reg[1] : '');
8327 $smonth = (!empty($reg[2]) ? $reg[2] : '');
8328 $sday = (!empty($reg[3]) ? $reg[3] : '');
8329 $shour = (!empty($reg[4]) ? $reg[4] : '');
8330 $smin = (!empty($reg[5]) ? $reg[5] : '');
8331 } elseif (strval($set_time) != '' && $set_time != -1) {
8332 // set_time est un timestamps (0 possible)
8333 $syear = dol_print_date($set_time, "%Y", $gm);
8334 $smonth = dol_print_date($set_time, "%m", $gm);
8335 $sday = dol_print_date($set_time, "%d", $gm);
8336 if ($orig_set_time != '') {
8337 $shour = dol_print_date($set_time, "%H", $gm);
8338 $smin = dol_print_date($set_time, "%M", $gm);
8339 $ssec = dol_print_date($set_time, "%S", $gm);
8340 }
8341 } else {
8342 // Date est '' ou vaut -1
8343 $syear = '';
8344 $smonth = '';
8345 $sday = '';
8346 $shour = getDolGlobalString('MAIN_DEFAULT_DATE_HOUR', ($h == -1 ? '23' : ''));
8347 $smin = getDolGlobalString('MAIN_DEFAULT_DATE_MIN', ($h == -1 ? '59' : ''));
8348 $ssec = getDolGlobalString('MAIN_DEFAULT_DATE_SEC', ($h == -1 ? '59' : ''));
8349 }
8350 if ($h == 3 || $h == 4) {
8351 $shour = '';
8352 }
8353 if ($m == 3) {
8354 $smin = '';
8355 }
8356
8357 $nowgmt = dol_now('gmt');
8358 //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
8359
8360 // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
8361 $usecalendar = 'combo';
8362 if (!empty($conf->use_javascript_ajax) && (!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') != "none")) {
8363 $usecalendar = ((!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') == 'eldy') ? 'jquery' : getDolGlobalString("MAIN_POPUP_CALENDAR"));
8364 }
8365 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8366 // If we use a text browser or screen reader, we use the 'combo' date selector
8367 $usecalendar = 'html';
8368 }
8369
8370 if ($d) {
8371 // Show date with popup
8372 if ($usecalendar != 'combo') {
8373 // Set $format and $formatjs and $formatjquery
8374 $reduceformat = (!empty($conf->dol_optimize_smallscreen) ? 1 : 0); // Test on original $format param.
8375 if ($reduceformat) {
8376 $format = str_replace('%Y', '%y', $langs->transnoentitiesnoconv("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8377 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8378 $formatjs = str_replace('yyyy', 'yy', $langs->transnoentitiesnoconv("FormatDateShortJavaInput"));
8379 $formatjquery = str_replace('yyyy', 'yy', $langs->trans("FormatDateShortJQueryInput"));
8380 } else {
8381 $format = $langs->transnoentitiesnoconv("FormatDateShortInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8382 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8383 $formatjs = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8384 $formatjquery = $langs->trans("FormatDateShortJQueryInput");
8385 }
8386
8387 // Set formatted_date (for example: '%d/%m/%Y', '%m-%d-%y', ...
8388 $formatted_date = '';
8389 if (strval($set_time) != '' && $set_time != -1) {
8390 $formatted_date = dol_print_date($set_time, $format, $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8391 }
8392
8393 // Calendrier popup version eldy
8394 if ($usecalendar == "eldy") {
8395 // To have this manager working back, you must retrieve all functions showDP child found into the lib_head.js of v4 for example
8396 // and load the js that contains them so the call of showDP will works.
8397 /*
8398 // Input area to enter date manually
8399 $retstring .= '<!-- datepicker usecalendar=eldy --><input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formatted_date . '"';
8400 $retstring .= ($disabled ? ' disabled' : '');
8401 $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($formatjslong")) . '\'); "'; // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8402 $retstring .= ' autocomplete="off">';
8403
8404 // Icon calendar
8405 $retstringbuttom = '';
8406 if (!$disabled) {
8407 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
8408 $base = DOL_URL_ROOT . '/core/';
8409 $retstringbuttom .= ' onClick="showDP(\'' . dol_escape_js($base) . '\',\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\',\'' . dol_escape_js($langs->defaultlang) . '\');"';
8410 $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8411 } else {
8412 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8413 }
8414 $retstring = $retstringbuttom . $retstring;
8415
8416 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8417 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8418 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8419 */
8420 } elseif ($usecalendar == 'jquery' || $usecalendar == 'html') {
8421 if (!$disabled && $usecalendar != 'html') {
8422 // Output javascript for datepicker
8423 $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (idate('Y') - 100));
8424 $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (idate('Y') + 100));
8425
8426 $retstring .= '<!-- datepicker usecalendar='.$usecalendar.' --><script nonce="' . getNonce() . '" type="text/javascript">';
8427 $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
8428 dateFormat: '" . dol_escape_js($formatjquery) . "',
8429 autoclose: true,
8430 todayHighlight: true,
8431 yearRange: '" . $minYear . ":" . $maxYear . "',";
8432 if (!empty($conf->dol_use_jmobile)) {
8433 $retstring .= "
8434 beforeShow: function (input, datePicker) {
8435 input.disabled = true;
8436 },
8437 onClose: function (dateText, datePicker) {
8438 this.disabled = false;
8439 },
8440 ";
8441 }
8442 // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
8443 if (!getDolGlobalString('MAIN_POPUP_CALENDAR_ON_FOCUS')) {
8444 $buttonImage = $calendarpicto ?: DOL_URL_ROOT . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png";
8445 $retstring .= "
8446 showOn: 'button', /* both has problem with autocompletion */
8447 buttonImage: '" . $buttonImage . "',
8448 buttonImageOnly: true";
8449 }
8450 $retstring .= "
8451 }) });";
8452 $retstring .= "</script>";
8453 }
8454
8455 // Input area to enter date manually
8456 $retstring .= '<div class="nowraponall inline-block divfordateinput">';
8457 $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="'.($usecalendar == 'html' ? "date" : "text").'" class="maxwidthdate'.(getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' textbrowser' : '').' center" maxlength="11" value="'.$formatted_date.'"';
8458 $retstring .= ($disabled ? ' disabled' : '');
8459 $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
8460 $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
8461 $retstring .= ' autocomplete="off">';
8462
8463 // Icon calendar
8464 if ($disabled) {
8465 $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink ui-datepicker-notrigger"') . '</button>';
8466 $retstring .= $retstringbutton;
8467 }
8468
8469 $retstring .= '</div>';
8470 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8471 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8472 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8473 } else {
8474 $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
8475 }
8476 } else {
8477 // Show date with combo selects
8478 // Day
8479 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
8480
8481 if ($emptydate || $set_time == -1) {
8482 $retstring .= '<option value="0" selected>&nbsp;</option>';
8483 }
8484
8485 for ($day = 1; $day <= 31; $day++) {
8486 $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
8487 }
8488
8489 $retstring .= "</select>";
8490
8491 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
8492 if ($emptydate || $set_time == -1) {
8493 $retstring .= '<option value="0" selected>&nbsp;</option>';
8494 }
8495
8496 // Month
8497 for ($month = 1; $month <= 12; $month++) {
8498 $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
8499 $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
8500 $retstring .= "</option>";
8501 }
8502 $retstring .= "</select>";
8503
8504 // Year
8505 if ($emptydate || $set_time == -1) {
8506 $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 . '">';
8507 } else {
8508 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
8509
8510 $syear = (int) $syear;
8511 for ($year = $syear - 10; $year < (int) $syear + 10; $year++) {
8512 $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
8513 }
8514 $retstring .= "</select>\n";
8515 }
8516 }
8517 }
8518
8519 if ($d && $h) {
8520 $retstring .= (($h == 2 || $h == 4) ? '<br>' : ' ');
8521 $retstring .= '<span class="nowraponall">';
8522 }
8523
8524 if ($h) {
8525 $hourstart = 0;
8526 $hourend = 24;
8527 if ($openinghours != '') {
8528 $openinghours = explode(',', $openinghours);
8529 $hourstart = $openinghours[0];
8530 $hourend = $openinghours[1];
8531 if ($hourend < $hourstart) {
8532 $hourend = $hourstart;
8533 }
8534 }
8535
8536 // Show hour
8537 $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
8538 $retstring .= ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
8539 if ($emptyhours) {
8540 $retstring .= '<option value="-1">&nbsp;</option>';
8541 }
8542 for ($hour = $hourstart; $hour < $hourend; $hour++) {
8543 if (strlen($hour) < 2) {
8544 $hour = "0" . $hour;
8545 }
8546 $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
8547 $retstring .= '</option>';
8548 }
8549 $retstring .= '</select>';
8550
8551 if ($disabled) {
8552 $retstring .= '<input type="hidden" id="' . $prefix . 'hour" name="' . $prefix . 'hour" value="' . $shour . '">' . "\n";
8553 }
8554 if ($m) {
8555 $retstring .= ":";
8556 }
8557 }
8558
8559 if ($m) {
8560 // Show minutes
8561 $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
8562 $retstring .= ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
8563 if ($emptyhours) {
8564 $retstring .= '<option value="-1">&nbsp;</option>';
8565 }
8566 for ($min = 0; $min < 60; $min += $stepminutes) {
8567 $min_str = sprintf("%02d", $min);
8568 $retstring .= '<option value="' . $min_str . '"' . (($min_str == $smin) ? ' selected' : '') . '>' . $min_str . '</option>';
8569 }
8570 $retstring .= '</select>';
8571 if ($disabled) {
8572 $retstring .= '<input type="hidden" id="' . $prefix . 'min" name="' . $prefix . 'min" value="' . $smin . '">' . "\n";
8573 }
8574 // Add also seconds
8575 $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
8576 }
8577
8578 if ($d && $h) {
8579 $retstring .= '</span>';
8580 }
8581
8582 // Add a "Now" link
8583 if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
8584 // Script which will be inserted in the onClick of the "Now" link
8585 $reset_scripts = "";
8586 if ($addnowlink == 2) { // local computer time
8587 // pad add leading 0 on numbers
8588 $reset_scripts .= "Number.prototype.pad = function(size) {
8589 var s = String(this);
8590 while (s.length < (size || 2)) {s = '0' + s;}
8591 return s;
8592 };
8593 var d = new Date();";
8594 }
8595
8596 // Generate the date part, depending on the use or not of the javascript calendar
8597 if ($addnowlink == 1) { // server time expressed in user time setup
8598 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8599 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8600 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8601 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8602 } elseif ($addnowlink == 2) {
8603 /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
8604 * This break application for foreign languages.
8605 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
8606 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
8607 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
8608 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
8609 */
8610 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8611 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8612 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8613 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8614 }
8615 /*if ($usecalendar == "eldy")
8616 {
8617 $base=DOL_URL_ROOT.'/core/';
8618 $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
8619 }
8620 else
8621 {
8622 $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
8623 $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
8624 $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
8625 }*/
8626 // Update the hour part
8627 if ($h) {
8628 if ($fullday) {
8629 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8630 }
8631 //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
8632 if ($addnowlink == 1) {
8633 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8634 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8635 } elseif ($addnowlink == 2) {
8636 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
8637 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8638 }
8639
8640 if ($fullday) {
8641 $reset_scripts .= ' } ';
8642 }
8643 }
8644 // Update the minute part
8645 if ($m) {
8646 if ($fullday) {
8647 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8648 }
8649 //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
8650 if ($addnowlink == 1) {
8651 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8652 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8653 } elseif ($addnowlink == 2) {
8654 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
8655 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8656 }
8657 if ($fullday) {
8658 $reset_scripts .= ' } ';
8659 }
8660 }
8661 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8662 if ($reset_scripts && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8663 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
8664 $retstring .= $langs->trans("Now");
8665 $retstring .= '</button> ';
8666 }
8667 }
8668
8669 // Add a "Plus one hour" link
8670 if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
8671 // Script which will be inserted in the onClick of the "Add plusone" link
8672 $reset_scripts = "";
8673
8674 // Generate the date part, depending on the use or not of the javascript calendar
8675 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
8676 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8677 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8678 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8679 // Update the hour part
8680 if ($h) {
8681 if ($fullday) {
8682 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8683 }
8684 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8685 if ($fullday) {
8686 $reset_scripts .= ' } ';
8687 }
8688 }
8689 // Update the minute part
8690 if ($m) {
8691 if ($fullday) {
8692 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8693 }
8694 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8695 if ($fullday) {
8696 $reset_scripts .= ' } ';
8697 }
8698 }
8699 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8700 if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
8701 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
8702 $retstring .= $langs->trans("DateStartPlusOne");
8703 $retstring .= '</button> ';
8704 }
8705 }
8706
8707 // Add a link to set data
8708 if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
8709 if (!is_array($adddateof)) {
8710 $arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
8711 } else {
8712 $arrayofdateof = $adddateof;
8713 }
8714 foreach ($arrayofdateof as $valuedateof) {
8715 $tmpadddateof = empty($valuedateof['adddateof']) ? 0 : $valuedateof['adddateof'];
8716 $tmplabeladddateof = empty($valuedateof['labeladddateof']) ? '' : $valuedateof['labeladddateof'];
8717 $tmparray = dol_getdate($tmpadddateof);
8718 if (empty($tmplabeladddateof)) {
8719 $tmplabeladddateof = $langs->trans("DateInvoice");
8720 }
8721 $reset_scripts = 'console.log(\'Click on now link\'); ';
8722 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($tmpadddateof, 'dayinputnoreduce').'\');';
8723 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
8724 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
8725 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
8726 $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="'.$reset_scripts.'">'.$tmplabeladddateof.'</button>';
8727 }
8728 }
8729
8730 return $retstring;
8731 }
8732
8742 public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array(), $morecss = 'minwidth75 maxwidth100')
8743 {
8744 global $langs;
8745
8746 $TDurationTypes = $this->getDurationTypes($langs);
8747
8748 // Removed undesired duration types
8749 foreach ($excludetypes as $value) {
8750 unset($TDurationTypes[$value]);
8751 }
8752
8753 $retstring = '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
8754 foreach ($TDurationTypes as $key => $typeduration) {
8755 $retstring .= '<option value="' . $key . '"';
8756 if ($key == $selected) {
8757 $retstring .= " selected";
8758 }
8759 $retstring .= ">" . $typeduration . "</option>";
8760 }
8761 $retstring .= "</select>";
8762
8763 $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
8764
8765 return $retstring;
8766 }
8767
8768 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8769
8783 public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
8784 {
8785 // phpcs:enable
8786 global $langs;
8787
8788 $retstring = '<span class="nowraponall">';
8789
8790 $hourSelected = '';
8791 $minSelected = '';
8792
8793 // Hours
8794 if ($iSecond != '') {
8795 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8796
8797 $hourSelected = convertSecondToTime($iSecond, 'allhour');
8798 $minSelected = convertSecondToTime($iSecond, 'min');
8799 }
8800
8801 if ($typehour == 'select') {
8802 $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
8803 for ($hour = 0; $hour < 25; $hour++) { // For a duration, we allow 24 hours
8804 $retstring .= '<option value="' . $hour . '"';
8805 if (is_numeric($hourSelected) && $hourSelected == $hour) {
8806 $retstring .= " selected";
8807 }
8808 $retstring .= ">" . $hour . "</option>";
8809 }
8810 $retstring .= "</select>";
8811 } elseif ($typehour == 'text' || $typehour == 'textselect') {
8812 $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
8813 } else {
8814 return 'BadValueForParameterTypeHour';
8815 }
8816
8817 if ($typehour != 'text') {
8818 $retstring .= ' ' . $langs->trans('HourShort');
8819 } else {
8820 $retstring .= '<span class="">:</span>';
8821 }
8822
8823 // Minutes
8824 if ($minunderhours) {
8825 $retstring .= '<br>';
8826 } else {
8827 if ($typehour != 'text') {
8828 $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
8829 }
8830 }
8831
8832 if ($typehour == 'select' || $typehour == 'textselect') {
8833 $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
8834 $step = getDolGlobalInt('MAIN_DURATION_STEP');
8835 $duration_step = ($step > 0) ? $step : 5;
8836 for ($min = 0; $min <= 59; $min += $duration_step) {
8837 $retstring .= '<option value="' . $min . '"';
8838 if (is_numeric($minSelected) && $minSelected == $min) {
8839 $retstring .= ' selected';
8840 }
8841 $retstring .= '>' . $min . '</option>';
8842 }
8843 $retstring .= "</select>";
8844 } elseif ($typehour == 'text') {
8845 $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
8846 }
8847
8848 if ($typehour != 'text') {
8849 $retstring .= ' ' . $langs->trans('MinuteShort');
8850 }
8851
8852 $retstring .= "</span>";
8853
8854 if (!empty($nooutput)) {
8855 return $retstring;
8856 }
8857
8858 print $retstring;
8859
8860 return '';
8861 }
8862
8882 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)
8883 {
8884 global $langs, $conf;
8885
8886 $out = '';
8887
8888 // check parameters
8889 if (is_null($ajaxoptions)) {
8890 $ajaxoptions = array();
8891 }
8892
8893 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
8894 $placeholder = '';
8895
8896 if ($selected && empty($selected_input_value)) {
8897 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
8898 $tickettmpselect = new Ticket($this->db);
8899 $tickettmpselect->fetch((int) $selected);
8900 $selected_input_value = $tickettmpselect->ref;
8901 unset($tickettmpselect);
8902 }
8903
8904 $urloption = '';
8905 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
8906
8907 if (empty($hidelabel)) {
8908 $out .= $langs->trans("RefOrLabel") . ' : ';
8909 } elseif ($hidelabel > 1) {
8910 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
8911 if ($hidelabel == 2) {
8912 $out .= img_picto($langs->trans("Search"), 'search');
8913 }
8914 }
8915 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
8916 if ($hidelabel == 3) {
8917 $out .= img_picto($langs->trans("Search"), 'search');
8918 }
8919 } else {
8920 $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
8921 }
8922
8923 if (empty($nooutput)) {
8924 print $out;
8925 } else {
8926 return $out;
8927 }
8928 return '';
8929 }
8930
8931
8948 public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
8949 {
8950 global $langs;
8951
8952 $out = '';
8953 $outarray = array();
8954
8955 $selectFields = " p.rowid, p.ref, p.message";
8956
8957 $sql = "SELECT ";
8958 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
8959 $sql .= " FROM " . $this->db->prefix() . "ticket as p";
8960 $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
8961
8962 // Add criteria on ref/label
8963 if ($filterkey != '') {
8964 $sql .= ' AND (';
8965 $prefix = getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if TICKET_DONOTSEARCH_ANYWHERE is on
8966 // For natural search
8967 $search_crit = explode(' ', $filterkey);
8968 $i = 0;
8969 if (count($search_crit) > 1) {
8970 $sql .= "(";
8971 }
8972 foreach ($search_crit as $crit) {
8973 if ($i > 0) {
8974 $sql .= " AND ";
8975 }
8976 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
8977 $sql .= ")";
8978 $i++;
8979 }
8980 if (count($search_crit) > 1) {
8981 $sql .= ")";
8982 }
8983 $sql .= ')';
8984 }
8985
8986 $sql .= $this->db->plimit($limit, 0);
8987
8988 // Build output string
8989 dol_syslog(get_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
8990 $result = $this->db->query($sql);
8991 if ($result) {
8992 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
8993 require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
8994
8995 $num = $this->db->num_rows($result);
8996
8997 $events = array();
8998
8999 if (!$forcecombo) {
9000 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9001 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('TICKET_USE_SEARCH_TO_SELECT'));
9002 }
9003
9004 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9005
9006 $textifempty = '';
9007 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9008 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9009 if (getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9010 if ($showempty && !is_numeric($showempty)) {
9011 $textifempty = $langs->trans($showempty);
9012 } else {
9013 $textifempty .= $langs->trans("All");
9014 }
9015 } else {
9016 if ($showempty && !is_numeric($showempty)) {
9017 $textifempty = $langs->trans($showempty);
9018 }
9019 }
9020 if ($showempty) {
9021 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9022 }
9023
9024 $i = 0;
9025 while ($num && $i < $num) {
9026 $opt = '';
9027 $optJson = array();
9028 $objp = $this->db->fetch_object($result);
9029
9030 $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
9031 '@phan-var-force array{key:string,value:mixed,type:int} $optJson';
9032 // Add new entry
9033 // "key" value of json key array is used by jQuery automatically as selected value
9034 // "label" value of json key array is used by jQuery automatically as text for combo box
9035 $out .= $opt;
9036 array_push($outarray, $optJson);
9037
9038 $i++;
9039 }
9040
9041 $out .= '</select>';
9042
9043 $this->db->free($result);
9044
9045 if (empty($outputmode)) {
9046 return $out;
9047 }
9048 return $outarray;
9049 } else {
9050 dol_print_error($this->db);
9051 }
9052
9053 return array();
9054 }
9055
9067 protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9068 {
9069 $outkey = '';
9070 $outref = '';
9071 $outtype = '';
9072
9073 $outkey = $objp->rowid;
9074 $outref = $objp->ref;
9075
9076 $opt = '<option value="' . $objp->rowid . '"';
9077 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9078 $opt .= '>';
9079 $opt .= $objp->ref;
9080 $objRef = $objp->ref;
9081 if (!empty($filterkey) && $filterkey != '') {
9082 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
9083 }
9084
9085 $opt .= "</option>\n";
9086 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9087 }
9088
9108 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)
9109 {
9110 global $langs, $conf;
9111
9112 $out = '';
9113
9114 // check parameters
9115 if (is_null($ajaxoptions)) {
9116 $ajaxoptions = array();
9117 }
9118
9119 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9120 $placeholder = '';
9121
9122 if ($selected && empty($selected_input_value)) {
9123 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9124 $projecttmpselect = new Project($this->db);
9125 $projecttmpselect->fetch((int) $selected);
9126 $selected_input_value = $projecttmpselect->ref;
9127 unset($projecttmpselect);
9128 }
9129
9130 $urloption = '';
9131 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9132
9133 if (empty($hidelabel)) {
9134 $out .= $langs->trans("RefOrLabel") . ' : ';
9135 } elseif ($hidelabel > 1) {
9136 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9137 if ($hidelabel == 2) {
9138 $out .= img_picto($langs->trans("Search"), 'search');
9139 }
9140 }
9141 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9142 if ($hidelabel == 3) {
9143 $out .= img_picto($langs->trans("Search"), 'search');
9144 }
9145 } else {
9146 $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
9147 }
9148
9149 if (empty($nooutput)) {
9150 print $out;
9151 } else {
9152 return $out;
9153 }
9154 return '';
9155 }
9156
9173 public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9174 {
9175 global $langs, $conf;
9176
9177 $out = '';
9178 $outarray = array();
9179
9180 $selectFields = " p.rowid, p.ref";
9181
9182 $sql = "SELECT ";
9183 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9184 $sql .= " FROM " . $this->db->prefix() . "projet as p";
9185 $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
9186
9187 // Add criteria on ref/label
9188 if ($filterkey != '') {
9189 $sql .= ' AND (';
9190 $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9191 // For natural search
9192 $search_crit = explode(' ', $filterkey);
9193 $i = 0;
9194 if (count($search_crit) > 1) {
9195 $sql .= "(";
9196 }
9197 foreach ($search_crit as $crit) {
9198 if ($i > 0) {
9199 $sql .= " AND ";
9200 }
9201 $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9202 $sql .= "";
9203 $i++;
9204 }
9205 if (count($search_crit) > 1) {
9206 $sql .= ")";
9207 }
9208 $sql .= ')';
9209 }
9210
9211 $sql .= $this->db->plimit($limit, 0);
9212
9213 // Build output string
9214 dol_syslog(get_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
9215 $result = $this->db->query($sql);
9216 if ($result) {
9217 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9218 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
9219
9220 $num = $this->db->num_rows($result);
9221
9222 $events = array();
9223
9224 if (!$forcecombo) {
9225 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9226 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9227 }
9228
9229 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9230
9231 $textifempty = '';
9232 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9233 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9234 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9235 if ($showempty && !is_numeric($showempty)) {
9236 $textifempty = $langs->trans($showempty);
9237 } else {
9238 $textifempty .= $langs->trans("All");
9239 }
9240 } else {
9241 if ($showempty && !is_numeric($showempty)) {
9242 $textifempty = $langs->trans($showempty);
9243 }
9244 }
9245 if ($showempty) {
9246 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9247 }
9248
9249 $i = 0;
9250 while ($num && $i < $num) {
9251 $opt = '';
9252 $optJson = array();
9253 $objp = $this->db->fetch_object($result);
9254
9255 $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
9256 // Add new entry
9257 // "key" value of json key array is used by jQuery automatically as selected value
9258 // "label" value of json key array is used by jQuery automatically as text for combo box
9259 $out .= $opt;
9260 array_push($outarray, $optJson);
9261
9262 $i++;
9263 }
9264
9265 $out .= '</select>';
9266
9267 $this->db->free($result);
9268
9269 if (empty($outputmode)) {
9270 return $out;
9271 }
9272 return $outarray;
9273 } else {
9274 dol_print_error($this->db);
9275 }
9276
9277 return array();
9278 }
9279
9293 protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9294 {
9295 $outkey = '';
9296 $outref = '';
9297 $outtype = '';
9298
9299 $label = $objp->label;
9300
9301 $outkey = $objp->rowid;
9302 $outref = $objp->ref;
9303 $outlabel = $objp->label;
9304 $outtype = $objp->fk_product_type;
9305
9306 $opt = '<option value="' . $objp->rowid . '"';
9307 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9308 $opt .= '>';
9309 $opt .= $objp->ref;
9310 $objRef = $objp->ref;
9311 if (!empty($filterkey) && $filterkey != '') {
9312 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
9313 }
9314
9315 $opt .= "</option>\n";
9316 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9317 }
9318
9319
9340 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())
9341 {
9342 global $langs, $conf;
9343
9344 $out = '';
9345
9346 // check parameters
9347 if (is_null($ajaxoptions)) {
9348 $ajaxoptions = array();
9349 }
9350
9351 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9352 $placeholder = '';
9353
9354 if ($selected && empty($selected_input_value)) {
9355 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9356 $adherenttmpselect = new Adherent($this->db);
9357 $adherenttmpselect->fetch((int) $selected);
9358 $selected_input_value = $adherenttmpselect->ref;
9359 unset($adherenttmpselect);
9360 }
9361
9362 $urloption = '';
9363
9364 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9365
9366 if (empty($hidelabel)) {
9367 $out .= $langs->trans("RefOrLabel") . ' : ';
9368 } elseif ($hidelabel > 1) {
9369 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9370 if ($hidelabel == 2) {
9371 $out .= img_picto($langs->trans("Search"), 'search');
9372 }
9373 }
9374 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9375 if ($hidelabel == 3) {
9376 $out .= img_picto($langs->trans("Search"), 'search');
9377 }
9378 } else {
9379 $filterkey = '';
9380
9381 $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss, $excludeids);
9382 }
9383
9384 if (empty($nooutput)) {
9385 print $out;
9386 } else {
9387 return $out;
9388 }
9389 return '';
9390 }
9391
9409 public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $excludeids = array())
9410 {
9411 global $langs, $conf;
9412
9413 $out = '';
9414 $outarray = array();
9415
9416 $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
9417
9418 $sql = "SELECT ";
9419 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9420 $sql .= " FROM " . $this->db->prefix() . "adherent as p";
9421 $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
9422
9423 // Add criteria on ref/label
9424 if ($filterkey != '') {
9425 $sql .= ' AND (';
9426 $prefix = !getDolGlobalString('MEMBER_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9427 // For natural search
9428 $search_crit = explode(' ', $filterkey);
9429 $i = 0;
9430 if (count($search_crit) > 1) {
9431 $sql .= "(";
9432 }
9433 foreach ($search_crit as $crit) {
9434 if ($i > 0) {
9435 $sql .= " AND ";
9436 }
9437 $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9438 $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
9439 $i++;
9440 }
9441 if (count($search_crit) > 1) {
9442 $sql .= ")";
9443 }
9444 $sql .= ')';
9445 }
9446 if ($status != -1) {
9447 $sql .= ' AND statut = ' . ((int) $status);
9448 }
9449 if (!empty($excludeids)) {
9450 $sql .= " AND p.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
9451 }
9452 $sql .= $this->db->plimit($limit, 0);
9453
9454 // Build output string
9455 dol_syslog(get_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
9456 $result = $this->db->query($sql);
9457 if ($result) {
9458 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9459 require_once DOL_DOCUMENT_ROOT . '/core/lib/member.lib.php';
9460
9461 $num = $this->db->num_rows($result);
9462
9463 $events = array();
9464
9465 if (!$forcecombo) {
9466 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9467 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9468 }
9469
9470 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9471
9472 $textifempty = '';
9473 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9474 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9475 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9476 if ($showempty && !is_numeric($showempty)) {
9477 $textifempty = $langs->trans($showempty);
9478 } else {
9479 $textifempty .= $langs->trans("All");
9480 }
9481 } else {
9482 if ($showempty && !is_numeric($showempty)) {
9483 $textifempty = $langs->trans($showempty);
9484 }
9485 }
9486 if ($showempty) {
9487 $out .= '<option value="-1" selected>' . $textifempty . '</option>';
9488 }
9489
9490 $i = 0;
9491 while ($num && $i < $num) {
9492 $opt = '';
9493 $optJson = array();
9494 $objp = $this->db->fetch_object($result);
9495
9496 $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
9497
9498 // Add new entry
9499 // "key" value of json key array is used by jQuery automatically as selected value
9500 // "label" value of json key array is used by jQuery automatically as text for combo box
9501 $out .= $opt;
9502 array_push($outarray, $optJson);
9503
9504 $i++;
9505 }
9506
9507 $out .= '</select>';
9508
9509 $this->db->free($result);
9510
9511 if (empty($outputmode)) {
9512 return $out;
9513 }
9514 return $outarray;
9515 } else {
9516 dol_print_error($this->db);
9517 }
9518
9519 return array();
9520 }
9521
9533 protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9534 {
9535 $outkey = '';
9536 $outlabel = '';
9537 $outtype = '';
9538
9539 $outkey = $objp->rowid;
9540 $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
9541 $outtype = $objp->fk_adherent_type;
9542
9543 $opt = '<option value="' . $objp->rowid . '"';
9544 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9545 $opt .= '>';
9546 if (!empty($filterkey) && $filterkey != '') {
9547 $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
9548 }
9549 $opt .= $outlabel;
9550 $opt .= "</option>\n";
9551
9552 $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
9553 }
9554
9576 public function selectForForms($objectdesc, $htmlname, $preSelectedValue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '', $objectfield = '')
9577 {
9578 global $conf, $extrafields, $user, $hookmanager, $action;
9579
9580 // Example of common usage for a link to a thirdparty
9581
9582 // We got this in a modulebuilder form of "MyObject" of module "mymodule".
9583 // 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
9584 // $objectdesc = 'Societe'
9585 // $objectfield = Method 1: 'myobject@mymodule:fk_soc' ('fk_soc' is code to retrieve myobject->fields['fk_soc'])
9586 // 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__))" ...)
9587
9588 // We got this when showing an extrafields on resource that is a link to societe
9589 // 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
9590 // $objectdesc = 'Societe'
9591 // $objectfield = Method 1: 'resource:options_link_to_societe'
9592 // Method 2 recommended (it can be the array): array("type"=>'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...)
9593
9594 // With old usage:
9595 // $objectdesc = 'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))'
9596 // $objectfield = ''
9597
9598 //var_dump($objectdesc.' '.$objectfield);
9599 //debug_print_backtrace();
9600
9601 $objectdescorig = $objectdesc;
9602 $objecttmp = null;
9603 $InfoFieldList = array();
9604 $classname = '';
9605 $filter = ''; // Ensure filter has value (for static analysis)
9606 $sortfield = ''; // Ensure filter has value (for static analysis)
9607
9608 if (is_array($objectfield)) { // objectfield is an array
9609 $objectdesc = $objectfield['type'];
9610 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9611 } 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.
9612 // Example: $objectfield = 'product:options_package' or 'myobject@mymodule:options_myfield'
9613 $tmparray = explode(':', $objectfield);
9614
9615 // Get instance of object from $element
9616 $objectforfieldstmp = fetchObjectByElement(0, strtolower($tmparray[0]));
9617
9618 if (is_object($objectforfieldstmp)) {
9619 $objectdesc = '';
9620
9621 $reg = array();
9622 if (preg_match('/^options_(.*)$/', $tmparray[1], $reg)) {
9623 // For a property in extrafields
9624 $key = $reg[1];
9625 // fetch optionals attributes and labels
9626 $extrafields->fetch_name_optionals_label($objectforfieldstmp->table_element);
9627
9628 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key]) && $extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key] == 'link') {
9629 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options'])) {
9630 $tmpextrafields = array_keys($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options']);
9631 $objectdesc = $tmpextrafields[0];
9632 }
9633 }
9634 } else {
9635 // For a property in ->fields
9636 if (array_key_exists($tmparray[1], $objectforfieldstmp->fields)) {
9637 $objectdesc = $objectforfieldstmp->fields[$tmparray[1]]['type'];
9638 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9639 }
9640 }
9641 }
9642 }
9643
9644 if ($objectdesc) {
9645 // Example of value for $objectdesc:
9646 // Bom:bom/class/bom.class.php:0:t.status=1
9647 // Bom:bom/class/bom.class.php:0:t.status=1:ref
9648 // Bom:bom/class/bom.class.php:0:(t.status:=:1) OR (t.field2:=:2):ref
9649 $InfoFieldList = explode(":", $objectdesc, 4);
9650 $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
9651 $reg = array();
9652 if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
9653 $InfoFieldList[4] = $reg[1]; // take the sort field
9654 }
9655 $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp); // take the filter field
9656
9657 $classname = $InfoFieldList[0];
9658 $classpath = empty($InfoFieldList[1]) ? '' : $InfoFieldList[1];
9659 //$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
9660 $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
9661 $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
9662
9663 // Load object according to $id and $element
9664 $objecttmp = fetchObjectByElement(0, strtolower($InfoFieldList[0]));
9665
9666 // Fallback to another solution to get $objecttmp
9667 if (empty($objecttmp) && !empty($classpath)) {
9668 dol_include_once($classpath);
9669
9670 if ($classname && class_exists($classname)) {
9671 $objecttmp = new $classname($this->db);
9672 }
9673 }
9674 }
9675
9676 // Make some replacement in $filter. May not be used if we used the ajax mode with $objectfield. In such a case
9677 // we propagate the $objectfield and not the filter and replacement is done by the ajax/selectobject.php component.
9678 $sharedentities = (is_object($objecttmp) && property_exists($objecttmp, 'element')) ? getEntity($objecttmp->element) : strtolower($classname);
9679 $filter = str_replace(
9680 array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
9681 array($conf->entity, $sharedentities, $user->id),
9682 $filter
9683 );
9684
9685 if (!is_object($objecttmp)) {
9686 dol_syslog('selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc, LOG_WARNING);
9687 return 'selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc;
9688 }
9689 '@phan-var-force CommonObject $objecttmp';
9691 //var_dump($filter);
9692 $prefixforautocompletemode = $objecttmp->element;
9693 if ($prefixforautocompletemode == 'societe') {
9694 $prefixforautocompletemode = 'company';
9695 }
9696 if ($prefixforautocompletemode == 'product') {
9697 $prefixforautocompletemode = 'produit';
9698 }
9699
9700 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9701
9702 dol_syslog(get_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
9703
9704 // Generate the combo HTML component
9705 $out = '';
9706 if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
9707 // No immediate load of all database
9708 $placeholder = '';
9709
9710 if ($preSelectedValue && empty($selected_input_value)) {
9711 $objecttmp->fetch($preSelectedValue);
9712 $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
9713
9714 $oldValueForShowOnCombobox = 0;
9715 foreach ($objecttmp->fields as $fieldK => $fielV) {
9716 if (!array_key_exists('showoncombobox', $fielV) || !$fielV['showoncombobox'] || empty($objecttmp->$fieldK)) {
9717 continue;
9718 }
9719
9720 if (!$oldValueForShowOnCombobox) {
9721 $selected_input_value = '';
9722 }
9723
9724 $selected_input_value .= $oldValueForShowOnCombobox ? ' - ' : '';
9725 $selected_input_value .= $objecttmp->$fieldK;
9726 $oldValueForShowOnCombobox = empty($fielV['showoncombobox']) ? 0 : $fielV['showoncombobox'];
9727 }
9728 }
9729
9730 // Set url and param to call to get json of the search results
9731 $urlforajaxcall = DOL_URL_ROOT . '/core/ajax/selectobject.php';
9732 $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdescorig) . (is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9733 //$urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1'.(is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9734
9735 // Hook 'selectForFormsListUrl' - Added to allow modules to modify the AJAX URL
9736 $parameters = array(
9737 'urloption' => $urloption,
9738 'object' => $objecttmp,
9739 'htmlname' => $htmlname,
9740 'filter' => $filter,
9741 'searchkey' => $searchkey,
9742 );
9743 $reshook = $hookmanager->executeHooks('selectForFormsListUrl', $parameters, $objecttmp, $action);
9744 if (!empty($reshook)) {
9745 $urloption = $hookmanager->resPrint;
9746 $hookmanager->resPrint = '';
9747 }
9748
9749 // Activate the auto complete using ajax call.
9750 $out .= ajax_autocompleter((string) $preSelectedValue, $htmlname, $urlforajaxcall, $urloption, getDolGlobalInt($confkeyforautocompletemode), 0);
9751 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
9752 $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) . '"' : '') . ' />';
9753 } else {
9754 // Immediate load of table record.
9755 $out .= $this->selectForFormsList($objecttmp, $htmlname, $preSelectedValue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
9756 }
9757
9758 return $out;
9759 }
9760
9761
9783 public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '', $sortorder = 'ASC')
9784 {
9785 global $langs, $user, $hookmanager;
9786
9787 //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
9788
9789 $prefixforautocompletemode = $objecttmp->element;
9790 if ($prefixforautocompletemode == 'societe') {
9791 $prefixforautocompletemode = 'company';
9792 }
9793 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9794
9795 $fieldstoshow = '';
9796 if (!empty($objecttmp->fields)) { // For object that declare it, it is better to use declared fields (like societe, contact, ...)
9797 $tmpfieldstoshow = '';
9798 foreach ($objecttmp->fields as $key => $val) {
9799 if (! (int) dol_eval((string) $val['enabled'], 1, 1, '1')) {
9800 continue;
9801 }
9802 if (!empty($val['showoncombobox'])) {
9803 $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
9804 }
9805 }
9806 if ($tmpfieldstoshow) {
9807 $fieldstoshow = $tmpfieldstoshow;
9808 }
9809 } elseif ($objecttmp->element === 'category') {
9810 $fieldstoshow = 't.label';
9811 } else {
9812 // For backward compatibility
9813 $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'visible' => 4, 'showoncombobox' => 1);
9814 }
9815
9816 if (empty($fieldstoshow)) {
9817 if (!empty($objecttmp->parent_element)) {
9818 $fieldstoshow = 'o.ref';
9819 if (empty($sortfield)) {
9820 $sortfield = 'o.ref';
9821 }
9822 if (in_array($objecttmp->element, ['commandedet', 'propaldet', 'facturedet', 'expeditiondet'])) {
9823 $fieldstoshow .= ',p.ref AS p_ref,p.label,t.description';
9824 $sortfield .= ', p.ref';
9825 }
9826 } elseif (isset($objecttmp->fields['ref'])) {
9827 $fieldstoshow = 't.ref';
9828 } else {
9829 $langs->load("errors");
9830 $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
9831 return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
9832 }
9833 }
9834
9835 $out = '';
9836 $outarray = array();
9837 $tmparray = array();
9838
9839 $num = 0;
9840
9841 $sanitizedfieldstoshow = $fieldstoshow;
9842
9843 // Search data
9844 $sql = "SELECT t.rowid, " . $sanitizedfieldstoshow . " FROM " . $this->db->prefix() . $this->db->sanitize($objecttmp->table_element) . " as t";
9845 if (!empty($objecttmp->isextrafieldmanaged)) {
9846 $extrafieldTable = $objecttmp->table_element;
9847 if ($extrafieldTable == 'categorie') {
9848 $extrafieldTable = 'categories'; // For compatibility
9849 }
9850 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($extrafieldTable) . "_extrafields as e ON t.rowid = e.fk_object";
9851 }
9852 if (!empty($objecttmp->parent_element)) { // If parent_element is defined
9853 '@phan-var-force CommonObjectLine $objecttmp';
9854 $parent_properties = getElementProperties($objecttmp->parent_element);
9855 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($parent_properties['table_element']) . " as o ON o.rowid = t.".$objecttmp->fk_parent_attribute;
9856 }
9857 if (!empty($objecttmp->parent_element) && in_array($objecttmp->parent_element, ['commande', 'propal', 'facture', 'expedition'])) {
9858 $sql .= " LEFT JOIN " . $this->db->prefix() . "product as p ON p.rowid = t.fk_product";
9859 }
9860 if (!empty($objecttmp->ismultientitymanaged)) {
9861 if ($objecttmp->ismultientitymanaged == 1) { // @phan-suppress-current-line PhanPluginEmptyStatementIf
9862 // No need to join/link another table
9863 }
9864 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9865 $tmparray = explode('@', $objecttmp->ismultientitymanaged);
9866 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($tmparray[1]) . " as parenttable ON parenttable.rowid = t." . $this->db->sanitize($tmparray[0]);
9867 }
9868 }
9869
9870 // Add where from hooks
9871 $parameters = array(
9872 'object' => $objecttmp,
9873 'htmlname' => $htmlname,
9874 'filter' => $filter,
9875 'searchkey' => $searchkey
9876 );
9877
9878 $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
9879 if (!empty($hookmanager->resPrint)) {
9880 $sql .= $hookmanager->resPrint;
9881 } else {
9882 $sql .= " WHERE 1=1";
9883
9884 // If table need a multientity restriction
9885 if (!empty($objecttmp->ismultientitymanaged)) {
9886 if ($objecttmp->ismultientitymanaged == 1) {
9887 $sql .= " AND t.entity IN (" . getEntity($objecttmp->element) . ")";
9888 }
9889 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9890 $sql .= " AND parenttable.entity = t." . $this->db->sanitize($tmparray[0]);
9891 }
9892 // If the parent table is llx_societe and user is not an external user (a more robust test done later for external users),
9893 // then we must also check that user has permissions
9894 if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
9895 if (!$user->hasRight('societe', 'client', 'voir') && empty($user->socid)) {
9896 $sql .= " AND EXISTS (SELECT sc.rowid FROM ".$this->db->prefix() . "societe_commerciaux as sc";
9897 $sql .= " WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $user->id).")";
9898 }
9899 }
9900 }
9901
9902 // If user is external user, we must also make a test on llx_societe_commerciaux
9903 if (!empty($user->socid)) {
9904 if ($objecttmp->element == 'societe') {
9905 $sql .= " AND t.rowid = " . ((int) $user->socid);
9906 } elseif (!empty($objecttmp->fields['fk_soc']) || !empty($objecttmp->fields['t.fk_soc']) || property_exists($objecttmp, 'fk_soc') || property_exists($objecttmp, 'socid')) {
9907 $sql .= " AND t.fk_soc = " . ((int) $user->socid);
9908 }
9909 }
9910
9911 $splittedfieldstoshow = explode(',', $fieldstoshow);
9912 foreach ($splittedfieldstoshow as &$field2) {
9913 if (is_numeric($pos = strpos($field2, ' '))) {
9914 $field2 = substr($field2, 0, $pos);
9915 }
9916 }
9917 if ($searchkey != '') {
9918 $sql .= natural_search($splittedfieldstoshow, $searchkey);
9919 }
9920
9921 if ($filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:>:'20160101')"
9922 $errormessage = '';
9923 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
9924 if ($errormessage) {
9925 return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
9926 }
9927 }
9928 }
9929 $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, $sortorder);
9930 //$sql.=$this->db->plimit($limit, 0);
9931 //print $sql;
9932
9933 // Build output string
9934 $resql = $this->db->query($sql);
9935 if ($resql) {
9936 // Construct $out and $outarray
9937 $out .= '<select id="' . $htmlname . '" class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
9938
9939 // 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
9940 $textifempty = '&nbsp;';
9941
9942 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9943 if (getDolGlobalInt($confkeyforautocompletemode)) {
9944 if ($showempty && !is_numeric($showempty)) {
9945 $textifempty = $langs->trans($showempty);
9946 } else {
9947 $textifempty .= $langs->trans("All");
9948 }
9949 }
9950 if ($showempty) {
9951 $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
9952 }
9953
9954 $num = $this->db->num_rows($resql);
9955 $i = 0;
9956 if ($num) {
9957 while ($i < $num) {
9958 $obj = $this->db->fetch_object($resql);
9959 $label = '';
9960 $labelhtml = '';
9961 $tmparray = explode(',', $fieldstoshow);
9962 $oldvalueforshowoncombobox = 0;
9963 foreach ($tmparray as $key => $val) {
9964 $val = preg_replace('/(t|p|o)\./', '', $val);
9965 $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
9966 $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
9967 $label .= $obj->$val;
9968 $labelhtml .= $obj->$val;
9969
9970 $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
9971 }
9972 if (empty($outputmode)) {
9973 if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
9974 $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>';
9975 } else {
9976 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
9977 }
9978 } else {
9979 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
9980 }
9981
9982 $i++;
9983 if (($i % 10) == 0) {
9984 $out .= "\n";
9985 }
9986 }
9987 }
9988
9989 $out .= '</select>' . "\n";
9990
9991 if (!$forcecombo) {
9992 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9993 $out .= ajax_combobox($htmlname, array(), getDolGlobalInt($confkeyforautocompletemode, 0));
9994 }
9995 } else {
9996 dol_print_error($this->db);
9997 }
9998
9999 $this->result = array('nbofelement' => $num);
10000
10001 if ($outputmode) {
10002 return $outarray;
10003 }
10004 return $out;
10005 }
10006
10017 public static function radio($htmlName, $radioItems, $selected = '', $moreGlobalParams = [])
10018 {
10019 // Default parameters for each radio input
10020 $defaultParams = [
10021 'disabled' => false,
10022 'attr' => [
10023 'type' => 'radio',
10024 'name' => $htmlName,
10025 ],
10026 'unescapedAttr' => [],
10027 'attrLabel' => [],
10028 'unescapedAttrLabel' => [],
10029 'labelIsHtml' => false
10030 ];
10031
10032 // Merge global parameters with defaults
10033 $params = array_merge_recursive_distinct($defaultParams, $moreGlobalParams);
10034
10035 $out = '';
10036 if (!empty($radioItems)) {
10037 foreach ($radioItems as $key => $item) {
10038 // Normalize item to array structure if it's a simple string
10039 if (!is_array($item)) {
10040 $item = [
10041 'attr' => [
10042 'value' => $key,
10043 ],
10044 'label' => $item
10045 ];
10046 }
10047
10048 // Default properties for individual item
10049 $defaultItem = [
10050 'attr' => [
10051 'value' => !isset($item['attr']['value']) ? $key : '',
10052 ],
10053 'label' => '',
10054 ];
10055
10056 // Merge defaults with global params and item-specific properties
10057 $defaultItem = array_merge_recursive_distinct($params, $defaultItem);
10058 $item = array_merge_recursive_distinct($defaultItem, $item);
10059
10060 // Determine if this radio should be checked
10061 if ((is_array($selected) && in_array($item['attr']['value'], $selected, true)) || $selected === $item['attr']['value']) {
10062 $item['attr']['checked'] = true;
10063 }
10064
10065 // Build HTML attributes for input and label
10066 $inputAttributes = implode(' ', commonHtmlAttributeBuilder($item['attr'], $item['unescapedAttr']));
10067 $labelAttributes = implode(' ', commonHtmlAttributeBuilder($item['attrLabel'], $item['unescapedAttrLabel']));
10068
10069 // prevent accidental Xss todo : escape $item['label'] but html friendly compatible
10070 $text = $item['labelIsHtml'] ? $item['label'] : htmlspecialchars($item['label'], ENT_QUOTES | ENT_SUBSTITUTE);
10071
10072 // Generate HTML
10073 $out .= '<label ' . $labelAttributes . '><input ' . $inputAttributes . ' /> ' . $text . '</label> ';
10074 }
10075 }
10076
10077 return $out;
10078 }
10079
10080
10104 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)
10105 {
10106 global $conf, $langs;
10107
10108 // Do we want a multiselect ?
10109 //$jsbeautify = 0;
10110 //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
10111 $jsbeautify = 1;
10112
10113 if ($value_as_key) {
10114 $array = array_combine($array, $array);
10115 }
10116
10117 '@phan-var-force array{label:string,data-html:string,disable?:int<0,1>,css?:string} $array'; // Array combine breaks information
10118
10119 $out = '';
10120
10121 if ($addjscombo < 0) {
10122 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10123 $addjscombo = 1;
10124 } else {
10125 $addjscombo = 0;
10126 }
10127 }
10128 $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
10129 $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . ' selectformat"';
10130 $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
10131 $out .= '>'."\n";
10132
10133 if ($show_empty) {
10134 $textforempty = ' ';
10135 if (!empty($conf->use_javascript_ajax)) {
10136 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
10137 }
10138 if (!is_numeric($show_empty)) {
10139 $textforempty = $show_empty;
10140 }
10141 $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . (((int) $show_empty) < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
10142 }
10143 if (is_array($array)) {
10144 // Translate
10145 if ($translate) {
10146 foreach ($array as $key => $value) {
10147 if (!is_array($value)) {
10148 $array[$key] = $langs->trans($value);
10149 } else {
10150 $array[$key]['label'] = $langs->trans($value['label']);
10151 }
10152 }
10153 }
10154 // Sort
10155 if ($sort == 'ASC') {
10156 asort($array);
10157 } elseif ($sort == 'DESC') {
10158 arsort($array);
10159 }
10160
10161 foreach ($array as $key => $tmpvalue) {
10162 if (is_array($tmpvalue)) {
10163 $value = $tmpvalue['label'];
10164 //$valuehtml = empty($tmpvalue['data-html']) ? $value : $tmpvalue['data-html'];
10165 $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
10166 $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
10167 } else {
10168 $value = $tmpvalue;
10169 //$valuehtml = $tmpvalue;
10170 $disabled = '';
10171 $style = '';
10172 }
10173 if (!empty($disablebademail)) {
10174 if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
10175 || ($disablebademail == 2 && preg_match('/---/', $value))) {
10176 $disabled = ' disabled';
10177 $style = ' class="warning"';
10178 }
10179 }
10180 if ($key_in_label) {
10181 if (empty($nohtmlescape)) {
10182 $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
10183 } else {
10184 $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
10185 }
10186 } else {
10187 if (empty($nohtmlescape)) {
10188 $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
10189 } else {
10190 $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
10191 }
10192 if ($value == '' || $value == '-') {
10193 $selectOptionValue = '&nbsp;';
10194 }
10195 }
10196 $out .= '<option value="' . $key . '"';
10197 $out .= $style . $disabled;
10198 $out .= is_array($tmpvalue) && !empty($tmpvalue['parent']) ? ' parent="' . dolPrintHTMLForAttribute($tmpvalue['parent']) . '"' : '';
10199 if (is_array($id)) {
10200 if (in_array($key, $id) && !$disabled) {
10201 $out .= ' selected'; // To preselect a value
10202 }
10203 } else {
10204 $id = (string) $id; // if $id = 0, then $id = '0'
10205 if ($id != '' && (($id == (string) $key) || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
10206 $out .= ' selected'; // To preselect a value
10207 }
10208 }
10209
10210 if (is_array($tmpvalue)) {
10211 foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
10212 if ($keyforvalue == 'labelhtml') {
10213 $keyforvalue = 'data-html';
10214 }
10215 if (preg_match('/^data-/', $keyforvalue)) { // The best solution if you want to use HTML values into the list is to use data-html.
10216 $out .= ' '.dol_escape_htmltag($keyforvalue).'="'.dol_escape_htmltag($valueforvalue).'"';
10217 }
10218 }
10219 } elseif (!empty($nohtmlescape)) { // deprecated. Use instead the previous cas, an array with 'data-html', 'data-xxx' ... to use HTML content in the select
10220 $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
10221 }
10222
10223 $out .= '>';
10224 $out .= $selectOptionValue;
10225 $out .= "</option>\n";
10226 }
10227 }
10228 $out .= "</select>";
10229
10230 // Add code for jquery to use multiselect
10231 if ($addjscombo && $jsbeautify) {
10232 // Enhance with select2
10233 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10234 $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', (((int) $show_empty) < 0 ? (string) $show_empty : '-1'), $morecss);
10235 }
10236
10237 return $out;
10238 }
10239
10258 public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
10259 {
10260 global $conf;
10261 global $delayedhtmlcontent; // Will be used later outside of this function
10262
10263 // TODO Use an internal dolibarr component instead of select2
10264 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10265 return '';
10266 }
10267
10268 $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
10269
10270 $outdelayed = '';
10271 if (!empty($conf->use_javascript_ajax)) {
10272 $tmpplugin = 'select2';
10273 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10274 <script nonce="' . getNonce() . '">
10275 $(document).ready(function () {
10276
10277 ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
10278
10279 $(".' . $htmlname . '").select2({
10280 ajax: {
10281 dir: "ltr",
10282 url: "' . $url . '",
10283 dataType: \'json\',
10284 delay: 250,
10285 data: function (params) {
10286 return {
10287 q: params.term, // search term
10288 page: params.page
10289 }
10290 },
10291 processResults: function (data) {
10292 // parse the results into the format expected by Select2.
10293 // since we are using custom formatting functions we do not need to alter the remote JSON data
10294 //console.log(data);
10295 saveRemoteData = data;
10296 /* format json result for select2 */
10297 result = []
10298 $.each( data, function( key, value ) {
10299 result.push({id: key, text: value.text});
10300 });
10301 //return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
10302 //console.log(result);
10303 return {results: result, more: false}
10304 },
10305 cache: true
10306 },
10307 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10308 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10309 placeholder: \'' . dol_escape_js($placeholder) . '\',
10310 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10311 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10312 formatResult: function (result, container, query, escapeMarkup) {
10313 return escapeMarkup(result.text);
10314 },
10315 });
10316
10317 ' . ($callurlonselect ? '
10318 /* Code to execute a GET when we select a value */
10319 $(".' . $htmlname . '").change(function() {
10320 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10321 console.log("We select in selectArrayAjax the entry "+selected)
10322 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10323 $.each( saveRemoteData, function( key, value ) {
10324 if (key == selected)
10325 {
10326 console.log("selectArrayAjax - Do a redirect to "+value.url)
10327 location.assign(value.url);
10328 }
10329 });
10330 });' : '') . '
10331
10332 });
10333 </script>';
10334 }
10335
10336 if ($acceptdelayedhtml) {
10337 $delayedhtmlcontent .= $outdelayed;
10338 } else {
10339 $out .= $outdelayed;
10340 }
10341 return $out;
10342 }
10343
10363 public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
10364 {
10365 global $conf;
10366 global $delayedhtmlcontent; // Will be used later outside of this function
10367
10368 // TODO Use an internal dolibarr component instead of select2
10369 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10370 return '';
10371 }
10372
10373 $out = '<select type="text"'.($textfortitle ? ' title="'.dol_escape_htmltag($textfortitle).'"' : '').' id="'.$htmlname.'" class="'.$htmlname.($morecss ? ' ' . $morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.'"><option></option></select>';
10374
10375 $formattedarrayresult = array();
10376
10377 foreach ($array as $key => $value) {
10378 $o = new stdClass();
10379 $o->id = $key;
10380 $o->text = $value['text'];
10381 $o->url = $value['url'];
10382 $formattedarrayresult[] = $o;
10383 }
10384
10385 $outdelayed = '';
10386 if (!empty($conf->use_javascript_ajax)) {
10387 $tmpplugin = 'select2';
10388 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10389 <script nonce="' . getNonce() . '">
10390 $(document).ready(function () {
10391 var data = ' . json_encode($formattedarrayresult) . ';
10392
10393 ' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
10394
10395 $(\'.' . dol_escape_js($htmlname) . '\').select2({
10396 data: data,
10397 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10398 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10399 placeholder: \'' . dol_escape_js($placeholder) . '\',
10400 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10401 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10402 formatResult: function (result, container, query, escapeMarkup) {
10403 return escapeMarkup(result.text);
10404 },
10405 matcher: function (params, data) {
10406
10407 if(! data.id) return null;';
10408
10409 if ($callurlonselect) {
10410 // We forge the url with 'sall='
10411 $outdelayed .= '
10412
10413 var urlBase = data.url;
10414 var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
10415 /* console.log("params.term="+params.term); */
10416 /* console.log("params.term encoded="+encodeURIComponent(params.term)); */
10417 saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
10418 }
10419
10420 if (!$disableFiltering) {
10421 $outdelayed .= '
10422
10423 if(data.text.match(new RegExp(params.term))) {
10424 return data;
10425 }
10426
10427 return null;';
10428 } else {
10429 $outdelayed .= '
10430
10431 return data;';
10432 }
10433
10434 $outdelayed .= '
10435 }
10436 });
10437
10438 ' . ($callurlonselect ? '
10439 /* Code to execute a GET when we select a value */
10440 $(\'.' . dol_escape_js($htmlname) . '\').change(function() {
10441 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10442 console.log("We select "+selected)
10443
10444 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10445 $.each( saveRemoteData, function( key, value ) {
10446 if (key == selected)
10447 {
10448 console.log("selectArrayFilter - Do a redirect to "+value.url)
10449 location.assign(value.url);
10450 }
10451 });
10452 });' : '') . '
10453
10454 });
10455 </script>';
10456 }
10457
10458 if ($acceptdelayedhtml) {
10459 $delayedhtmlcontent .= $outdelayed;
10460 } else {
10461 $out .= $outdelayed;
10462 }
10463 return $out;
10464 }
10465
10484 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)
10485 {
10486 global $conf, $langs;
10487 $out = '';
10488
10489 if ($addjscombo < 0) {
10490 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10491 $addjscombo = 1;
10492 } else {
10493 $addjscombo = 0;
10494 }
10495 }
10496
10497 $useenhancedmultiselect = 0;
10498 if (!empty($conf->use_javascript_ajax) && !defined('MAIN_DO_NOT_USE_JQUERY_MULTISELECT') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
10499 if ($addjscombo) {
10500 $useenhancedmultiselect = 1; // Use the js multiselect in one line. Possible only if $addjscombo not 0.
10501 }
10502 }
10503
10504 $out .= '<span class="multiselectarray'.$htmlname.'">';
10505
10506 // We need a hidden field because when using the multiselect, if we unselect all, there is no
10507 // variable submitted at all, so no way to make a difference between variable not submitted and variable
10508 // submitted to nothing.
10509 $out .= '<input type="hidden" name="'.$htmlname.'_multiselect" value="1">';
10510 // Output select component
10511 $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";
10512 if (is_array($array) && !empty($array)) {
10513 if ($value_as_key) {
10514 $array = array_combine($array, $array);
10515 }
10516
10517 if (!empty($array)) {
10518 foreach ($array as $key => $value) {
10519 $tmpkey = $key;
10520 $tmplabel = $value;
10521 $tmplabelhtml = '';
10522 $tmpcolor = '';
10523 $tmppicto = '';
10524 $tmpdisabled = '';
10525 if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
10526 $tmpkey = $value['id'];
10527 $tmplabel = empty($value['label']) ? '' : $value['label'];
10528 $tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
10529 $tmpcolor = empty($value['color']) ? '' : $value['color'];
10530 $tmppicto = empty($value['picto']) ? '' : $value['picto'];
10531 $tmpdisabled = empty($value['disabled']) ? '' : $value['disabled'];
10532 }
10533 $newval = ($translate ? $langs->trans($tmplabel) : $tmplabel);
10534 $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
10535
10536 $tmplabelhtml = ($translate ? $langs->trans($tmplabelhtml) : $tmplabelhtml);
10537 $tmplabelhtml = ($key_in_label ? $tmpkey . ' - ' . $tmplabelhtml : $tmplabelhtml);
10538
10539 $out .= '<option value="' . $tmpkey . '"';
10540 if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
10541 $out .= ' selected';
10542 }
10543 $out .= is_array($value) && array_key_exists('parent', $value) && !empty($value['parent']) ? ' parent="' . dolPrintHTMLForAttribute($value['parent']) . '"' : '';
10544 if ($tmpdisabled) {
10545 $out .= ' disabled="disabled"';
10546 }
10547 if (!empty($tmplabelhtml)) {
10548 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10549 } else {
10550 $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
10551 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10552 }
10553 $out .= '>';
10554 $out .= dol_htmlentitiesbr($newval);
10555 $out .= '</option>' . "\n";
10556 }
10557 }
10558 }
10559 $out .= '</select>' . "\n";
10560
10561 $out .= '</span>';
10562
10563 // Add code for jquery to use multiselect
10564 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT')) {
10565 $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
10566 $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
10567 if ($addjscombo == 1) {
10568 $tmpplugin = getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT', (defined('REQUIRE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : 'select2'));
10569
10570 // If property data-html set, we decode html entities and use this.
10571 // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
10572 // TODO Move this into common js ?
10573 $out .= 'function formatResult(record, container) {' . "\n";
10574 $out .= ' if ($(record.element).attr("data-html") != undefined && typeof htmlEntityDecodeJs === "function") {';
10575 $out .= ' return htmlEntityDecodeJs($(record.element).attr("data-html"));';
10576 $out .= ' }'."\n";
10577 $out .= ' return record.text;';
10578 $out .= '}' . "\n";
10579
10580 $out .= 'function formatSelection(record) {' . "\n";
10581 $out .= ' return record.text;';
10582 $out .= '}' . "\n";
10583
10584 // Load the select2 enhancer
10585 //$out .= 'console.log(\'addjscombo=1 for htmlname=' . dol_escape_js($htmlname) . '\');';
10586 $out .= '$(document).ready(function () {
10587 $(\'#' . dol_escape_js($htmlname) . '\').' . $tmpplugin . '({';
10588 if ($placeholder) {
10589 $out .= '
10590 placeholder: {
10591 id: \'-1\',
10592 text: \''.dol_escape_js($placeholder).'\'
10593 },';
10594 }
10595 $out .= ' dir: \'ltr\',
10596 containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
10597 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. */
10598 // Specify format function for dropdown item
10599 formatResult: formatResult,
10600 templateResult: formatResult, /* For 4.0 */
10601 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10602 // Specify format function for selected item
10603 formatSelection: formatSelection,
10604 templateSelection: formatSelection, /* For 4.0 */
10605 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage
10606 });
10607
10608 /* Add also morecss to the css .select2 that is after the #htmlname, for component that are shown dynamically after load, because select2 set
10609 the size only if component is not hidden by default on load */
10610 $(\'#' . dol_escape_js($htmlname) . ' + .select2\').addClass(\'' . dol_escape_js($morecss) . '\');
10611 });' . "\n";
10612 } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
10613 // Add other js lib
10614 // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
10615 // ...
10616 $out .= 'console.log(\'addjscombo=2 for htmlname=' . dol_escape_js($htmlname) . '\');';
10617 $out .= '$(document).ready(function () {
10618 $(\'#' . dol_escape_js($htmlname) . '\').multiSelect({
10619 containerHTML: \'<div class="multi-select-container">\',
10620 menuHTML: \'<div class="multi-select-menu">\',
10621 buttonHTML: \'<span class="multi-select-button ' . dol_escape_js($morecss) . '">\',
10622 menuItemHTML: \'<label class="multi-select-menuitem">\',
10623 activeClass: \'multi-select-container--open\',
10624 noneText: \'' . dol_escape_js($placeholder) . '\'
10625 });
10626 })';
10627 }
10628 $out .= '</script>';
10629 }
10630
10631 return $out;
10632 }
10633
10634
10648 public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '', $draganddrop = 0)
10649 {
10650 global $conf, $langs, $user;
10651
10652 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10653 return '';
10654 }
10655 if (empty($array)) {
10656 return '';
10657 }
10658
10659 $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
10660
10661 if (!empty($user->conf->$tmpvar)) { // A list of fields was already customized for user
10662 $tmparray = explode(',', $user->conf->$tmpvar);
10663 foreach ($array as $key => $val) {
10664 //var_dump($key);
10665 //var_dump($tmparray);
10666 if (in_array($key, $tmparray)) {
10667 $array[$key]['checked'] = 1;
10668 } else {
10669 $array[$key]['checked'] = 0;
10670 }
10671 }
10672 } else { // There is no list of fields already customized for user
10673 foreach ($array as $key => $val) {
10674 if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
10675 $array[$key]['checked'] = 0;
10676 }
10677 }
10678 }
10679
10680 $listoffieldsforselection = '';
10681 $listcheckedstring = '';
10682
10683 foreach ($array as $key => $val) {
10684 // var_dump($val);
10685 // var_dump(array_key_exists('enabled', $val));
10686 // var_dump(!$val['enabled']);
10687 if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
10688 unset($array[$key]); // We don't want this field
10689 continue;
10690 }
10691 if (!empty($val['type']) && $val['type'] == 'separate') {
10692 // Field remains in array but we don't add it into $listoffieldsforselection
10693 //$listoffieldsforselection .= '<li>-----</li>';
10694 continue;
10695 }
10696 if (!empty($val['label']) && $val['label']) {
10697 if (!empty($val['langfile']) && is_object($langs)) {
10698 $langs->load($val['langfile']);
10699 }
10700
10701 // Note: $val['checked'] <> 0 means we must show the field into the combo list @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10702 $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']).'" />';
10703 $listoffieldsforselection .= '<label for="checkbox' . $key . '" class="paddingleft">';
10704 $listoffieldsforselection .= dolPrintHTML(dol_string_nohtmltag($langs->trans($val['label'])));
10705 $listoffieldsforselection .= '</label>';
10706 if (!empty($draganddrop)) {
10707 $listoffieldsforselection .= img_picto($langs->trans("MoveField", !empty($key) ? $key : 'none'), 'grip_title', 'class="opacitymedium boxhandle hideonsmartphone cursormove marginleftonly"');
10708 }
10709 $listoffieldsforselection .='</li>';
10710 $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
10711 }
10712 }
10713
10714 $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
10715
10716 <dl class="dropdown">
10717 <dt>
10718 <a href="#' . $htmlname . '" class="multiselectpicto">
10719 ' . img_picto('', 'list') . '
10720 </a>
10721 <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
10722 </dt>
10723 <dd class="dropdowndd">
10724 <div class="multiselectcheckbox'.$htmlname.'">
10725 <ul class="'.$htmlname.(((string) $pos == '1' || (string) $pos == 'left') ? 'left' : '').(!empty($draganddrop) ? ' sortable' : '').'">
10726 <li class="liinputsearch">
10727 <input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="'.$langs->trans('Search').'">
10728 </li>
10729 '.$listoffieldsforselection.'
10730 </ul>
10731 </div>
10732 </dd>
10733 </dl>
10734
10735 <script>
10736 function updateFieldOrder() {
10737 var positionfields = $(".sortable").sortable("toArray");
10738 $.ajax({
10739 url: \''.DOL_URL_ROOT.'/core/ajax/changepositionfields.php?positionfields=\'+positionfields+\'&token='.newToken().'&action=listafterchangingpositionfields&contextpage='.$varpage.'&userid='.$user->id.'\',
10740 async: false,
10741 success: function () {
10742 // reload page
10743 window.location.href = "'.$_SERVER["PHP_SELF"].'";
10744 }
10745 });
10746 }
10747 $( ".sortable" ).sortable({
10748 handle: \'.boxhandle\',
10749 revert: \'invalid\',
10750 items: \'.fieldsortable\',
10751 stop: function(event, ui) {
10752 console.log("We moved box so we call updateBoxOrder with ajax actions");
10753 updateFieldOrder(); /* 1 to avoid message after a move */
10754 }
10755 });
10756 </script>
10757
10758 <script nonce="' . getNonce() . '" type="text/javascript">
10759 jQuery(document).ready(function () {
10760 $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on("click", function () {
10761 console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
10762
10763 $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\'); // Update field so we know we changed something on selected fields after POST
10764
10765 var title = $(this).val() + ",";
10766 if ($(this).is(\':checked\')) {
10767 $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
10768 }
10769 else {
10770 $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
10771 }
10772 // Now, we submit page
10773 //$(this).parents(\'form:first\').submit();
10774 });
10775
10776 $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
10777 console.log("keyup on inputsearch_dropdownselectedfields");
10778 var value = $(this).val().toLowerCase();
10779 $(\'.multiselectcheckbox'.$htmlname.' li > label\').filter(function() {
10780 $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
10781 });
10782 });
10783 ';
10784 if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') {
10785 $out .= '
10786 $(".dropdown dt a").on("click", function () {
10787 console.log("Click on dropdown, we set focus to search field");
10788 setTimeout(() => { $(\'.inputsearch_dropdownselectedfields\').focus(); }, 200);
10789 });';
10790 }
10791 $out .= '
10792 });
10793 </script>
10794
10795 ';
10796 return $out;
10797 }
10798
10808 public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
10809 {
10810 global $conf;
10811
10812 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
10813
10814 $cat = new Categorie($this->db);
10815 $categories = $cat->containing($id, $type);
10816
10817 if ($rendermode == 1 || $rendermode == 2) {
10818 $toprint = array();
10819 foreach ($categories as $c) {
10820 $ways = $c->print_all_ways('auto', ($nolink ? 'none' : ''), 0, 1, ($rendermode == 2 ? 0 : 1)); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
10821 foreach ($ways as $way) {
10822 $color = $c->color;
10823 $sfortag = '<li class="select2-search-choice-dolibarr noborderoncategories'.(empty($toprint) ? ' nomarginleft' : '');
10824 $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.
10825 if ($c->color && colorIsLight($c->color)) {
10826 $forced_color = 'categtextblack';
10827 }
10828 $sfortag .= ' '.$forced_color;
10829 $sfortag .= '"';
10830 $sfortag .= ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"');
10831 $titlestring = $ways[0];
10832 $titlestring = str_replace('>', ' - ', dol_string_nohtmltag($titlestring));
10833 $sfortag .= ' title="' . dolPrintHTMLForAttribute($titlestring) . '"';
10834 $sfortag .= '>';
10835 if ($rendermode == 1) {
10836 $sfortag .= '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.((int) $c->id).'&type='.urlencode($c->type).'" class="'.$forced_color.'">';
10837 $sfortag .= img_picto('', 'category', 'class="paddingright"');
10838 if ($conf->dol_optimize_smallscreen) {
10839 $sfortag .= dolPrintHTML(dol_trunc($c->label, 8));
10840 } else {
10841 $sfortag .= dolPrintHTML($c->label);
10842 }
10843 $sfortag .= '</a>';
10844 } else {
10845 $sfortag .= $way;
10846 }
10847 $sfortag .= '</li>';
10848
10849 $toprint[] = $sfortag; // Add tag in list of tag to show
10850 }
10851 }
10852 if (empty($toprint)) {
10853 return '';
10854 } else {
10855 return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
10856 }
10857 }
10858
10859 if ($rendermode == 0) {
10860 $arrayselected = array();
10861 $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 3);
10862 foreach ($categories as $c) {
10863 $arrayselected[(string) $c->id] = (string) $c->id;
10864 }
10865
10866 return $this->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, '', 0, '100%', 'disabled', 'category');
10867 }
10868
10869 return 'ErrorBadValueForParameterRenderMode'; // Should not happened
10870 }
10871
10881 public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = array(), $title = 'RelatedObjects')
10882 {
10883 global $conf, $langs, $hookmanager;
10884 global $action;
10885 global $db, $user; // Will be used into tpl
10886
10887 dol_syslog(__METHOD__, LOG_DEBUG);
10888
10889 $object->fetchObjectLinked();
10890
10891 // Bypass the default method
10892 $hookmanager->initHooks(array('commonobject'));
10893 $parameters = array(
10894 'morehtmlright' => $morehtmlright,
10895 'compatibleImportElementsList' => &$compatibleImportElementsList,
10896 );
10897 $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
10898
10899 $nbofdifferenttypes = count($object->linkedObjects);
10900
10901 if (empty($reshook)) {
10902 print '<!-- showLinkedObjectBlock -->';
10903 print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, '', 'showlinkedobjectblock');
10904
10905
10906 print '<div class="div-table-responsive-no-min">';
10907 print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '" data-elementid="' . $object->id . '" >';
10908
10909 print '<tr class="liste_titre">';
10910 print '<td>' . $langs->trans("Type") . '</td>';
10911 print '<td>' . $langs->trans("Ref") . '</td>';
10912 print '<td></td>';
10913 print '<td></td>';
10914 print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
10915 print '<td class="right">' . $langs->trans("Status") . '</td>';
10916 print '<td></td>';
10917 print '</tr>';
10918
10919 $nboftypesoutput = 0;
10920
10921 foreach ($object->linkedObjects as $objecttype => $objects) {
10922 $tplpath = $element = $subelement = $objecttype;
10923
10924 // to display import button on tpl
10925 global $showImportButton; // Will be used into tpl
10926 $showImportButton = false;
10927 if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
10928 $showImportButton = true;
10929 }
10930
10931 $regs = array();
10932
10933 if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
10934 $element = $regs[1];
10935 $subelement = $regs[2];
10936 $tplpath = $element . '/' . $subelement;
10937 }
10938 $tplname = 'linkedobjectblock';
10939
10940 // If we ask a resource form external module (instead of default path)
10941 if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) { // 'myobject@mymodule'
10942 $element = $regs[1];
10943 $module = $regs[2];
10944 $tplpath = $module. '/' . $element;
10945 $tplname = $tplname.'_'.$element;
10946 }
10947
10948 // To work with non standard path
10949 if ($objecttype == 'facture') {
10950 $tplpath = 'compta/' . $element;
10951 if (!isModEnabled('invoice')) {
10952 continue; // Do not show if module disabled
10953 }
10954 } elseif ($objecttype == 'facturerec') {
10955 $tplpath = 'compta/facture';
10956 $tplname = 'linkedobjectblockForRec';
10957 if (!isModEnabled('invoice')) {
10958 continue; // Do not show if module disabled
10959 }
10960 } elseif ($objecttype == 'propal') {
10961 $tplpath = 'comm/' . $element;
10962 if (!isModEnabled('propal')) {
10963 continue; // Do not show if module disabled
10964 }
10965 } elseif ($objecttype == 'supplier_proposal') {
10966 if (!isModEnabled('supplier_proposal')) {
10967 continue; // Do not show if module disabled
10968 }
10969 } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
10970 $tplpath = 'expedition';
10971 if (!isModEnabled('shipping')) {
10972 continue; // Do not show if module disabled
10973 }
10974 } elseif ($objecttype == 'reception') {
10975 $tplpath = 'reception';
10976 if (!isModEnabled('reception')) {
10977 continue; // Do not show if module disabled
10978 }
10979 } elseif ($objecttype == 'delivery') {
10980 $tplpath = 'delivery';
10981 if (!getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) {
10982 continue; // Do not show if sub module disabled
10983 }
10984 } elseif ($objecttype == 'ficheinter') {
10985 $tplpath = 'fichinter';
10986 if (!isModEnabled('intervention')) {
10987 continue; // Do not show if module disabled
10988 }
10989 } elseif ($objecttype == 'invoice_supplier') {
10990 $tplpath = 'fourn/facture';
10991 } elseif ($objecttype == 'order_supplier') {
10992 $tplpath = 'fourn/commande';
10993 } elseif ($objecttype == 'expensereport') {
10994 $tplpath = 'expensereport';
10995 } elseif ($objecttype == 'subscription') {
10996 $tplpath = 'adherents';
10997 } elseif ($objecttype == 'conferenceorbooth') {
10998 $tplpath = 'eventorganization';
10999 } elseif ($objecttype == 'conferenceorboothattendee') {
11000 $tplpath = 'eventorganization';
11001 } elseif ($objecttype == 'mo') {
11002 $tplpath = 'mrp';
11003 if (!isModEnabled('mrp')) {
11004 continue; // Do not show if module disabled
11005 }
11006 } elseif ($objecttype == 'project_task') {
11007 $tplpath = 'projet/tasks';
11008 }
11009
11010 global $linkedObjectBlock; // Will be used into tpl
11011 $linkedObjectBlock = $objects;
11012
11013 // Output template part (modules that overwrite templates must declare this into descriptor)
11014 $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
11015
11016 foreach ($dirtpls as $reldir) {
11017 $reldir = rtrim($reldir, '/');
11018 if ($nboftypesoutput == ($nbofdifferenttypes - 1)) { // No more type to show after
11019 global $noMoreLinkedObjectBlockAfter; // Will be used into tpl
11020 $noMoreLinkedObjectBlockAfter = 1;
11021 }
11022 $file = dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
11023 if (file_exists($file)) {
11024 $res = @include $file;
11025 if ($res) {
11026 $nboftypesoutput++;
11027 break;
11028 }
11029 }
11030 }
11031 }
11032
11033 if (!$nboftypesoutput) {
11034 print '<tr><td colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
11035 }
11036
11037 print '</table>';
11038
11039 if (!empty($compatibleImportElementsList)) {
11040 $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
11041 }
11042
11043 print '</div>';
11044 }
11045
11046 return $nbofdifferenttypes;
11047 }
11048
11058 public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array(), $nooutput = 0)
11059 {
11060 global $conf, $langs, $hookmanager, $form;
11061 global $action;
11062
11063 dol_syslog(__METHOD__, LOG_DEBUG);
11064
11065 if (empty($form)) {
11066 $form = new Form($this->db);
11067 }
11068
11069 $linktoelem = '';
11070 $linktoelemlist = '';
11071 $listofidcompanytoscan = '';
11072
11073 if (!is_object($object->thirdparty)) {
11074 if ($object->element == 'subscription' && isset($object->fk_adherent)) {
11075 $subby = new Subscription($object->db);
11076 $subby->fetch($object->id);
11077 $adh = new Adherent($object->db);
11078 //$fk_adherent = $object->fk_adherent;
11079 // creating new subscription object only to fetch the adherent which obviously exists given the if statement above are Inefficient, but else phan complains
11080 $fk_adherent = $subby->fk_adherent;
11081 $adh->fetch($fk_adherent);
11082 $thirdparty_id = $adh->fetch_thirdparty();
11083 }
11084 } else {
11085 $thirdparty_id = $object->thirdparty->id;
11086 }
11087
11088 $possiblelinks = array();
11089
11090 $dontIncludeCompletedItems = getDolGlobalString('DONT_INCLUDE_COMPLETED_ELEMENTS_LINKS');
11091
11092 if (!empty($thirdparty_id) && $thirdparty_id > 0) {
11093 $listofidcompanytoscan = (int) $thirdparty_id;
11094 if (is_object($object->thirdparty) && ($object->thirdparty->parent > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PARENT_IN_LINKTO')) {
11095 $listofidcompanytoscan .= ',' . (int) $object->thirdparty->parent;
11096 }
11097 if (($object->fk_project > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO')) {
11098 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
11099 $tmpproject = new Project($this->db);
11100 $tmpproject->fetch((int) $object->fk_project);
11101 if ($tmpproject->socid > 0 && ($tmpproject->socid != $thirdparty_id)) {
11102 $listofidcompanytoscan .= ',' . (int) $tmpproject->socid;
11103 }
11104 unset($tmpproject);
11105 }
11106
11107 $possiblelinks = array(
11108 'propal' => array(
11109 'enabled' => isModEnabled('propal'),
11110 'perms' => 1,
11111 'label' => 'LinkToProposal',
11112 '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' : ''),
11113 ),
11114 'shipping' => array(
11115 'enabled' => isModEnabled('shipping'),
11116 'perms' => 1,
11117 'label' => 'LinkToExpedition',
11118 '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' : ''),
11119 ),
11120 'order' => array(
11121 'enabled' => isModEnabled('order'),
11122 'perms' => 1,
11123 'label' => 'LinkToOrder',
11124 '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' : ''),
11125 'linkname' => 'commande',
11126 ),
11127 'subscription' => array(
11128 'enabled' => isModEnabled('member'),
11129 'perms' => 1,
11130 'label' => 'LinkToMemberSubscription',
11131 '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') . ')',
11132 'linkname' => 'subscription',
11133 ),
11134 'conferenceorboothattendee' => array(
11135 'enabled' => isModEnabled('eventorganization'),
11136 'perms' => 1,
11137 'label' => 'LinkToConferenceOrBoothAttendee',
11138 '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),
11139 'linkname' => 'attendee'
11140 ),
11141 'invoice' => array(
11142 'enabled' => isModEnabled('invoice'),
11143 'perms' => 1,
11144 'label' => 'LinkToInvoice',
11145 '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' : ''),
11146 'linkname' => 'facture',
11147 ),
11148 'invoice_template' => array(
11149 'enabled' => isModEnabled('invoice'),
11150 'perms' => 1,
11151 'label' => 'LinkToTemplateInvoice',
11152 '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') . ')',
11153 ),
11154 'contrat' => array(
11155 'enabled' => isModEnabled('contract'),
11156 'perms' => 1,
11157 'label' => 'LinkToContract',
11158 '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
11159 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',
11160 ),
11161 'fichinter' => array(
11162 'enabled' => isModEnabled('intervention'),
11163 'perms' => 1,
11164 'label' => 'LinkToIntervention',
11165 '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') . ')',
11166 ),
11167 'supplier_proposal' => array(
11168 'enabled' => isModEnabled('supplier_proposal'),
11169 'perms' => 1,
11170 'label' => 'LinkToSupplierProposal',
11171 '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' : ''),
11172 ),
11173 'order_supplier' => array(
11174 'enabled' => isModEnabled("supplier_order"),
11175 'perms' => 1,
11176 'label' => 'LinkToSupplierOrder',
11177 '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' : ''),
11178 ),
11179 'invoice_supplier' => array(
11180 'enabled' => isModEnabled("supplier_invoice"),
11181 'perms' => 1, 'label' => 'LinkToSupplierInvoice',
11182 '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' : ''),
11183 ),
11184 'ticket' => array(
11185 'enabled' => isModEnabled('ticket'),
11186 'perms' => 1,
11187 'label' => 'LinkToTicket',
11188 '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' : ''),
11189 ),
11190 'mo' => array(
11191 'enabled' => isModEnabled('mrp'),
11192 'perms' => 1,
11193 'label' => 'LinkToMo',
11194 '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' : ''),
11195 ),
11196 );
11197 }
11198
11199 if ($object->table_element == 'commande_fournisseur') {
11200 $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' : '');
11201 } elseif ($object->table_element == 'mrp_mo') {
11202 $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' : '');
11203 }
11204
11205 $reshook = 0; // Ensure $reshook is defined for static analysis
11206 if (!empty($listofidcompanytoscan)) { // If empty, we don't have criteria to scan the object we can link to
11207 // Can complete the possiblelink array
11208 $hookmanager->initHooks(array('commonobject'));
11209 $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
11210 $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
11211 }
11212
11213 if (empty($reshook)) {
11214 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11215 $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
11216 }
11217 } elseif ($reshook > 0) {
11218 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11219 $possiblelinks = $hookmanager->resArray;
11220 }
11221 }
11222
11223 if (!empty($possiblelinks)) {
11224 $object->fetchObjectLinked();
11225 }
11226
11227 // Build the html part with possible suggested links
11228 $htmltoenteralink = '';
11229 foreach ($possiblelinks as $key => $possiblelink) {
11230 $num = 0;
11231 if (empty($possiblelink['enabled'])) {
11232 continue;
11233 }
11234
11235
11236 // If we ask a resource form external module (instead of default path)
11237 $module = '';
11238 if (preg_match('/^([^@]+)@([^@]+)$/i', $key, $regs)) { // 'myobject@mymodule'
11239 $key = $regs[1];
11240 $module = $regs[2];
11241 }
11242
11243 if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
11244 $htmltoenteralink .= '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
11245
11246 // Section for free ref input
11247 if (!getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11248 $htmltoenteralink .= '<br>'."\n";
11249 $htmltoenteralink .= '<!-- form to add a link from anywhere -->'."\n";
11250 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
11251 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11252 $htmltoenteralink .= '<input type="hidden" name="action" value="addlinkbyref">';
11253 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11254 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key .(!empty($module) ? '@'.$module : ''). '">';
11255 $htmltoenteralink .= '<table class="noborder">';
11256 $htmltoenteralink .= '<tr class="liste_titre">';
11257 //print '<td>' . $langs->trans("Ref") . '</td>';
11258 $htmltoenteralink .= '<td class="center"><input type="text" placeholder="'.dol_escape_htmltag($langs->trans("Ref")).'" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">';
11259 $htmltoenteralink .= '<br>';
11260 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
11261 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp" name="cancel" value="' . $langs->trans('Cancel') . '">';
11262 $htmltoenteralink .= '</td>';
11263 $htmltoenteralink .= '</tr>';
11264 $htmltoenteralink .= '</table>';
11265 $htmltoenteralink .= '</form>';
11266 }
11267
11268 $sql = $possiblelink['sql'];
11269
11270 $resqllist = $this->db->query($sql);
11271 if ($resqllist) {
11272 $num = $this->db->num_rows($resqllist);
11273
11274 if ($num > 0) {
11275 // Section for free predefined list
11276 if (getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11277 $htmltoenteralink .= '<br>';
11278 }
11279 $htmltoenteralink .= '<!-- form to add a link from object to same thirdparty -->'."\n";
11280 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
11281 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11282 $htmltoenteralink .= '<input type="hidden" name="action" value="addlink">';
11283 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11284 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key . (!empty($module) ? '@'.$module : ''). '">';
11285 $htmltoenteralink .= '<table class="noborder">';
11286
11287 switch ($key) {
11288 case 'conferenceorboothattendee':
11289 // Custom logic for linking to attendees
11290 $htmltoenteralink .= $this->makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist);
11291 break;
11292
11293 default:
11294 // Standard logic for all other object types
11295 $htmltoenteralink .= $this->makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist);
11296 break;
11297 }
11298
11299 $htmltoenteralink .= '</table>';
11300 $htmltoenteralink .= '<div class="center">';
11301 if ($num) {
11302 $htmltoenteralink .= '<input type="submit" class="button valignmiddle marginleftonly marginrightonly smallpaddingimp" value="' . $langs->trans('ToLink') . '">';
11303 }
11304 if (empty($conf->use_javascript_ajax)) {
11305 $htmltoenteralink .= '<input type="submit" class="button button-cancel marginleftonly marginrightonly smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
11306 } else {
11307 $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>';
11308 }
11309 $htmltoenteralink .= '</form>';
11310 }
11311
11312 $this->db->free($resqllist);
11313 } else {
11314 dol_print_error($this->db);
11315 }
11316 $htmltoenteralink .= '</div>';
11317
11318
11319 // Complete the list for the combo box
11320 if ($num > 0 || !getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11321 $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
11322 // } else $linktoelem.=$langs->trans($possiblelink['label']);
11323 } else {
11324 $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
11325 }
11326 }
11327 }
11328
11329 if ($linktoelemlist) {
11330 $linktoelem = '
11331 <dl class="dropdown" id="linktoobjectname">
11332 ';
11333 if (!empty($conf->use_javascript_ajax)) {
11334 $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
11335 }
11336 $linktoelem .= '<dd>
11337 <div class="multiselectlinkto">
11338 <ul class="ulselectedfields">' . $linktoelemlist . '
11339 </ul>
11340 </div>
11341 </dd>
11342 </dl>';
11343 } else {
11344 $linktoelem = '';
11345 }
11346
11347 if (!empty($conf->use_javascript_ajax)) {
11348 print '<!-- Add js to show linkto box -->
11349 <script nonce="' . getNonce() . '">
11350 jQuery(document).ready(function() {
11351 jQuery(".linkto").click(function() {
11352 console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
11353 jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
11354 });
11355 });
11356 </script>
11357 ';
11358 }
11359
11360 if ($nooutput) {
11361 return array('linktoelem' => $linktoelem, 'htmltoenteralink' => $htmltoenteralink);
11362 } else {
11363 print $htmltoenteralink;
11364 }
11365
11366 return $linktoelem;
11367 }
11368
11383 public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = 'yesno width75', $labelyes = 'Yes', $labelno = 'No')
11384 {
11385 global $langs;
11386
11387 $yes = "yes";
11388 $no = "no";
11389 if ($option) {
11390 $yes = "1";
11391 $no = "0";
11392 }
11393
11394 $disabled = ($disabled ? ' disabled' : '');
11395
11396 $resultyesno = '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
11397 if ($useempty) {
11398 $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
11399 }
11400 if (("$value" == 'yes') || ($value == 1)) {
11401 $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
11402 $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
11403 } else {
11404 $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
11405 $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
11406 $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
11407 }
11408 $resultyesno .= '</select>' . "\n";
11409
11410 if ($addjscombo) {
11411 $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
11412 }
11413
11414 return $resultyesno;
11415 }
11416
11417 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11418
11428 public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
11429 {
11430 // phpcs:enable
11431 $sql = "SELECT rowid, label";
11432 $sql .= " FROM " . $this->db->prefix() . "export_model";
11433 $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
11434 $sql .= " ORDER BY rowid";
11435 $result = $this->db->query($sql);
11436 if ($result) {
11437 print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
11438 if ($useempty) {
11439 print '<option value="-1">&nbsp;</option>';
11440 }
11441
11442 $num = $this->db->num_rows($result);
11443 $i = 0;
11444 while ($i < $num) {
11445 $obj = $this->db->fetch_object($result);
11446 if ($selected == $obj->rowid) {
11447 print '<option value="' . $obj->rowid . '" selected>';
11448 } else {
11449 print '<option value="' . $obj->rowid . '">';
11450 }
11451 print $obj->label;
11452 print '</option>';
11453 $i++;
11454 }
11455 print "</select>";
11456 } else {
11457 dol_print_error($this->db);
11458 }
11459 }
11460
11479 public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
11480 {
11481 global $conf, $langs, $hookmanager, $extralanguages;
11482
11483 $ret = '';
11484 if (empty($fieldid)) {
11485 $fieldid = 'rowid';
11486 }
11487 if (empty($fieldref)) {
11488 $fieldref = 'ref';
11489 }
11490
11491 // Preparing gender's display if there is one
11492 $addgendertxt = '';
11493 if (property_exists($object, 'gender') && !empty($object->gender)) {
11494 $addgendertxt = ' ';
11495 switch ($object->gender) {
11496 case 'man':
11497 $addgendertxt .= '<i class="fas fa-mars valignmiddle"></i>';
11498 break;
11499 case 'woman':
11500 $addgendertxt .= '<i class="fas fa-venus valignmiddle"></i>';
11501 break;
11502 case 'other':
11503 $addgendertxt .= '<i class="fas fa-transgender valignmiddle"></i>';
11504 break;
11505 }
11506 }
11507
11508 // Add where from hooks
11509 if (is_object($hookmanager)) {
11510 $parameters = array('showrefnav' => true);
11511 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
11512 if (!empty($hookmanager->resPrint)) {
11513 if (empty($object->next_prev_filter) && preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11514 $object->next_prev_filter = (string) preg_replace('/^\s*AND\s*/i', '', $hookmanager->resPrint);
11515 } elseif (!empty($object->next_prev_filter) && !preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11516 $object->next_prev_filter .= ' AND '.$hookmanager->resPrint;
11517 } else {
11518 $object->next_prev_filter .= $hookmanager->resPrint;
11519 }
11520 }
11521 }
11522
11523 $previous_ref = $next_ref = '';
11524 if ($shownav) {
11525 //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,fieldid=$fieldid,filedref=$fieldref,morehtmlref=$morehtmlref,moreparam=$moreparam";
11526 $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
11527
11528 $navurl = $_SERVER["PHP_SELF"];
11529
11530 // Special case for token card
11531 if ($paramid == 'api_token_card') {
11532 if (preg_match('/\/user\/api_token/', $navurl)) {
11533 $navurl = preg_replace('/card/', 'list', $navurl);
11534 $paramid = 'id';
11535 }
11536 }
11537
11538 // Special case for project/task page
11539 if ($paramid == 'project_ref') {
11540 if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) { // TODO Remove this when nav with project_ref on task pages are ok
11541 $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
11542 $paramid = 'ref';
11543 }
11544 }
11545
11546 $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>';
11547 $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>';
11548 }
11549
11550 //print "xx".$previous_ref."x".$next_ref;
11551 $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
11552
11553 // Right part of banner
11554 if ($morehtmlright) {
11555 $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
11556 }
11557
11558 if ($previous_ref || $next_ref || $morehtml) {
11559 $ret .= '<div class="pagination paginationref"><ul class="right">';
11560 }
11561 if ($morehtml && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
11562 $ret .= '<!-- morehtml --><li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
11563 }
11564 if ($shownav && ($previous_ref || $next_ref)) {
11565 $ret .= '<li class="pagination">' . $previous_ref . '</li>';
11566 $ret .= '<li class="pagination">' . $next_ref . '</li>';
11567 }
11568 if ($previous_ref || $next_ref || $morehtml) {
11569 $ret .= '</ul></div>';
11570 }
11571
11572 // Status
11573 $parameters = array('morehtmlstatus' => $morehtmlstatus);
11574 $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
11575 if (empty($reshook)) {
11576 $morehtmlstatus .= $hookmanager->resPrint;
11577 } else {
11578 $morehtmlstatus = $hookmanager->resPrint;
11579 }
11580 if ($morehtmlstatus) {
11581 $ret .= '<!-- status --><div class="statusref">' . $morehtmlstatus . '</div>';
11582 }
11583
11584 $parameters = array();
11585 $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
11586 if (empty($reshook)) {
11587 $morehtmlref .= $hookmanager->resPrint;
11588 } elseif ($reshook > 0) {
11589 $morehtmlref = $hookmanager->resPrint;
11590 }
11591
11592 // Left part of banner
11593 if ($morehtmlleft) {
11594 if ($conf->browser->layout == 'phone') {
11595 $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
11596 } else {
11597 $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
11598 }
11599 }
11600
11601 //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
11602 $ret .= '<!-- Ref or ID --><div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
11603
11604 // For thirdparty, contact, user, member, the ref is the id, so we show something else
11605 if ($object->element == 'societe') {
11606 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->name).'</span>';
11607
11608 // List of extra languages
11609 $arrayoflangcode = array();
11610 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
11611 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
11612 }
11613
11614 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
11615 if (!is_object($extralanguages)) {
11616 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
11617 $extralanguages = new ExtraLanguages($this->db);
11618 }
11619 $extralanguages->fetch_name_extralanguages('societe');
11620
11621 // Guard against PHP 8 'Undefined array key' when MAIN_USE_ALTERNATE_TRANSLATION_FOR
11622 // is not configured and fetch_name_extralanguages() leaves attributes empty (issue #34596).
11623 if (!empty($extralanguages->attributes['societe']) && !empty($extralanguages->attributes['societe']['name'])) {
11624 $object->fetchValuesForExtraLanguages();
11625
11626 $htmltext = '';
11627 // If there is extra languages
11628 foreach ($arrayoflangcode as $extralangcode) {
11629 $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
11630 if ($object->array_languages['name'][$extralangcode]) {
11631 $htmltext .= $object->array_languages['name'][$extralangcode];
11632 } else {
11633 $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
11634 }
11635 }
11636 $ret .= '<!-- Show translations of name -->' . "\n";
11637 $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
11638 }
11639 }
11640 } elseif ($object->element == 'member') {
11641 '@phan-var-force Adherent $object';
11642 $ret .= $object->ref . '<br>';
11643 $fullname = $object->getFullName($langs);
11644 if ($object->morphy == 'mor' && $object->societe) {
11645 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '').'</span>';
11646 } else {
11647 $ret .= '<span class="valignmiddle">'.dolPrintHTML($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities((string) $object->societe) . ')' : '').'</span>';
11648 }
11649 } elseif (in_array($object->element, array('contact', 'user'))) {
11650 $ret .= '<span class="valignmiddle">'.dolPrintHTML($object->getFullName($langs)).'</span>'.$addgendertxt;
11651 } elseif ($object->element == 'usergroup') {
11652 $ret .= dol_htmlentities((string) $object->name);
11653 } elseif (in_array($object->element, array('action', 'agenda'))) {
11654 '@phan-var-force ActionComm $object';
11655 $ret .= $object->ref . '<br>' . $object->label;
11656 } elseif (in_array($object->element, array('adherent_type'))) {
11657 $ret .= $object->label;
11658 } elseif ($object->element == 'ecm_directories') {
11659 $ret .= '';
11660 } elseif ($object->element == 'accountingbookkeeping' && !empty($object->context['mode']) && $object->context['mode'] == '_tmp') {
11661 $ret .= '<span class="valignmiddle">'.$langs->trans("Draft").'</span>';
11662 } elseif ($object instanceof Ticket) {
11663 '@phan-var-force Ticket $object';
11664 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11665 $ret .= ' &nbsp; <span class="nobold small" title="'.dolPrintHTMLForAttribute($langs->trans("TicketTrackId")).'">('.$object->track_id.')</span>';
11666 } elseif ($fieldref != 'none') {
11667 // Generic case
11668 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11669 }
11670 if ($morehtmlref) {
11671 // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
11672 if (substr($morehtmlref, 0, 4) != '<div') {
11673 $ret .= ' ';
11674 }
11675
11676 $ret .= '<!-- morehtmlref -->'.$morehtmlref;
11677 }
11678
11679 $ret .= '</div>';
11680
11681 $ret .= '</div><!-- End banner content -->';
11682
11683 return $ret;
11684 }
11685
11686
11695 public function showbarcode(&$object, $width = 100, $morecss = '')
11696 {
11697 //Check if barcode is filled in the card
11698 if (empty($object->barcode)) {
11699 return '';
11700 }
11701
11702 // Complete object if not complete
11703 if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
11704 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11705 $result = $object->fetchBarCode();
11706 //Check if fetchBarCode() failed
11707 if ($result < 1) {
11708 return '<!-- ErrorFetchBarcode -->';
11709 }
11710 }
11711
11712 // Barcode image @phan-suppress-next-line PhanUndeclaredProperty
11713 $url = DOL_URL_ROOT . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
11714 $out = '<!-- url barcode = ' . $url . ' -->';
11715 $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
11716
11717 return $out;
11718 }
11719
11738 public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0, $usesharelinkifavailable = 0)
11739 {
11740 global $conf, $db, $langs;
11741
11742 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
11743 $id = (empty($object->id) ? $object->rowid : $object->id); // @phan-suppress-current-line PhanUndeclaredProperty (->rowid)
11744
11745 $dir = '';
11746 $file = '';
11747 $originalfile = '';
11748 $altfile = '';
11749 $email = '';
11750 $capture = '';
11751 if ($modulepart == 'societe') {
11752 $dir = $conf->societe->multidir_output[$entity];
11753 if (!empty($object->logo)) {
11754 if (dolIsAllowedForPreview($object->logo)) {
11755 if ((string) $imagesize == 'mini') {
11756 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
11757 } elseif ((string) $imagesize == 'small') {
11758 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
11759 } else {
11760 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11761 }
11762 $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11763 }
11764 }
11765 $email = $object->email;
11766 } elseif ($modulepart == 'contact') {
11767 $dir = $conf->societe->multidir_output[$entity] . '/contact';
11768 $photo = $object->photo; // Copy to help static analysis
11769 if (!empty($photo)) {
11770 if (dolIsAllowedForPreview($photo)) {
11771 if ((string) $imagesize == 'mini') {
11772 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11773 } elseif ((string) $imagesize == 'small') {
11774 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_small');
11775 } else {
11776 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11777 }
11778 $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11779 }
11780 }
11781 $email = $object->email;
11782 $capture = 'user';
11783 } elseif ($modulepart == 'userphoto') {
11784 $dir = $conf->user->dir_output;
11785 $photo = $object->photo; // Copy to help static analysis
11786 if (!empty($photo)) {
11787 if (dolIsAllowedForPreview($photo)) {
11788 if ((string) $imagesize == 'mini') {
11789 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11790 } elseif ((string) $imagesize == 'small') {
11791 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_small');
11792 } else {
11793 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11794 }
11795 $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11796 }
11797 }
11798 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11799 $altfile = $object->id . ".jpg"; // For backward compatibility
11800 }
11801 $email = $object->email;
11802 $capture = 'user';
11803 } elseif ($modulepart == 'memberphoto') {
11804 $dir = $conf->member->dir_output;
11805 $photo = $object->photo; // Copy to help static analysis
11806 if (!empty($photo)) {
11807 if (dolIsAllowedForPreview($photo)) {
11808 if ((string) $imagesize == 'mini') {
11809 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11810 } elseif ((string) $imagesize == 'small') {
11811 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_small');
11812 } else {
11813 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11814 }
11815 $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11816 }
11817 }
11818 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11819 $altfile = $object->id . ".jpg"; // For backward compatibility
11820 }
11821 $email = $object->email;
11822 $capture = 'user';
11823 } else {
11824 // Generic case to show photos
11825 // TODO Implement this method in previous objects so we can always use this generic method.
11826 if ($modulepart != "unknown" && method_exists($object, 'getDataToShowPhoto')) {
11827 $tmpdata = $object->getDataToShowPhoto($modulepart, $imagesize);
11828
11829 $dir = $tmpdata['dir'];
11830 $file = $tmpdata['file'];
11831 $originalfile = $tmpdata['originalfile'];
11832 $altfile = $tmpdata['altfile'];
11833 $email = $tmpdata['email'];
11834 $capture = $tmpdata['capture'];
11835 }
11836 }
11837
11838 if ($forcecapture) {
11839 $capture = $forcecapture;
11840 }
11841
11842 $ret = '';
11843
11844 if ($dir) {
11845 if ($file && file_exists($dir . "/" . $file)) {
11846 if ($addlinktofullsize) {
11847 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11848 if ($urladvanced) {
11849 $ret .= '<a href="' . $urladvanced . '">';
11850 } else {
11851 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11852 }
11853 }
11854
11855 $sharekey = '';
11856 if ($usesharelinkifavailable) {
11857 // $dir is a full path '/home/.../dolibarr_documents/module'
11858 $relativefileforecm = preg_replace('/^'.preg_quote(DOL_DATA_ROOT.'/', '/').'/', '', $dir.'/'.$originalfile);
11859 // $relativefileforecme = 'module/...'
11860 require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php';
11861 $ecmfiles = new EcmFiles($db);
11862 $ecmfiles->fetch(0, '', $relativefileforecm);
11863
11864 $sharekey = (string) $ecmfiles->share;
11865 }
11866
11867 if (!empty($sharekey)) {
11868 $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) . '">';
11869 } else {
11870 $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) . '">';
11871 }
11872 if ($addlinktofullsize) {
11873 $ret .= '</a>';
11874 }
11875 } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
11876 if ($addlinktofullsize) {
11877 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11878 if ($urladvanced) {
11879 $ret .= '<a href="' . $urladvanced . '">';
11880 } else {
11881 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11882 }
11883 }
11884 $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) . '">';
11885 if ($addlinktofullsize) {
11886 $ret .= '</a>';
11887 }
11888 } else {
11889 $nophoto = '/public/theme/common/nophoto.png';
11890 $defaultimg = 'identicon'; // For gravatar
11891 if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) { // For modules that need a special image when photo not found
11892 if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor') !== false)) {
11893 $nophoto = 'company';
11894 } else {
11895 $nophoto = '/public/theme/common/user_anonymous.png';
11896 if (!empty($object->gender) && $object->gender == 'man') {
11897 $nophoto = '/public/theme/common/user_man.png';
11898 }
11899 if (!empty($object->gender) && $object->gender == 'woman') {
11900 $nophoto = '/public/theme/common/user_woman.png';
11901 }
11902 }
11903 }
11904
11905 if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
11906 // see https://gravatar.com/site/implement/images/php/
11907 $ret .= '<!-- Put link to gravatar -->';
11908 $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
11909 } else {
11910 if ($nophoto == 'company') {
11911 $ret .= '<div class="divforspanimg valignmiddle inline-block center photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
11912 //$ret .= '<div class="difforspanimgright"></div>';
11913 } else {
11914 $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '">';
11915 }
11916 }
11917 }
11918
11919 if ($caneditfield) {
11920 if ($object->photo) {
11921 $ret .= "<br>\n";
11922 }
11923 $ret .= '<table class="nobordernopadding centpercent">';
11924 if ($object->photo) {
11925 $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
11926 }
11927 $ret .= '<tr><td class="tdoverflow">';
11928 $maxfilesizearray = getMaxFileSizeArray();
11929 $maxmin = $maxfilesizearray['maxmin'];
11930 if ($maxmin > 0) {
11931 $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">'; // MAX_FILE_SIZE must precede the field type=file
11932 }
11933 $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . dolPrintHTMLForAttribute($capture) . '"' : '') . '>';
11934 $ret .= '</td></tr>';
11935 $ret .= '</table>';
11936 }
11937 }
11938
11939 return $ret;
11940 }
11941
11942 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11943
11960 public function select_dolgroups($selected = 0, $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0', $multiple = false, $morecss = 'minwidth200')
11961 {
11962 // phpcs:enable
11963 global $conf, $user, $langs;
11964
11965 // Allow excluding groups
11966 $excludeGroups = null;
11967 if (is_array($exclude)) {
11968 $excludeGroups = implode(",", $exclude);
11969 }
11970 // Allow including groups
11971 $includeGroups = null;
11972 if (is_array($include)) {
11973 $includeGroups = implode(",", $include);
11974 }
11975
11976 if (!is_array($selected)) {
11977 $selected = array($selected);
11978 }
11979
11980 $out = '';
11981
11982 // Build sql to search groups
11983 $sql = "SELECT ug.rowid, ug.nom as name";
11984 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
11985 $sql .= ", e.label";
11986 }
11987 $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
11988 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
11989 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
11990 if ($force_entity) {
11991 $sql .= " WHERE ug.entity IN (0, " . $force_entity . ")";
11992 } else {
11993 $sql .= " WHERE ug.entity IS NOT NULL";
11994 }
11995 } else {
11996 $sql .= " WHERE ug.entity IN (0, " . ((int) $conf->entity) . ")";
11997 }
11998 if (is_array($exclude) && $excludeGroups) {
11999 $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
12000 }
12001 if (is_array($include) && $includeGroups) {
12002 $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
12003 }
12004 $sql .= " ORDER BY ug.nom ASC";
12005
12006 dol_syslog(get_class($this) . "::select_dolgroups", LOG_DEBUG);
12007 $resql = $this->db->query($sql);
12008 if ($resql) {
12009 // Enhance with select2
12010 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12011
12012 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
12013
12014 $num = $this->db->num_rows($resql);
12015 $i = 0;
12016 if ($num) {
12017 if ($show_empty && !$multiple) {
12018 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
12019 }
12020
12021 while ($i < $num) {
12022 $obj = $this->db->fetch_object($resql);
12023 $disableline = 0;
12024 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
12025 $disableline = 1;
12026 }
12027
12028 $label = $obj->name;
12029 $labelhtml = $obj->name;
12030 if (isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1) {
12031 $label .= " (" . $obj->label . ")";
12032 $labelhtml .= ' <span class="opacitymedium">(' . $obj->label . ')</span>';
12033 }
12034
12035 $out .= '<option value="' . $obj->rowid . '"';
12036 if ($disableline) {
12037 $out .= ' disabled';
12038 }
12039 if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid)
12040 || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
12041 $out .= ' selected';
12042 }
12043 $out .= ' data-html="'.dol_escape_htmltag($labelhtml).'"';
12044 $out .= '>';
12045 $out .= $label;
12046 $out .= '</option>';
12047 $i++;
12048 }
12049 } else {
12050 if ($show_empty) {
12051 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
12052 }
12053 $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
12054 }
12055 $out .= '</select>';
12056
12057 $out .= ajax_combobox($htmlname);
12058 } else {
12059 dol_print_error($this->db);
12060 }
12061
12062 return $out;
12063 }
12064
12065
12072 public function showFilterButtons($pos = '')
12073 {
12074 $out = '<div class="nowraponall">';
12075 $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fas fa-search"></span></button>';
12076 $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fas fa-times"></span></button>';
12077 $out .= '</div>';
12078
12079 return $out;
12080 }
12081
12090 public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12091 {
12092 global $conf;
12093
12094 $out = '';
12095
12096 if (!empty($conf->use_javascript_ajax)) {
12097 $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
12098 }
12099 $out .= '<script nonce="' . getNonce() . '">
12100 $(document).ready(function() {
12101 $("#' . $cssclass . 's").click(function() {
12102 if($(this).is(\':checked\')){
12103 console.log("We check all ' . $cssclass . ' and trigger the change method");
12104 $(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
12105 }
12106 else
12107 {
12108 console.log("We uncheck all");
12109 $(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
12110 }' . "\n";
12111 if ($calljsfunction) {
12112 $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
12113 }
12114 $out .= ' });
12115/*
12116 $(".' . $cssclass . '").change(function() {
12117 console.log("We check and change the tr class highlight after a change on .'.$cssclass.'");
12118 var $row = $(this).closest("tr");
12119 if ($row.length) {
12120 var anyChecked = $row.find(\'input[type="checkbox"].checkforselect:checked\').length > 0;
12121 console.log("anychecked="+anyChecked);
12122 if (!anyChecked) {
12123 $row.removeClass("highlight");
12124 } else {
12125 $row.addClass("highlight");
12126 }
12127 }
12128 });
12129*/
12130 });
12131 </script>';
12132
12133 return $out;
12134 }
12135
12145 public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12146 {
12147 $out = $this->showFilterButtons();
12148 if ($addcheckuncheckall) {
12149 $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
12150 }
12151 return $out;
12152 }
12153
12167 public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
12168 {
12169 global $langs, $user;
12170
12171 $out = '';
12172 $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
12173 $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
12174 if (!empty($excludeid)) {
12175 $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
12176 }
12177 $sql .= " ORDER BY label";
12178
12179 $resql = $this->db->query($sql);
12180 if ($resql) {
12181 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
12182 if ($useempty) {
12183 $out .= '<option value="0">&nbsp;</option>';
12184 }
12185
12186 while ($obj = $this->db->fetch_object($resql)) {
12187 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
12188 }
12189 $out .= '</select>';
12190 $out .= ajax_combobox('select_' . $htmlname);
12191
12192 if (!empty($htmlname) && $user->admin && $info_admin) {
12193 $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
12194 }
12195
12196 if (!empty($target)) {
12197 $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
12198 $resql = $this->db->query($sql);
12199 if ($resql) {
12200 if ($this->db->num_rows($resql) > 0) {
12201 $obj = $this->db->fetch_object($resql);
12202 $out .= '<script nonce="' . getNonce() . '">
12203 $(function() {
12204 $("select[name=' . $target . ']").on("change", function() {
12205 var current_val = $(this).val();
12206 if (current_val == ' . $obj->id . ') {';
12207 if (!empty($default_selected) || !empty($selected)) {
12208 $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
12209 }
12210
12211 $out .= '
12212 $("select[name=' . $htmlname . ']").change();
12213 }
12214 });
12215
12216 $("select[name=' . $htmlname . ']").change(function() {
12217
12218 if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
12219 // get price of kilometer to fill the unit price
12220 $.ajax({
12221 method: "POST",
12222 dataType: "json",
12223 data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
12224 url: "' . (DOL_URL_ROOT . '/expensereport/ajax/ajaxik.php?' . implode('&', $params)) . '",
12225 }).done(function( data, textStatus, jqXHR ) {
12226 console.log(data);
12227 if (typeof data.up != "undefined") {
12228 $("input[name=value_unit]").val(data.up);
12229 $("select[name=' . $htmlname . ']").attr("title", data.title);
12230 } else {
12231 $("input[name=value_unit]").val("");
12232 $("select[name=' . $htmlname . ']").attr("title", "");
12233 }
12234 });
12235 }
12236 });
12237 });
12238 </script>';
12239 }
12240 }
12241 }
12242 } else {
12243 dol_print_error($this->db);
12244 }
12245
12246 return $out;
12247 }
12248
12257 public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
12258 {
12259 global $conf, $langs;
12260
12261 $out = '';
12262 $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
12263 $sql .= " WHERE entity = " . ((int) $conf->entity) . " AND active = 1";
12264
12265 $resql = $this->db->query($sql);
12266 if ($resql) {
12267 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12268 if ($useempty) {
12269 $out .= '<option value="0"></option>';
12270 }
12271
12272 while ($obj = $this->db->fetch_object($resql)) {
12273 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
12274 }
12275 $out .= '</select>';
12276 } else {
12277 dol_print_error($this->db);
12278 }
12279
12280 return $out;
12281 }
12282
12293 public function selectExpenseFees($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
12294 {
12295 global $langs;
12296
12297 $out = '';
12298 $sql = "SELECT id, code, label";
12299 $sql .= " FROM ".$this->db->prefix()."c_type_fees";
12300 $sql .= " WHERE active = 1";
12301
12302 $resql = $this->db->query($sql);
12303 if ($resql) {
12304 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12305 if ($useempty) {
12306 $out .= '<option value="0"></option>';
12307 }
12308 if ($allchoice) {
12309 $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
12310 }
12311
12312 $field = 'code';
12313 if ($useid) {
12314 $field = 'id';
12315 }
12316
12317 while ($obj = $this->db->fetch_object($resql)) {
12318 $key = $langs->trans($obj->code);
12319 $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
12320 }
12321 $out .= '</select>';
12322
12323 $out .= ajax_combobox('select_'.$htmlname);
12324 } else {
12325 dol_print_error($this->db);
12326 }
12327
12328 return $out;
12329 }
12330
12349 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)
12350 {
12351 global $user, $conf, $langs;
12352
12353 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
12354
12355 if (is_null($usertofilter)) {
12356 $usertofilter = $user;
12357 }
12358
12359 $out = '';
12360
12361 $hideunselectables = false;
12362 if (getDolGlobalString('INVOICE_HIDE_UNSELECTABLES')) {
12363 $hideunselectables = true;
12364 }
12365
12366 if (empty($projectsListId)) {
12367 if (!$usertofilter->hasRight('projet', 'all', 'lire')) {
12368 $projectstatic = new Project($this->db);
12369 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
12370 }
12371 }
12372
12373 // Search all projects
12374 $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";
12375 $sql .= " FROM " . $this->db->prefix() . "facture as f";
12376 $sql .= " INNER JOIN " . $this->db->prefix() . "projet as p ON p.entity IN (" . getEntity('project') . ") AND f.fk_projet = p.rowid";
12377 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = p.fk_soc";
12378 $sql .= " WHERE f.fk_statut = 0"; // Draft invoices only
12379 //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
12380 //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
12381 //if ($socid > 0) $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
12382 $sql .= " ORDER BY p.ref, f.ref ASC";
12383
12384 $resql = $this->db->query($sql);
12385 if ($resql) {
12386 // Use select2 selector
12387 if (!empty($conf->use_javascript_ajax)) {
12388 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12389 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12390 $out .= $comboenhancement;
12391 $morecss = 'minwidth200imp maxwidth500';
12392 }
12393
12394 if (empty($option_only)) {
12395 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12396 }
12397 if (!empty($show_empty)) {
12398 $out .= '<option value="0" class="optiongrey">';
12399 if (!is_numeric($show_empty)) {
12400 $out .= $show_empty;
12401 } else {
12402 $out .= '&nbsp;';
12403 }
12404 $out .= '</option>';
12405 }
12406 $num = $this->db->num_rows($resql);
12407 $i = 0;
12408 if ($num) {
12409 while ($i < $num) {
12410 $obj = $this->db->fetch_object($resql);
12411 // 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.
12412 if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && !$usertofilter->hasRight('societe', 'lire')) {
12413 // Do nothing
12414 } else {
12415 if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
12416 $i++;
12417 continue;
12418 }
12419
12420 $labeltoshow = '';
12421
12422 if ($showproject == 'all') {
12423 $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
12424 if ($obj->name) {
12425 $labeltoshow .= ' - ' . $obj->name; // Soc name
12426 }
12427
12428 $disabled = 0;
12429 if ($obj->fk_statut == Project::STATUS_DRAFT) {
12430 $disabled = 1;
12431 $labeltoshow .= ' - ' . $langs->trans("Draft");
12432 } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
12433 if ($discard_closed == 2) {
12434 $disabled = 1;
12435 }
12436 $labeltoshow .= ' - ' . $langs->trans("Closed");
12437 } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
12438 $disabled = 1;
12439 $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
12440 }
12441 }
12442
12443 if (!empty($selected) && $selected == $obj->rowid) {
12444 $out .= '<option value="' . $obj->rowid . '" selected';
12445 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12446 $out .= '>' . $labeltoshow . '</option>';
12447 } else {
12448 if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
12449 $resultat = '';
12450 } else {
12451 $resultat = '<option value="' . $obj->rowid . '"';
12452 if ($disabled) {
12453 $resultat .= ' disabled';
12454 }
12455 //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
12456 //else $labeltoshow.=' ('.$langs->trans("Private").')';
12457 $resultat .= '>';
12458 $resultat .= $labeltoshow;
12459 $resultat .= '</option>';
12460 }
12461 $out .= $resultat;
12462 }
12463 }
12464 $i++;
12465 }
12466 }
12467 if (empty($option_only)) {
12468 $out .= '</select>';
12469 }
12470
12471 $this->db->free($resql);
12472
12473 return $out;
12474 } else {
12475 dol_print_error($this->db);
12476 return '';
12477 }
12478 }
12479
12494 public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
12495 {
12496 global $conf, $langs;
12497
12498 $out = '';
12499
12500 dol_syslog('FactureRec::fetch', LOG_DEBUG);
12501
12502 $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
12503 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
12504 $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
12505 $sql .= " ORDER BY f.titre ASC";
12506
12507 $resql = $this->db->query($sql);
12508 if ($resql) {
12509 // Use select2 selector
12510 if (!empty($conf->use_javascript_ajax)) {
12511 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12512 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12513 $out .= $comboenhancement;
12514 $morecss = 'minwidth200imp maxwidth500';
12515 }
12516
12517 if (empty($option_only)) {
12518 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12519 }
12520 if (!empty($show_empty)) {
12521 $out .= '<option value="0" class="optiongrey">';
12522 if (!is_numeric($show_empty)) {
12523 $out .= $show_empty;
12524 } else {
12525 $out .= '&nbsp;';
12526 }
12527 $out .= '</option>';
12528 }
12529 $num = $this->db->num_rows($resql);
12530 if ($num) {
12531 while ($obj = $this->db->fetch_object($resql)) {
12532 $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
12533
12534 $disabled = 0;
12535 if (!empty($obj->suspended)) {
12536 $disabled = 1;
12537 $labeltoshow .= ' - ' . $langs->trans("Closed");
12538 }
12539
12540
12541 if (!empty($selected) && $selected == $obj->rowid) {
12542 $out .= '<option value="' . $obj->rowid . '" selected';
12543 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12544 $out .= '>' . $labeltoshow . '</option>';
12545 } else {
12546 if ($disabled && ($selected != $obj->rowid)) {
12547 $resultat = '';
12548 } else {
12549 $resultat = '<option value="' . $obj->rowid . '"';
12550 if ($disabled) {
12551 $resultat .= ' disabled';
12552 }
12553 $resultat .= '>';
12554 $resultat .= $labeltoshow;
12555 $resultat .= '</option>';
12556 }
12557 $out .= $resultat;
12558 }
12559 }
12560 }
12561 if (empty($option_only)) {
12562 $out .= '</select>';
12563 }
12564
12565 print $out;
12566
12567 $this->db->free($resql);
12568 return $num;
12569 } else {
12570 $this->errors[] = $this->db->lasterror;
12571 return -1;
12572 }
12573 }
12574
12575
12586 public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '', $arrayoffiltercriterias = array())
12587 {
12588 // TODO: Use $arrayoffiltercriterias param instead of $arrayofcriterias to include linked object fields in search
12589 global $langs, $form;
12590
12591 //require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
12592 //$formother = new FormOther($this->db);
12593
12594 if ($search_component_params_hidden != '' && !preg_match('/^\‍(.*\‍)$/', $search_component_params_hidden)) { // If $search_component_params_hidden does not start and end with ()
12595 $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
12596 }
12597
12598 $ret = '<!-- searchComponent -->';
12599
12600 $ret .= '<div class="divadvancedsearchfieldcomp centpercent inline-block">';
12601 $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
12602 $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
12603 $ret .= '</a>';
12604
12605 $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
12606
12607 // Show select fields as tags.
12608 $ret .= '<div id="divsearch_component_params" name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
12609
12610 if ($search_component_params_hidden) {
12611 // Split the criteria on each AND
12612 //var_dump($search_component_params_hidden);
12613
12614 $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
12615
12616 // $arrayofandtags is now array( '...' , '...', ...)
12617 // Show each AND part
12618 foreach ($arrayofandtags as $tmpkey => $tmpval) {
12619 $errormessage = '';
12620 $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
12621 if ($errormessage) {
12622 $this->error = 'ERROR in parsing search string: '.$errormessage;
12623 }
12624 // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
12625 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12626 $searchtags = removeGlobalParenthesis($searchtags);
12627
12628 $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="'.($tmpkey + 1).'" data-ufilter="'.dol_escape_htmltag($tmpval).'">';
12629 $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="'.($tmpkey + 1).'">x</span> ';
12630 $ret .= dol_escape_htmltag($searchtags);
12631 $ret .= '</span>';
12632 }
12633 }
12634
12635 //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
12636
12637 //$ret .= search_component_params
12638 //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
12639 //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
12640
12641 $show_search_component_params_hidden = 1;
12642 if ($show_search_component_params_hidden) {
12643 $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
12644 }
12645 $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%')) -->";
12646 $ret .= '<input type="hidden" id="search_component_params_hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
12647 // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
12648
12649 // TODO : Use $arrayoffiltercriterias instead of $arrayofcriterias
12650 // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
12651 foreach ($arrayofcriterias as $criteria) {
12652 foreach ($criteria as $criteriafamilykey => $criteriafamilyval) {
12653 if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
12654 continue;
12655 }
12656 if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
12657 continue;
12658 }
12659 if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
12660 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
12661 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
12662 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
12663 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
12664 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
12665 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
12666 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
12667 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
12668 } else {
12669 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
12670 }
12671 }
12672 }
12673
12674 $ret .= '</div>';
12675
12676 $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";
12677 $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" id="search_component_params_input" name="search_component_params_input" class="noborderall search_component_input" value="">';
12678
12679 $ret .= '</div>';
12680 $ret .= '</div>';
12681
12682 $ret .= '<script>
12683 jQuery(".tagsearchdelete").click(function(e) {
12684 var filterid = $(this).parents().attr("data-ufilterid");
12685 console.log("We click to delete the criteria nb "+filterid);
12686
12687 // Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
12688 var newparamstring = \'\';
12689 $(\'.tagsearch\').each(function(index, element) {
12690 tmpfilterid = $(this).attr("data-ufilterid");
12691 if (tmpfilterid != filterid) {
12692 // We keep this criteria
12693 if (newparamstring == \'\') {
12694 newparamstring = $(this).attr("data-ufilter");
12695 } else {
12696 newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
12697 }
12698 }
12699 });
12700 console.log("newparamstring = "+newparamstring);
12701
12702 jQuery("#search_component_params_hidden").val(newparamstring);
12703
12704 // We repost the form
12705 $(this).closest(\'form\').submit();
12706 });
12707
12708 jQuery("#search_component_params_input").keydown(function(e) {
12709 console.log("We press a key on the filter field that is "+jQuery("#search_component_params_input").val());
12710 console.log(e.which);
12711 if (jQuery("#search_component_params_input").val() == "" && e.which == 8) {
12712 /* We click on back when the input field is already empty */
12713 event.preventDefault();
12714 jQuery("#divsearch_component_params .tagsearch").last().remove();
12715 /* Regenerate content of search_component_params_hidden from remaining .tagsearch */
12716 var s = "";
12717 jQuery("#divsearch_component_params .tagsearch").each(function( index ) {
12718 if (s != "") {
12719 s = s + " AND ";
12720 }
12721 s = s + $(this).attr("data-ufilter");
12722 });
12723 console.log("New value for search_component_params_hidden = "+s);
12724 jQuery("#search_component_params_hidden").val(s);
12725 }
12726 });
12727
12728 </script>
12729 ';
12730
12731 // Convert $arrayoffiltercriterias into a json object that can be used in jquery to build the search component dynamically
12732 $arrayoffiltercriterias_json = json_encode($arrayoffiltercriterias);
12733 $ret .= '<script>
12734 var arrayoffiltercriterias = ' . $arrayoffiltercriterias_json . ';
12735 </script>';
12736
12737
12738 $arrayoffilterfieldslabel = array();
12739 foreach ($arrayoffiltercriterias as $key => $val) {
12740 $arrayoffilterfieldslabel[$key]['label'] = $val['label'];
12741 $arrayoffilterfieldslabel[$key]['data-type'] = $val['type'];
12742 }
12743
12744 // Adding the div for search assistance
12745 $ret .= '<div class="search-component-assistance">';
12746 $ret .= '<div>';
12747
12748 $ret .= '<p class="assistance-title">' . img_picto('', 'filter') . ' ' . $langs->trans('FilterAssistance') . ' </p>';
12749
12750 $ret .= '<p class="assistance-errors error" style="display:none">' . $langs->trans('AllFieldsRequired') . ' </p>';
12751
12752 $ret .= '<div class="operand">';
12753 $ret .= $form->selectarray('search_filter_field', $arrayoffilterfieldslabel, '', $langs->trans("Fields"), 0, 0, '', 0, 0, 0, '', 'width200 combolargeelem', 1);
12754 $ret .= '</div>';
12755
12756 $ret .= '<span class="separator"></span>';
12757
12758 // Operator selector (will be populated dynamically)
12759 $ret .= '<div class="operator">';
12760 $ret .= '<select class="operator-selector width150" id="operator-selector"">';
12761 $ret .= '</select>';
12762 $ret .= '<script>$(document).ready(function() {';
12763 $ret .= ' $(".operator-selector").select2({';
12764 $ret .= ' placeholder: \'' . dol_escape_js($langs->transnoentitiesnoconv('Operator')) . '\'';
12765 $ret .= ' });';
12766 $ret .= '});</script>';
12767 $ret .= '</div>';
12768
12769 $ret .= '<span class="separator"></span>';
12770
12771 $ret .= '<div class="value">';
12772 // Input field for entering values
12773 $ret .= '<input type="text" class="flat width100 value-input" placeholder="' . dolPrintHTML($langs->trans('Value')) . '">';
12774
12775 // Date selector
12776 $dateOne = '';
12777 $ret .= '<span class="date-one" style="display:none">';
12778 $ret .= $form->selectDate(($dateOne ? $dateOne : -1), 'dateone', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '');
12779 $ret .= '</span>';
12780
12781 // Value selector (will be populated dynamically) based on search_filter_field value if a selected value has an array of values
12782 $ret .= '<select class="value-selector width150" id="value-selector" style="display:none">';
12783 $ret .= '</select>';
12784 $ret .= '<script>
12785 $(document).ready(function() {
12786 $("#value-selector").select2({
12787 placeholder: "' . dol_escape_js($langs->trans('Value')) . '"
12788 });
12789 $("#value-selector").hide();
12790 $("#value-selector").next(".select2-container").hide();
12791 });
12792 </script>';
12793
12794 $ret .= '</div>';
12795
12796 $ret .= '<div class="btn-div">';
12797 $ret .= '<button class="button buttongen button-save add-filter-btn" type="button">' . $langs->trans("addToFilter") . '</button>';
12798 $ret .= '</div>';
12799
12800 $ret .= '</div>';
12801 //$ret .= '</tbody></table>';
12802
12803 // End of the assistance div
12804 $ret .= '</div>';
12805
12806 // Script jQuery to show/hide the floating assistance
12807 $ret .= '<script>
12808 $(document).ready(function() {
12809 $("#search_component_params_input").on("click", function() {
12810 const inputPosition = $(this).offset();
12811 const inputHeight = $(this).outerHeight();
12812 $(".search-component-assistance").css({
12813 top: inputPosition.top + inputHeight + 5 + "px",
12814 left: $("#divsearch_component_params").position().left
12815 }).slideToggle(200);
12816 });
12817 $(document).on("click", function(e) {
12818 if (!$(e.target).closest("#search_component_params_input, .search-component-assistance, #ui-datepicker-div").length) {
12819 $(".search-component-assistance").hide();
12820 }
12821 });
12822 });
12823 </script>';
12824
12825 $ret .= '<script>
12826 $(document).ready(function() {
12827 $(".search_filter_field").on("change", function() {
12828 console.log("We change search_filter_field");
12829
12830 let maybenull = 0;
12831 const selectedField = $(this).find(":selected");
12832 let fieldType = selectedField.data("type");
12833 const selectedFieldValue = selectedField.val();
12834
12835 // If the selected field has an array of values then ask toshow the value selector instead of the value input
12836 if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12837 fieldType = "select";
12838 }
12839
12840 // If the selected field may be null then ask to append the "IsDefined" and "IsNotDefined" operators
12841 if (arrayoffiltercriterias[selectedFieldValue]["maybenull"] !== undefined) {
12842 maybenull = 1;
12843 }
12844 const operators = getOperatorsForFieldType(fieldType, maybenull);
12845 const operatorSelector = $(".operator-selector");
12846
12847 // Clear existing options
12848 operatorSelector.empty();
12849
12850 // Populate operators
12851 Object.entries(operators).forEach(function([operator, label]) {
12852 operatorSelector.append("<option value=\'" + operator + "\'>" + label + "</option>");
12853 });
12854
12855 operatorSelector.trigger("change.select2");
12856
12857 // Clear and hide all input elements initially
12858 $(".value-input, .dateone, .datemonth, .dateyear").val("").hide();
12859 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12860 $("#dateone").datepicker("setDate", null);
12861 $(".date-one, .date-month, .date-year").hide();
12862 $("#value-selector").val("").hide();
12863 $("#value-selector").next(".select2-container").hide();
12864 $("#value-selector").val(null).trigger("change.select2");
12865
12866 if (fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp") {
12867 $(".date-one").show();
12868 } else if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12869 var arrayofkeyval = arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"];
12870 var valueSelector = $("#value-selector");
12871 valueSelector.empty();
12872 Object.entries(arrayofkeyval).forEach(function([key, val]) {
12873 valueSelector.append("<option value=\'" + key + "\'>" + val + "</option>");
12874 });
12875 valueSelector.trigger("change.select2");
12876
12877 $("#value-selector").show();
12878 $("#value-selector").next(".select2-container").show();
12879 } else {
12880 $(".value-input").show();
12881 }
12882 });
12883
12884 $("#operator-selector").on("change", function() {
12885 console.log("We change operator-selector");
12886
12887 const selectedOperator = $(this).find(":selected").val();
12888 if (selectedOperator === "IsDefined" || selectedOperator === "IsNotDefined") {
12889 // Disable all value input elements
12890 $(".value-input, .dateone, .datemonth, .dateyear").val("").prop("disabled", true);
12891 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12892 $("#dateone").datepicker("setDate", null).datepicker("option", "disabled", true);
12893 $(".date-one, .date-month, .date-year").prop("disabled", true);
12894 $("#value-selector").val("").prop("disabled", true);
12895 $("#value-selector").val(null).trigger("change.select2");
12896 } else {
12897 // Enable all value input elements
12898 $(".value-input, .dateone, .datemonth, .dateyear").prop("disabled", false);
12899 $(".date-one, .date-month, .date-year").prop("disabled", false);
12900 $("#dateone").datepicker("option", "disabled", false);
12901 $("#value-selector").prop("disabled", false);
12902 }
12903 });
12904
12905 $(".add-filter-btn").on("click", function(event) {
12906 console.log("We click on add-filter-btn");
12907
12908 event.preventDefault();
12909
12910 const field = $(".search_filter_field").val();
12911 const operator = $(".operator-selector").val();
12912 let value = $(".value-input").val();
12913 const fieldType = $(".search_filter_field").find(":selected").data("type");
12914
12915 if (["date", "datetime", "timestamp"].includes(fieldType)) {
12916 const year = $("#dateoneyear").val().toString().padStart(4, "0");;
12917 const month = $("#dateonemonth").val().toString().padStart(2, "0");
12918 const day = $("#dateoneday").val().toString().padStart(2, "0");
12919 value = `${year}-${month}-${day}`;
12920 console.log("value="+value);
12921 }
12922
12923 // If the selected field has an array of values then take the selected value
12924 if (arrayoffiltercriterias[field]["arrayofkeyval"] !== undefined) {
12925 value = $("#value-selector").val();
12926 }
12927
12928 // If the operator is "IsDefined" or "IsNotDefined" then set the value to 1 (it will not be used)
12929 if (operator === "IsDefined" || operator === "IsNotDefined") {
12930 value = "1";
12931 }
12932
12933 const filterString = generateFilterString(field, operator, value, fieldType);
12934
12935 // Submit the form
12936 if (filterString !== "" && field !== "" && operator !== "" && value !== "") {
12937 $("#search_component_params_input").val($("#search_component_params_input").val() + " " + filterString);
12938 $("#search_component_params_input").closest("form").submit();
12939 } else {
12940 $(".assistance-errors").show();
12941 }
12942 });
12943 });
12944 </script>';
12945
12946 return $ret;
12947 }
12948
12960 public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0, $selected = 0, $morecss = '')
12961 {
12962 global $langs, $user;
12963
12964 $retstring = '';
12965
12966 $TModels = array();
12967
12968 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
12969 $formmail = new FormMail($this->db);
12970 $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
12971
12972 if ($default) {
12973 $TModels[0] = $langs->trans('DefaultMailModel');
12974 }
12975 if ($result > 0) {
12976 foreach ($formmail->lines_model as $model) {
12977 $TModels[(int) $model->id] = $model->label;
12978 }
12979 }
12980
12981 $retstring .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
12982
12983 foreach ($TModels as $id_model => $label_model) {
12984 $retstring .= '<option value="' . $id_model . '"';
12985 if (!empty($selected) && ((int) $selected) == $id_model) {
12986 $retstring .= "selected";
12987 }
12988 $retstring .= ">" . $label_model . "</option>";
12989 }
12990
12991 $retstring .= "</select>";
12992
12993 if ($addjscombo) {
12994 $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
12995 }
12996
12997 return $retstring;
12998 }
12999
13011 public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = false, $morecss = '', $dol_openinpopup = '')
13012 {
13013 global $langs;
13014
13015 $buttons = array();
13016
13017 $save = array(
13018 'name' => 'save',
13019 'label_key' => $save_label,
13020 );
13021
13022 if ($save_label == 'Create' || $save_label == 'Add') {
13023 $save['name'] = 'add';
13024 } elseif ($save_label == 'Modify') {
13025 $save['name'] = 'edit';
13026 }
13027
13028 $cancel = array(
13029 'name' => 'cancel',
13030 'label_key' => 'Cancel',
13031 );
13032
13033 // If MAIN_BUTTON_POSITION_FIRST_OR_LEFT not set, default is to have main action first, then complementary, then cancel at end
13034 if (!getDolGlobalInt('MAIN_BUTTON_POSITION_FIRST_OR_LEFT')) {
13035 !empty($save_label) ? $buttons[] = $save : '';
13036 if (!empty($morebuttons)) {
13037 $buttons[] = $morebuttons;
13038 }
13039 !empty($cancel_label) ? $buttons[] = $cancel : '';
13040 } else {
13041 if (!empty($morebuttons)) {
13042 $buttons[] = $morebuttons;
13043 }
13044 !empty($cancel_label) ? $buttons[] = $cancel : '';
13045 !empty($save_label) ? $buttons[] = $save : '';
13046 }
13047
13048 $retstring = $withoutdiv ? '' : '<div class="center">';
13049
13050 foreach ($buttons as $button) {
13051 $addclass = empty($button['addclass']) ? '' : $button['addclass'];
13052 $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'])) . '">';
13053 }
13054 $retstring .= $withoutdiv ? '' : '</div>';
13055
13056 if ($dol_openinpopup) {
13057 $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . dol_escape_htmltag($dol_openinpopup) . ' context, so we enable the close of dialog on cancel -->' . "\n";
13058 $retstring .= '<script nonce="' . getNonce() . '">';
13059 $retstring .= 'jQuery(".button-cancel").click(function(e) {
13060 e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . dol_escape_js($dol_openinpopup) . '\');
13061 window.parent.jQuery(\'#idfordialog' . dol_escape_js($dol_openinpopup) . '\').dialog(\'close\');
13062 });';
13063 $retstring .= '</script>';
13064 }
13065
13066 return $retstring;
13067 }
13068
13069
13070 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
13071
13078 {
13079 // phpcs:enable
13080 global $langs;
13081
13082 $num = count($this->cache_invoice_subtype);
13083 if ($num > 0) {
13084 return 0; // Cache already loaded
13085 }
13086
13087 dol_syslog(__METHOD__, LOG_DEBUG);
13088
13089 $sql = "SELECT rowid, code, label as label";
13090 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
13091 $sql .= " WHERE active = 1";
13092
13093 $resql = $this->db->query($sql);
13094 if ($resql) {
13095 $num = $this->db->num_rows($resql);
13096 $i = 0;
13097 while ($i < $num) {
13098 $obj = $this->db->fetch_object($resql);
13099
13100 // If translation exists, we use it, otherwise we take the default wording
13101 $label = ($langs->trans("InvoiceSubtype" . $obj->rowid) != "InvoiceSubtype" . $obj->rowid) ? $langs->trans("InvoiceSubtype" . $obj->rowid) : (($obj->label != '-') ? $obj->label : '');
13102 $this->cache_invoice_subtype[$obj->rowid]['rowid'] = $obj->rowid;
13103 $this->cache_invoice_subtype[$obj->rowid]['code'] = $obj->code;
13104 $this->cache_invoice_subtype[$obj->rowid]['label'] = $label;
13105 $i++;
13106 }
13107
13108 $this->cache_invoice_subtype = dol_sort_array($this->cache_invoice_subtype, 'code', 'asc', 0, 0, 1);
13109
13110 return $num;
13111 } else {
13112 dol_print_error($this->db);
13113 return -1;
13114 }
13115 }
13116
13117
13128 public function getSelectInvoiceSubtype($selected = 0, $htmlname = 'subtypeid', $addempty = 0, $noinfoadmin = 0, $morecss = '')
13129 {
13130 global $langs, $user;
13131
13132 $out = '';
13133 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
13134
13135 $this->load_cache_invoice_subtype();
13136
13137 $out .= '<select id="' . $htmlname . '" class="flat selectsubtype' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
13138 if ($addempty) {
13139 $out .= '<option value="0">&nbsp;</option>';
13140 }
13141
13142 foreach ($this->cache_invoice_subtype as $rowid => $subtype) {
13143 $label = $subtype['label'];
13144 $out .= '<option value="' . $subtype['rowid'] . '"';
13145 if ($selected == $subtype['rowid']) {
13146 $out .= ' selected="selected"';
13147 }
13148 $out .= '>';
13149 $out .= $label;
13150 $out .= '</option>';
13151 }
13152
13153 $out .= '</select>';
13154 if ($user->admin && empty($noinfoadmin)) {
13155 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
13156 }
13157 $out .= ajax_combobox($htmlname);
13158
13159 return $out;
13160 }
13161
13171 public function getSearchFilterToolInput($dataTarget, $htmlName = 'search-tools-input', $value = '', $params = [])
13172 {
13173 global $langs;
13174
13175 $attr = array(
13176 'type' => 'search',
13177 'name' => $htmlName,
13178 'value' => $value,
13179 'class' => "search-tool-input",
13180 'placeholder' => $langs->trans('Search'),
13181 'autocomplete' => 'off'
13182 );
13183
13184 // Optional data attr
13185 // 'autofocus' : will set auto focus on field ,
13186 // data-counter-target : will get count results
13187 // data-no-item-target : will be display if count results is 0
13188
13189 if ($dataTarget !== false) {
13190 $attr['data-search-tool-target'] = $dataTarget;
13191 }
13192
13193 // Override attr
13194 if (!empty($params['attr']) && is_array($params['attr'])) {
13195 foreach ($params['attr'] as $key => $value) {
13196 if ($key == 'class') {
13197 $attr['class'] .= ' '.$value;
13198 } elseif ($key == 'classOverride') {
13199 $attr['class'] = $value;
13200 } else {
13201 $attr[$key] = $value;
13202 }
13203 }
13204 }
13205
13206 // automatic add tooltip when title is detected
13207 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13208 $attr['class'] .= ' classfortooltip';
13209 }
13210
13211 $TCompiledAttr = [];
13212 foreach ($attr as $key => $value) {
13213 if (in_array($key, ['data-target'])
13214 || (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr']))) { // Not recommended
13215 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13216 } else {
13217 $value = dolPrintHTMLForAttribute($value);
13218 }
13219
13220 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13221 }
13222
13223 $compiledAttributes = implode(' ', $TCompiledAttr);
13224
13225
13226 return '<div class="search-tool-container"><input '.$compiledAttributes.'></div>';
13227 }
13228
13242 public function inputType($type, $name, $value = '', $id = '', $morecss = '', $moreparam = '', $label = '', $addInputLabel = '')
13243 {
13244 $out = '';
13245 if ($label != '') {
13246 $out .= '<label for="' . dolPrintHTMLForAttribute($id) . '">';
13247 }
13248 $out .= '<input type="' . dolPrintHTMLForAttribute($type) . '"';
13249 $out .= ' class="flat valignmiddle maxwidthonsmartphone ' . dolPrintHTMLForAttribute($morecss) . '"';
13250 if ($id != '') {
13251 $out .= ' id="' . dolPrintHTMLForAttribute($id) . '"';
13252 }
13253 $out .= ' name="' . dolPrintHTMLForAttribute($name) . '"';
13254 $out .= ' value="' . dolPrintHTMLForAttribute($value) . '" ';
13255 $out .= ($moreparam ? ' ' . $moreparam : '');
13256 $out .= ' />' . $addInputLabel;
13257 if ($label != '') {
13258 $out .= $label . '</label>';
13259 }
13260
13261 return $out;
13262 }
13263
13276 public function inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData = [], $morecss = 'minwidth75', $moreparam = '')
13277 {
13278 $out = "
13279 <script>
13280 $(document).ready(function () {
13281 $('#" . $htmlName . "').select2({
13282 ajax: {
13283 url: '" . $ajaxUrl . "',
13284 dataType: 'json',
13285 delay: 250, // wait 250 milliseconds before triggering the request
13286 data: function (params) {
13287 var query = {
13288 search: params.term,
13289 page: params.page || 1";
13290 if (!empty($ajaxData) && is_array($ajaxData)) {
13291 foreach ($ajaxData as $key => $value) {
13292 $out .= ", " . $key . ": '" . $value . "'";
13293 }
13294 }
13295 $out .= "
13296 }
13297 return query;
13298 }
13299 }
13300 })
13301 });
13302 </script>";
13303
13304 $out .= $this->selectarray($htmlName, $array, $id, 0, 0, 0, $moreparam, 0, 0, 0, '', $morecss);
13305
13306 return $out;
13307 }
13308
13318 public function inputHtml($htmlName, $value, $morecss = '', $moreparam = '')
13319 {
13320 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13321 $doleditor = new DolEditor($htmlName, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
13322
13323 return (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13324 }
13325
13336 public function inputText($htmlName, $value, $morecss = '', $moreparam = '', $options = array())
13337 {
13338 global $langs;
13339
13340 $out = '';
13341 if (!empty($options)) {
13342 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
13343 $out .= $this->selectarray($htmlName . "_multiinput", $options, '', 1, 0, 0, $moreparam, 0, 0, 0, '', "flat maxwidthonphone" . $morecss);
13344 $out .= '<input id="' . $htmlName . '_multiinputadd" type="button" class="button" value="' . $langs->trans("Add") . '">';
13345 $out .= "<script>";
13346 $out .= '
13347 function handlemultiinputdisabling(htmlname){
13348 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
13349 multiinput = $("#"+htmlname+"_multiinput");
13350 multiinput.find("option").each(function(){
13351 tmpval = $("#"+htmlname).val();
13352 tmpvalarray = tmpval.split("\n");
13353 valtotest = $(this).val();
13354 if(tmpvalarray.includes(valtotest)){
13355 $(this).prop("disabled",true);
13356 } else {
13357 if($(this).prop("disabled") == true){
13358 console.log(valtotest)
13359 $(this).prop("disabled", false);
13360 }
13361 }
13362 });
13363 }
13364
13365 $(document).ready(function () {
13366 $("#' . $htmlName . '_multiinputadd").on("click",function() {
13367 tmpval = $("#' . $htmlName . '").val();
13368 tmpvalarray = tmpval.split(",");
13369 valtotest = $("#' . $htmlName . '_multiinput").val();
13370 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
13371 console.log("We add the selected value to the text area ' . $htmlName . '");
13372 if(tmpval == ""){
13373 tmpval = valtotest;
13374 } else {
13375 tmpval = tmpval + "\n" + valtotest;
13376 }
13377 $("#' . $htmlName . '").val(tmpval);
13378 handlemultiinputdisabling("' . $htmlName . '");
13379 $("#' . $htmlName . '_multiinput").val(-1);
13380 } else {
13381 console.log("We add nothing the text area ' . $htmlName . '");
13382 }
13383 });
13384 $("#' . $htmlName . '").on("change",function(){
13385 handlemultiinputdisabling("' . $htmlName . '");
13386 });
13387 handlemultiinputdisabling("' . $htmlName . '");
13388 })';
13389 $out .= "</script>";
13390 $value = str_replace(',', "\n", $value);
13391 }
13392
13393 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13394 $doleditor = new DolEditor($htmlName, (string) $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
13395 $out .= (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13396
13397 return $out;
13398 }
13399
13410 public function inputRadio($htmlName, $options, $selectedValue, $morecss = '', $moreparam = '')
13411 {
13412 $out = '';
13413 foreach ($options as $optionKey => $optionLabel) {
13414 $selected = ((string) $selectedValue) === ((string) $optionKey) ? ' checked="checked"' : '';
13415 $optionId = $htmlName . '_' . $optionKey;
13416 $out .= '<input class="flat' . $morecss . '" type="radio" name="' . $htmlName . '" id="' . $optionId . '" value="' . dolPrintHTMLForAttribute((string) $optionKey) . '"' . $selected . $moreparam . '/><label for="' . $optionId . '">' . $optionLabel . '</label><br>';
13417 }
13418
13419 return $out;
13420 }
13421
13432 public function inputStars($htmlName, $size, $value, $morecss = '', $moreparam = '')
13433 {
13434 $out = '<input type="hidden" class="flat ' . $morecss . '" name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '"' . $moreparam . '>';
13435 $out .= '<div class="star-selection" id="' . $htmlName . '_selection">';
13436 for ($i = 1; $i <= $size; $i++) {
13437 $out .= '<span class="star" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13438 }
13439 $out .= '</div>';
13440 $out .= '<script>
13441 jQuery(function($) { /* commonobject.class.php 1 */
13442 let container = $("#' . $htmlName . '_selection");
13443 let selectedStars = parseInt($("#' . $htmlName . '").val()) || 0;
13444 container.find(".star").each(function() {
13445 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13446 });
13447 container.find(".star").on("mouseover", function() {
13448 let selectedStar = $(this).data("value");
13449 container.find(".star").each(function() {
13450 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
13451 });
13452 });
13453 container.on("mouseout", function() {
13454 container.find(".star").each(function() {
13455 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13456 });
13457 });
13458 container.find(".star").off("click").on("click", function() {
13459 selectedStars = $(this).data("value");
13460 if (selectedStars === 1 && $("#' . $htmlName . '").val() == 1) {
13461 selectedStars = 0;
13462 }
13463 $("#' . $htmlName . '").val(selectedStars);
13464 container.find(".star").each(function() {
13465 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13466 });
13467 });
13468 });
13469 </script>';
13470
13471 return $out;
13472 }
13473
13483 public function inputIcon($htmlName, $value, $morecss = '', $moreparam = '')
13484 {
13485 global $langs;
13486
13487 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
13488 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
13489 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
13490 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
13491 */
13492 $out = '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat ' . $morecss . ' maxwidthonsmartphone"';
13493 $out .= ' name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '" ' . ((string) $moreparam) . '>';
13494 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
13495 $out .= '<script>';
13496 $options = "{ title: '<b>" . $langs->trans("IconFieldSelector") . "</b>', placement: 'right', showFooter: false, templates: {";
13497 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
13498 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
13499 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
13500 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
13501 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
13502 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"" . $langs->trans("TypeToFilter") . "\" />',";
13503 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
13504 $options .= " <div class=\"arrow\" ></div>";
13505 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
13506 $options .= " <div class=\"popover-content \" ></div>";
13507 $options .= "</div>'}}";
13508 $out .= "$('#" . $htmlName . "').iconpicker(" . $options . ");";
13509 $out .= '</script>';
13510 }
13511
13512 return $out;
13513 }
13514
13523 public function inputGeoPoint($htmlName, $value, $type = '')
13524 {
13525 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13526 require_once DOL_DOCUMENT_ROOT . '/core/class/geomapeditor.class.php';
13527 $dolgeophp = new DolGeoPHP($this->db);
13528 $geomapeditor = new GeoMapEditor();
13529
13530 $geojson = '{}';
13531 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
13532 if (!empty($value)) {
13533 $tmparray = $dolgeophp->parseGeoString($value);
13534 $geojson = $tmparray['geojson'];
13535 $centroidjson = $tmparray['centroidjson'];
13536 }
13537
13538 return $geomapeditor->getHtml($htmlName, $geojson, $centroidjson, $type);
13539 }
13540
13547 public function outputMultiValues($values)
13548 {
13549 $out = '';
13550 $toPrint = array();
13551 $values = is_array($values) ? $values : array();
13552
13553 foreach ($values as $value) {
13554 $toPrint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $value . '</li>';
13555 }
13556 if (!empty($toPrint)) {
13557 $out = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toPrint) . '</ul></div>';
13558 }
13559
13560 return $out;
13561 }
13562
13570 public function outputStars($size, $value)
13571 {
13572 $out = '<div class="star-selection" data-value="' . dolPrintHTMLForAttribute((string) $value) . '">';
13573 for ($i = 1; $i <= $size; $i++) {
13574 $out .= '<span class="star' . ($i <= $value ? ' active' : '') . '" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13575 }
13576 $out .= '</div>';
13577
13578 return $out;
13579 }
13580
13587 public function outputIcon($value)
13588 {
13589 $out = '<span class="' . dolPrintHTMLForAttribute((string) $value) . '"></span>';
13590
13591 return $out;
13592 }
13593
13601 public function outputGeoPoint($value, $type)
13602 {
13603 $out = '';
13604
13605 if (!empty($value)) {
13606 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13607 $dolgeophp = new DolGeoPHP($this->db);
13608 if ($type == 'point') {
13609 $out = $dolgeophp->getXYString($value);
13610 } else { // multipts, linestrg, polygon
13611 $out = $dolgeophp->getPointString($value);
13612 }
13613 }
13614
13615 return $out;
13616 }
13617
13632 public function getNomUrl(&$object, $withpicto = 0, $option = '', $maxlength = 0, $save_lastsearch_value = -1, $notooltip = 0, $morecss = '', $add_label = 0, $sep = ' - ')
13633 {
13634 if (is_object($object) && method_exists($object, 'getNomUrl')) {
13635 $out = $object->getNomUrl($withpicto, $option, $maxlength, $save_lastsearch_value, $notooltip, $morecss, $add_label, $sep);
13636 return $out;
13637 } else {
13638 return '';
13639 }
13640 }
13641}
$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:594
$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:168
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_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
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.
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.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='')
Show information in HTML for admin users or standard users.
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.
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.
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.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
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, ....
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
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.
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.
isModEnabled($module)
Is Dolibarr module enabled.
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...
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formatted text with and (WARNING: string must not have mixed and br sep...
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
a disabled
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
Class to generate the form for creating a new ticket.
dol_get_trunk_prefix($db, $phone_code)
Get the national trunk prefix for a phone code.
dol_parse_phone($phone)
Parse a stored phone number into country code and number parts.
Definition phone.lib.php:33
dol_get_phone_code_from_country($db, $country_id)
Get the phone calling code for a country.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
getMaxFileSizeArray()
Return the max allowed for file upload.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
dolDecrypt($chain, $key='', $patterntotest='')
Decode a string with a symmetric encryption.
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET,...
Definition waf.inc.php:103