Passed
Branch develop (7a9ea1)
by
unknown
36:03
created

Form::load_cache_conditions_paiements()   B

Complexity

Conditions 6

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 24
nop 0
dl 0
loc 38
rs 8.9137
c 0
b 0
f 0
1
<?php
2
/* Copyright (c) 2002-2007  Rodolphe Quiedeville    <[email protected]>
3
 * Copyright (C) 2004-2012  Laurent Destailleur     <[email protected]>
4
 * Copyright (C) 2004       Benoit Mortier          <[email protected]>
5
 * Copyright (C) 2004       Sebastien Di Cintio     <[email protected]>
6
 * Copyright (C) 2004       Eric Seigne             <[email protected]>
7
 * Copyright (C) 2005-2017  Regis Houssin           <[email protected]>
8
 * Copyright (C) 2006       Andre Cianfarani        <[email protected]>
9
 * Copyright (C) 2006       Marc Barilley/Ocebo     <[email protected]>
10
 * Copyright (C) 2007       Franky Van Liedekerke   <[email protected]>
11
 * Copyright (C) 2007       Patrick Raguin          <[email protected]>
12
 * Copyright (C) 2010       Juanjo Menent           <[email protected]>
13
 * Copyright (C) 2010-2021  Philippe Grand          <[email protected]>
14
 * Copyright (C) 2011       Herve Prot              <[email protected]>
15
 * Copyright (C) 2012-2016  Marcos García           <[email protected]>
16
 * Copyright (C) 2012       Cedric Salvador         <[email protected]>
17
 * Copyright (C) 2012-2015  Raphaël Doursenaud      <[email protected]>
18
 * Copyright (C) 2014-2020  Alexandre Spangaro      <[email protected]>
19
 * Copyright (C) 2018-2021  Ferran Marcet           <[email protected]>
20
 * Copyright (C) 2018-2021  Frédéric France         <[email protected]>
21
 * Copyright (C) 2018       Nicolas ZABOURI	        <[email protected]>
22
 * Copyright (C) 2018       Christophe Battarel     <[email protected]>
23
 * Copyright (C) 2018       Josep Lluis Amador      <[email protected]>
24
 *
25
 * This program is free software; you can redistribute it and/or modify
26
 * it under the terms of the GNU General Public License as published by
27
 * the Free Software Foundation; either version 3 of the License, or
28
 * (at your option) any later version.
29
 *
30
 * This program is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33
 * GNU General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU General Public License
36
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
37
 */
38
39
/**
40
 *	\file       htdocs/core/class/html.form.class.php
41
 *  \ingroup    core
42
 *	\brief      File of class with all html predefined components
43
 */
44
45
46
/**
47
 *	Class to manage generation of HTML components
48
 *	Only common components must be here.
49
 *
50
 *  TODO Merge all function load_cache_* and loadCache* (except load_cache_vatrates) into one generic function loadCacheTable
51
 */
52
class Form
53
{
54
	/**
55
	 * @var DoliDB Database handler.
56
	 */
57
	public $db;
58
59
	/**
60
	 * @var string Error code (or message)
61
	 */
62
	public $error = '';
63
64
	/**
65
	 * @var string[]    Array of error strings
66
	 */
67
	public $errors = array();
68
69
	public $num;
70
71
	// Cache arrays
72
	public $cache_types_paiements = array();
73
	public $cache_conditions_paiements = array();
74
	public $cache_transport_mode = array();
75
	public $cache_availability = array();
76
	public $cache_demand_reason = array();
77
	public $cache_types_fees = array();
78
	public $cache_vatrates = array();
79
80
81
	/**
82
	 * Constructor
83
	 *
84
	 * @param		DoliDB		$db      Database handler
85
	 */
86
	public function __construct($db)
87
	{
88
		$this->db = $db;
89
	}
90
91
	/**
92
	 * Output key field for an editable field
93
	 *
94
	 * @param   string	$text			Text of label or key to translate
95
	 * @param   string	$htmlname		Name of select field ('edit' prefix will be added)
96
	 * @param   string	$preselected    Value to show/edit (not used in this function)
97
	 * @param	object	$object			Object
98
	 * @param	boolean	$perm			Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
99
	 * @param	string	$typeofdata		Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'checkbox:ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
100
	 * @param	string	$moreparam		More param to add on a href URL.
101
	 * @param   int     $fieldrequired  1 if we want to show field as mandatory using the "fieldrequired" CSS.
102
	 * @param   int     $notabletag     1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' '
103
	 * @param	string	$paramid		Key of parameter for id ('id', 'socid')
104
	 * @param	string	$help			Tooltip help
105
	 * @return	string					HTML edit field
106
	 */
107
	public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
108
	{
109
		global $conf, $langs;
110
111
		$ret = '';
112
113
		// TODO change for compatibility
114
		if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;/', $typeofdata)) {
115
			if (!empty($perm)) {
116
				$tmp = explode(':', $typeofdata);
117
				$ret .= '<div class="editkey_'.$tmp[0].(!empty($tmp[1]) ? ' '.$tmp[1] : '').'" id="'.$htmlname.'">';
118
				if ($fieldrequired) {
119
					$ret .= '<span class="fieldrequired">';
120
				}
121
				if ($help) {
122
					$ret .= $this->textwithpicto($langs->trans($text), $help);
123
				} else {
124
					$ret .= $langs->trans($text);
125
				}
126
				if ($fieldrequired) {
127
					$ret .= '</span>';
128
				}
129
				$ret .= '</div>'."\n";
130
			} else {
131
				if ($fieldrequired) {
132
					$ret .= '<span class="fieldrequired">';
133
				}
134
				if ($help) {
135
					$ret .= $this->textwithpicto($langs->trans($text), $help);
136
				} else {
137
					$ret .= $langs->trans($text);
138
				}
139
				if ($fieldrequired) {
140
					$ret .= '</span>';
141
				}
142
			}
143
		} else {
144
			if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
145
				$ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
146
			}
147
			if ($fieldrequired) {
148
				$ret .= '<span class="fieldrequired">';
149
			}
150
			if ($help) {
151
				$ret .= $this->textwithpicto($langs->trans($text), $help);
152
			} else {
153
				$ret .= $langs->trans($text);
154
			}
155
			if ($fieldrequired) {
156
				$ret .= '</span>';
157
			}
158
			if (!empty($notabletag)) {
159
				$ret .= ' ';
160
			}
161
			if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
162
				$ret .= '</td>';
163
			}
164
			if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
165
				$ret .= '<td class="right">';
166
			}
167
			if ($htmlname && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
168
				$ret .= '<a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit'.$htmlname.'&amp;'.$paramid.'='.$object->id.$moreparam.'">'.img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)).'</a>';
169
			}
170
			if (!empty($notabletag) && $notabletag == 1) {
171
				$ret .= ' : ';
172
			}
173
			if (!empty($notabletag) && $notabletag == 3) {
174
				$ret .= ' ';
175
			}
176
			if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
177
				$ret .= '</td>';
178
			}
179
			if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
180
				$ret .= '</tr></table>';
181
			}
182
		}
183
184
		return $ret;
185
	}
186
187
	/**
188
	 * Output value of a field for an editable field
189
	 *
190
	 * @param	string	$text			Text of label (not used in this function)
191
	 * @param	string	$htmlname		Name of select field
192
	 * @param	string	$value			Value to show/edit
193
	 * @param	object	$object			Object
194
	 * @param	boolean	$perm			Permission to allow button to edit parameter
195
	 * @param	string	$typeofdata		Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datepickerhour', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...')
196
	 * @param	string	$editvalue		When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of value). Use '' to use same than $value
197
	 * @param	object	$extObject		External object
198
	 * @param	mixed	$custommsg		String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
199
	 * @param	string	$moreparam		More param to add on the form action href URL
200
	 * @param   int     $notabletag     Do no output table tags
201
	 * @param	string	$formatfunc		Call a specific function to output field
202
	 * @param	string	$paramid		Key of parameter for id ('id', 'socid')
203
	 * @return  string					HTML edit field
204
	 */
205
	public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 0, $formatfunc = '', $paramid = 'id')
206
	{
207
		global $conf, $langs, $db;
208
209
		$ret = '';
210
211
		// Check parameters
212
		if (empty($typeofdata)) {
213
			return 'ErrorBadParameter';
214
		}
215
216
		// When option to edit inline is activated
217
		if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
218
			$ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
219
		} else {
220
			$editmode = (GETPOST('action', 'aZ09') == 'edit'.$htmlname);
221
			if ($editmode) {
222
				$ret .= "\n";
223
				$ret .= '<form method="post" action="'.$_SERVER["PHP_SELF"].($moreparam ? '?'.$moreparam : '').'">';
224
				$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
225
				$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
226
				$ret .= '<input type="hidden" name="'.$paramid.'" value="'.$object->id.'">';
227
				if (empty($notabletag)) {
228
					$ret .= '<table class="nobordernopadding centpercent">';
229
				}
230
				if (empty($notabletag)) {
231
					$ret .= '<tr><td>';
232
				}
233
				if (preg_match('/^(string|safehtmlstring|email)/', $typeofdata)) {
234
					$tmp = explode(':', $typeofdata);
235
					$ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($editvalue ? $editvalue : $value).'"'.($tmp[1] ? ' size="'.$tmp[1].'"' : '').' autofocus>';
236
				} elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
237
					$tmp = explode(':', $typeofdata);
238
					$valuetoshow = price2num($editvalue ? $editvalue : $value);
239
					$ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($valuetoshow != '' ?price($valuetoshow) : '').'"'.($tmp[1] ? ' size="'.$tmp[1].'"' : '').' autofocus>';
240
				} elseif (preg_match('/^(checkbox)/', $typeofdata)) {
241
					$tmp = explode(':', $typeofdata);
242
					$ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
243
				} elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {	// if wysiwyg is enabled $typeofdata = 'ckeditor'
244
					$tmp = explode(':', $typeofdata);
245
					$cols = $tmp[2];
246
					$morealt = '';
247
					if (preg_match('/%/', $cols)) {
248
						$morealt = ' style="width: '.$cols.'"';
249
						$cols = '';
250
					}
251
252
					$valuetoshow = ($editvalue ? $editvalue : $value);
253
					$ret .= '<textarea id="'.$htmlname.'" name="'.$htmlname.'" wrap="soft" rows="'.($tmp[1] ? $tmp[1] : '20').'"'.($cols ? ' cols="'.$cols.'"' : 'class="quatrevingtpercent"').$morealt.'" autofocus>';
254
					// textarea convert automatically entities chars into simple chars.
255
					// 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 wysiwig is off.
256
					$valuetoshow = str_replace('&', '&amp;', $valuetoshow);
257
					$ret .= dol_string_neverthesehtmltags($valuetoshow, array('textarea'));
258
					$ret .= '</textarea>';
259
				} elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
260
					$ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0);
261
				} elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
262
					$ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0);
263
				} elseif (preg_match('/^select;/', $typeofdata)) {
264
					$arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
265
					$arraylist = array();
266
					foreach ($arraydata as $val) {
267
						$tmp = explode(':', $val);
268
						$tmpkey = str_replace('|', ':', $tmp[0]);
269
						$arraylist[$tmpkey] = $tmp[1];
270
					}
271
					$ret .= $this->selectarray($htmlname, $arraylist, $value);
272
				} elseif (preg_match('/^ckeditor/', $typeofdata)) {
273
					$tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
274
					require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
275
					$doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), ($tmp[2] ? $tmp[2] : ''), ($tmp[3] ? $tmp[3] : '100'), ($tmp[1] ? $tmp[1] : 'dolibarr_notes'), 'In', ($tmp[5] ? $tmp[5] : 0), (isset($tmp[8]) ? ($tmp[8] ?true:false) : true), true, ($tmp[6] ? $tmp[6] : '20'), ($tmp[7] ? $tmp[7] : '100'));
276
					$ret .= $doleditor->Create(1);
277
				}
278
				if (empty($notabletag)) {
279
					$ret .= '</td>';
280
				}
281
282
				if (empty($notabletag)) {
283
					$ret .= '<td class="left">';
284
				}
285
				//else $ret.='<div class="clearboth"></div>';
286
				$ret .= '<input type="submit" class="smallpaddingimp button'.(empty($notabletag) ? '' : ' ').'" name="modify" value="'.$langs->trans("Modify").'">';
287
				if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
288
					$ret .= '<br>'."\n";
289
				}
290
				$ret .= '<input type="submit" class="smallpaddingimp button button-cancel'.(empty($notabletag) ? '' : ' ').'" name="cancel" value="'.$langs->trans("Cancel").'">';
291
				if (empty($notabletag)) {
292
					$ret .= '</td>';
293
				}
294
295
				if (empty($notabletag)) {
296
					$ret .= '</tr></table>'."\n";
297
				}
298
				$ret .= '</form>'."\n";
299
			} else {
300
				if (preg_match('/^(email)/', $typeofdata)) {
301
					$ret .= dol_print_email($value, 0, 0, 0, 0, 1);
302
				} elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
303
					$ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : '');
304
				} elseif (preg_match('/^(checkbox)/', $typeofdata)) {
305
					$tmp = explode(':', $typeofdata);
306
					$ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
307
				} elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
308
					$ret .= dol_htmlentitiesbr($value);
309
				} elseif (preg_match('/^safehtmlstring/', $typeofdata)) {
310
					$ret .= dol_string_onlythesehtmltags($value);
311
				} elseif (preg_match('/^restricthtml/', $typeofdata)) {
312
					$ret .= dol_string_onlythesehtmltags($value);
313
				} elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
314
					$ret .= '<span class="valuedate">'.dol_print_date($value, 'day').'</span>';
315
				} elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
316
					$ret .= '<span class="valuedate">'.dol_print_date($value, 'dayhour').'</span>';
317
				} elseif (preg_match('/^select;/', $typeofdata)) {
318
					$arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
319
					$arraylist = array();
320
					foreach ($arraydata as $val) {
321
						$tmp = explode(':', $val);
322
						$arraylist[$tmp[0]] = $tmp[1];
323
					}
324
					$ret .= $arraylist[$value];
325
					if ($htmlname == 'fk_product_type') {
326
						if ($value == 0) {
327
							$ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
328
						} else {
329
							$ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
330
						}
331
					}
332
				} elseif (preg_match('/^ckeditor/', $typeofdata)) {
333
					$tmpcontent = dol_htmlentitiesbr($value);
334
					if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) {
335
						$firstline = preg_replace('/<br>.*/', '', $tmpcontent);
336
						$firstline = preg_replace('/[\n\r].*/', '', $firstline);
337
						$tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
338
					}
339
					// We dont use dol_escape_htmltag to get the html formating active, but this need we must also
340
					// clean data from some dangerous html
341
					$ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
342
				} else {
343
					$ret .= dol_escape_htmltag($value);
344
				}
345
346
				if ($formatfunc && method_exists($object, $formatfunc)) {
347
					$ret = $object->$formatfunc($ret);
348
				}
349
			}
350
		}
351
		return $ret;
352
	}
353
354
	/**
355
	 * Output edit in place form
356
	 *
357
	 * @param   string	$fieldname		Name of the field
358
	 * @param	object	$object			Object
359
	 * @param	boolean	$perm			Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
360
	 * @param	string	$typeofdata		Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
361
	 * @param	string	$check			Same coe than $check parameter of GETPOST()
362
	 * @param	string	$morecss		More CSS
363
	 * @return	string   		      	HTML code for the edit of alternative language
364
	 */
365
	public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
366
	{
367
		global $conf, $langs, $extralanguages;
368
369
		$result = '';
370
371
		// List of extra languages
372
		$arrayoflangcode = array();
373
		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
374
			$arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
375
		}
376
377
		if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
378
			if (!is_object($extralanguages)) {
379
				include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
380
				$extralanguages = new ExtraLanguages($this->db);
381
			}
382
			$extralanguages->fetch_name_extralanguages('societe');
383
384
			if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
385
				return ''; // No extralang field to show
386
			}
387
388
			$result .= '<!-- Widget for translation -->'."\n";
389
			$result .= '<div class="inline-block paddingleft image-'.$object->element.'-'.$fieldname.'">';
390
			$s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
391
			$result .= $s;
392
			$result .= '</div>';
393
394
			$result .= '<div class="inline-block hidden field-'.$object->element.'-'.$fieldname.'">';
395
396
			$resultforextrlang = '';
397
			foreach ($arrayoflangcode as $langcode) {
398
				$valuetoshow = GETPOSTISSET('field-'.$object->element."-".$fieldname."-".$langcode) ? GETPOST('field-'.$object->element.'-'.$fieldname."-".$langcode, $check) : '';
399
				if (empty($valuetoshow)) {
400
					$object->fetchValuesForExtraLanguages();
401
					//var_dump($object->array_languages);
402
					$valuetoshow = $object->array_languages[$fieldname][$langcode];
403
				}
404
405
				$s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
406
				$resultforextrlang .= $s;
407
408
				// TODO Use the showInputField() method of ExtraLanguages object
409
				if ($typeofdata == 'textarea') {
410
					$resultforextrlang .= '<textarea name="field-'.$object->element."-".$fieldname."-".$langcode.'" id="'.$fieldname."-".$langcode.'" class="'.$morecss.'" rows="'.ROWS_2.'" wrap="soft">';
411
					$resultforextrlang .= $valuetoshow;
412
					$resultforextrlang .= '</textarea>';
413
				} else {
414
					$resultforextrlang .= '<input type="text" class="inputfieldforlang '.($morecss ? ' '.$morecss : '').'" name="field-'.$object->element.'-'.$fieldname.'-'.$langcode.'" value="'.$valuetoshow.'">';
415
				}
416
			}
417
			$result .= $resultforextrlang;
418
419
			$result .= '</div>';
420
			$result .= '<script>$(".image-'.$object->element.'-'.$fieldname.'").click(function() { console.log("Toggle lang widget"); jQuery(".field-'.$object->element.'-'.$fieldname.'").toggle(); });</script>';
421
		}
422
423
		return $result;
424
	}
425
426
	/**
427
	 * Output edit in place form
428
	 *
429
	 * @param	object	$object			Object
430
	 * @param	string	$value			Value to show/edit
431
	 * @param	string	$htmlname		DIV ID (field name)
432
	 * @param	int		$condition		Condition to edit
433
	 * @param	string	$inputType		Type of input ('string', 'numeric', 'datepicker' ('day' do not work, don't know why), 'textarea:rows:cols', 'ckeditor:dolibarr_zzz:width:height:?:1:rows:cols', 'select:loadmethod:savemethod:buttononly')
434
	 * @param	string	$editvalue		When in edit mode, use this value as $value instead of value
435
	 * @param	object	$extObject		External object
436
	 * @param	mixed	$custommsg		String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
437
	 * @return	string   		      	HTML edit in place
438
	 */
439
	protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
440
	{
441
		global $conf;
442
443
		$out = '';
444
445
		// Check parameters
446
		if (preg_match('/^text/', $inputType)) {
447
			$value = dol_nl2br($value);
448
		} elseif (preg_match('/^numeric/', $inputType)) {
449
			$value = price($value);
450
		} elseif ($inputType == 'day' || $inputType == 'datepicker') {
451
			$value = dol_print_date($value, 'day');
452
		}
453
454
		if ($condition) {
455
			$element = false;
456
			$table_element = false;
457
			$fk_element		= false;
458
			$loadmethod		= false;
459
			$savemethod		= false;
460
			$ext_element	= false;
461
			$button_only	= false;
462
			$inputOption = '';
463
464
			if (is_object($object)) {
465
				$element = $object->element;
466
				$table_element = $object->table_element;
467
				$fk_element = $object->id;
468
			}
469
470
			if (is_object($extObject)) {
471
				$ext_element = $extObject->element;
472
			}
473
474
			if (preg_match('/^(string|email|numeric)/', $inputType)) {
475
				$tmp = explode(':', $inputType);
476
				$inputType = $tmp[0];
477
				if (!empty($tmp[1])) {
478
					$inputOption = $tmp[1];
479
				}
480
				if (!empty($tmp[2])) {
481
					$savemethod = $tmp[2];
482
				}
483
				$out .= '<input id="width_'.$htmlname.'" value="'.$inputOption.'" type="hidden"/>'."\n";
484
			} elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
485
				$tmp = explode(':', $inputType);
486
				$inputType = $tmp[0];
487
				if (!empty($tmp[1])) {
488
					$inputOption = $tmp[1];
489
				}
490
				if (!empty($tmp[2])) {
491
					$savemethod = $tmp[2];
492
				}
493
494
				$out .= '<input id="timestamp" type="hidden"/>'."\n"; // Use for timestamp format
495
			} elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
496
				$tmp = explode(':', $inputType);
497
				$inputType = $tmp[0];
498
				$loadmethod = $tmp[1];
499
				if (!empty($tmp[2])) {
500
					$savemethod = $tmp[2];
501
				}
502
				if (!empty($tmp[3])) {
503
					$button_only = true;
504
				}
505
			} elseif (preg_match('/^textarea/', $inputType)) {
506
				$tmp = explode(':', $inputType);
507
				$inputType = $tmp[0];
508
				$rows = (empty($tmp[1]) ? '8' : $tmp[1]);
509
				$cols = (empty($tmp[2]) ? '80' : $tmp[2]);
510
			} elseif (preg_match('/^ckeditor/', $inputType)) {
511
				$tmp = explode(':', $inputType);
512
				$inputType = $tmp[0];
513
				$toolbar = $tmp[1];
514
				if (!empty($tmp[2])) {
515
					$width = $tmp[2];
516
				}
517
				if (!empty($tmp[3])) {
518
					$heigth = $tmp[3];
519
				}
520
				if (!empty($tmp[4])) {
521
					$savemethod = $tmp[4];
522
				}
523
524
				if (!empty($conf->fckeditor->enabled)) {
525
					$out .= '<input id="ckeditor_toolbar" value="'.$toolbar.'" type="hidden"/>'."\n";
526
				} else {
527
					$inputType = 'textarea';
528
				}
529
			}
530
531
			$out .= '<input id="element_'.$htmlname.'" value="'.$element.'" type="hidden"/>'."\n";
532
			$out .= '<input id="table_element_'.$htmlname.'" value="'.$table_element.'" type="hidden"/>'."\n";
533
			$out .= '<input id="fk_element_'.$htmlname.'" value="'.$fk_element.'" type="hidden"/>'."\n";
534
			$out .= '<input id="loadmethod_'.$htmlname.'" value="'.$loadmethod.'" type="hidden"/>'."\n";
535
			if (!empty($savemethod)) {
536
				$out .= '<input id="savemethod_'.$htmlname.'" value="'.$savemethod.'" type="hidden"/>'."\n";
537
			}
538
			if (!empty($ext_element)) {
539
				$out .= '<input id="ext_element_'.$htmlname.'" value="'.$ext_element.'" type="hidden"/>'."\n";
540
			}
541
			if (!empty($custommsg)) {
542
				if (is_array($custommsg)) {
543
					if (!empty($custommsg['success'])) {
544
						$out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg['success'].'" type="hidden"/>'."\n";
545
					}
546
					if (!empty($custommsg['error'])) {
547
						$out .= '<input id="errormsg_'.$htmlname.'" value="'.$custommsg['error'].'" type="hidden"/>'."\n";
548
					}
549
				} else {
550
					$out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg.'" type="hidden"/>'."\n";
551
				}
552
			}
553
			if ($inputType == 'textarea') {
554
				$out .= '<input id="textarea_'.$htmlname.'_rows" value="'.$rows.'" type="hidden"/>'."\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $rows does not seem to be defined for all execution paths leading up to this point.
Loading history...
555
				$out .= '<input id="textarea_'.$htmlname.'_cols" value="'.$cols.'" type="hidden"/>'."\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cols does not seem to be defined for all execution paths leading up to this point.
Loading history...
556
			}
557
			$out .= '<span id="viewval_'.$htmlname.'" class="viewval_'.$inputType.($button_only ? ' inactive' : ' active').'">'.$value.'</span>'."\n";
558
			$out .= '<span id="editval_'.$htmlname.'" class="editval_'.$inputType.($button_only ? ' inactive' : ' active').' hideobject">'.(!empty($editvalue) ? $editvalue : $value).'</span>'."\n";
559
		} else {
560
			$out = $value;
561
		}
562
563
		return $out;
564
	}
565
566
	/**
567
	 *	Show a text and picto with tooltip on text or picto.
568
	 *  Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip
569
	 *
570
	 *	@param	string		$text				Text to show
571
	 *	@param	string		$htmltext			HTML content of tooltip. Must be HTML/UTF8 encoded.
572
	 *	@param	int			$tooltipon			1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2
573
	 *	@param	int			$direction			-1=image is before, 0=no image, 1=image is after
574
	 *	@param	string		$img				Html code for image (use img_xxx() function to get it)
575
	 *	@param	string		$extracss			Add a CSS style to td tags
576
	 *	@param	int			$notabs				0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
577
	 *	@param	string		$incbefore			Include code before the text
578
	 *	@param	int			$noencodehtmltext	Do not encode into html entity the htmltext
579
	 *  @param  string      $tooltiptrigger		''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key)
580
	 *  @param	int			$forcenowrap		Force no wrap between text and picto (works with notabs=2 only)
581
	 *	@return	string							Code html du tooltip (texte+picto)
582
	 *	@see	textwithpicto() Use thisfunction if you can.
583
	 */
584
	public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
585
	{
586
		if ($incbefore) {
587
			$text = $incbefore.$text;
588
		}
589
		if (!$htmltext) {
590
			return $text;
591
		}
592
		$direction = (int) $direction;	// For backward compatibility when $direction was set to '' instead of 0
593
594
		$tag = 'td';
595
		if ($notabs == 2) {
596
			$tag = 'div';
597
		}
598
		if ($notabs == 3) {
599
			$tag = 'span';
600
		}
601
		// Sanitize tooltip
602
		$htmltext = str_replace(array("\r", "\n"), '', $htmltext);
603
604
		$extrastyle = '';
605
		if ($direction < 0) {
606
			$extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
607
			$extrastyle = 'padding: 0px; padding-left: 3px !important;';
608
		}
609
		if ($direction > 0) {
610
			$extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
611
			$extrastyle = 'padding: 0px; padding-right: 3px !important;';
612
		}
613
614
		$classfortooltip = 'classfortooltip';
615
616
		$s = '';
617
		$textfordialog = '';
618
619
		if ($tooltiptrigger == '') {
620
			$htmltext = str_replace('"', '&quot;', $htmltext);
621
		} else {
622
			$classfortooltip = 'classfortooltiponclick';
623
			$textfordialog .= '<div style="display: none;" id="idfortooltiponclick_'.$tooltiptrigger.'" class="classfortooltiponclicktext">'.$htmltext.'</div>';
624
		}
625
		if ($tooltipon == 2 || $tooltipon == 3) {
626
			$paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"';
627
			if ($tooltiptrigger == '') {
628
				$paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip
629
			} else {
630
				$paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"';
631
			}
632
		} else {
633
			$paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
634
		}
635
		if ($tooltipon == 1 || $tooltipon == 3) {
636
			$paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" ';
637
			if ($tooltiptrigger == '') {
638
				$paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip
639
			} else {
640
				$paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"';
641
			}
642
		} else {
643
			$paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
644
		}
645
		if (empty($notabs)) {
646
			$s .= '<table class="nobordernopadding"><tr style="height: auto;">';
647
		} elseif ($notabs == 2) {
648
			$s .= '<div class="inline-block'.($forcenowrap ? ' nowrap' : '').'">';
649
		}
650
		// Define value if value is before
651
		if ($direction < 0) {
652
			$s .= '<'.$tag.$paramfortooltipimg;
653
			if ($tag == 'td') {
654
				$s .= ' class=valigntop" width="14"';
655
			}
656
			$s .= '>'.$textfordialog.$img.'</'.$tag.'>';
657
		}
658
		// Use another method to help avoid having a space in value in order to use this value with jquery
659
		// Define label
660
		if ((string) $text != '') {
661
			$s .= '<'.$tag.$paramfortooltiptd.'>'.$text.'</'.$tag.'>';
662
		}
663
		// Define value if value is after
664
		if ($direction > 0) {
665
			$s .= '<'.$tag.$paramfortooltipimg;
666
			if ($tag == 'td') {
667
				$s .= ' class="valignmiddle" width="14"';
668
			}
669
			$s .= '>'.$textfordialog.$img.'</'.$tag.'>';
670
		}
671
		if (empty($notabs)) {
672
			$s .= '</tr></table>';
673
		} elseif ($notabs == 2) {
674
			$s .= '</div>';
675
		}
676
677
		return $s;
678
	}
679
680
	/**
681
	 *	Show a text with a picto and a tooltip on picto
682
	 *
683
	 *	@param	string	$text				Text to show
684
	 *	@param  string	$htmltext	     	Content of tooltip
685
	 *	@param	int		$direction			1=Icon is after text, -1=Icon is before text, 0=no icon
686
	 * 	@param	string	$type				Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none'
687
	 *  @param  string	$extracss           Add a CSS style to td, div or span tag
688
	 *  @param  int		$noencodehtmltext   Do not encode into html entity the htmltext
689
	 *  @param	int		$notabs				0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
690
	 *  @param  string  $tooltiptrigger     ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key, clickable link is on image or on link if param $type='none' or on both if $type='xxxclickable')
691
	 *  @param	int		$forcenowrap		Force no wrap between text and picto (works with notabs=2 only)
692
	 * 	@return	string						HTML code of text, picto, tooltip
693
	 */
694
	public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
695
	{
696
		global $conf, $langs;
697
698
		$alt = '';
699
		if ($tooltiptrigger) {
700
			$alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
701
		}
702
703
		//For backwards compatibility
704
		if ($type == '0') {
705
			$type = 'info';
706
		} elseif ($type == '1') {
707
			$type = 'help';
708
		}
709
710
		// If info or help with no javascript, show only text
711
		if (empty($conf->use_javascript_ajax)) {
712
			if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
713
				return $text;
714
			} else {
715
				$alt = $htmltext;
716
				$htmltext = '';
717
			}
718
		}
719
720
		// If info or help with smartphone, show only text (tooltip hover can't works)
721
		if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
722
			if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
723
				return $text;
724
			}
725
		}
726
		// If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
727
		//if (! empty($conf->dol_no_mouse_hover) && ! empty($tooltiptrigger))
728
		//{
729
		//if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.''</a>';
730
		//}
731
732
		$img = '';
733
		if ($type == 'info') {
734
			$img = img_help(0, $alt);
735
		} elseif ($type == 'help') {
736
			$img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
737
		} elseif ($type == 'helpclickable') {
738
			$img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
739
		} elseif ($type == 'superadmin') {
740
			$img = img_picto($alt, 'redstar');
741
		} elseif ($type == 'admin') {
742
			$img = img_picto($alt, 'star');
743
		} elseif ($type == 'warning') {
744
			$img = img_warning($alt);
745
		} elseif ($type != 'none') {
746
			$img = img_picto($alt, $type); // $type can be an image path
747
		}
748
749
		return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
750
	}
751
752
	/**
753
	 * Generate select HTML to choose massaction
754
	 *
755
	 * @param	string	$selected		Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default.
756
	 * @param	array	$arrayofaction	array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action.
757
	 * @param   int     $alwaysvisible  1=select button always visible
758
	 * @param   string  $name     		Name for massaction
759
	 * @param   string  $cssclass 		CSS class used to check for select
760
	 * @return	string|void				Select list
761
	 */
762
	public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
763
	{
764
		global $conf, $langs, $hookmanager;
765
766
767
		$disabled = 0;
768
		$ret = '<div class="centpercent center">';
769
		$ret .= '<select class="flat'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'select valignmiddle alignstart" id="'.$name.'" name="'.$name.'"'.($disabled ? ' disabled="disabled"' : '').'>';
770
771
		// 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.
772
		$parameters = array();
773
		$reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
774
		// check if there is a mass action
775
		if (count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
776
			return;
777
		}
778
		if (empty($reshook)) {
779
			$ret .= '<option value="0"'.($disabled ? ' disabled="disabled"' : '').'>-- '.$langs->trans("SelectAction").' --</option>';
780
			foreach ($arrayofaction as $code => $label) {
781
				$ret .= '<option value="'.$code.'"'.($disabled ? ' disabled="disabled"' : '').' data-html="'.dol_escape_htmltag($label).'">'.$label.'</option>';
782
			}
783
		}
784
		$ret .= $hookmanager->resPrint;
785
786
		$ret .= '</select>';
787
788
		if (empty($conf->dol_optimize_smallscreen)) {
789
			$ret .= ajax_combobox('.'.$name.'select');
790
		}
791
792
		// 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
793
		$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.
794
		$ret .= '<input type="submit" disabled name="confirmmassaction"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display: none"').' class="button'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'confirmed" value="'.dol_escape_htmltag($langs->trans("Confirm")).'">';
795
		$ret .= '</div>';
796
797
		if (!empty($conf->use_javascript_ajax)) {
798
			$ret .= '<!-- JS CODE TO ENABLE mass action select -->
799
    		<script>
800
                        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 checkfor select boxes */
801
        		{
802
        			atleastoneselected=0;
803
                                jQuery("."+cssclass).each(function( index ) {
804
    	  				/* console.log( index + ": " + $( this ).text() ); */
805
    	  				if ($(this).is(\':checked\')) atleastoneselected++;
806
    	  			});
807
808
					console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
809
810
    	  			if (atleastoneselected || '.$alwaysvisible.')
811
    	  			{
812
                                    jQuery("."+name).show();
813
        			    '.($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("'.$selected.'").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '').'
814
        			    '.($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '').'
815
    	  			}
816
    	  			else
817
    	  			{
818
                                    jQuery("."+name).hide();
819
                                    jQuery("."+name+"other").hide();
820
    	            }
821
        		}
822
823
        	jQuery(document).ready(function () {
824
                    initCheckForSelect(0, "' . $name.'", "'.$cssclass.'");
825
                    jQuery(".' . $cssclass.'").click(function() {
826
                        initCheckForSelect(1, "'.$name.'", "'.$cssclass.'");
827
                    });
828
                        jQuery(".' . $name.'select").change(function() {
829
        			var massaction = $( this ).val();
830
        			var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
831
        			if (massaction == "builddoc")
832
                    {
833
                        urlform = urlform + "#show_files";
834
    	            }
835
        			$( this ).closest("form").attr("action", urlform);
836
                    console.log("we select a mass action name='.$name.' massaction="+massaction+" - "+urlform);
837
        	        /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
838
        			if ($(this).val() != \'0\')
839
    	  			{
840
                                        jQuery(".' . $name.'confirmed").prop(\'disabled\', false);
841
										jQuery(".' . $name.'other").hide();	/* To disable if another div was open */
842
                                        jQuery(".' . $name.'"+massaction).show();
843
    	  			}
844
    	  			else
845
    	  			{
846
                                        jQuery(".' . $name.'confirmed").prop(\'disabled\', true);
847
										jQuery(".' . $name.'other").hide();	/* To disable any div open */
848
    	  			}
849
    	        });
850
        	});
851
    		</script>
852
        	';
853
		}
854
855
		return $ret;
856
	}
857
858
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
859
	/**
860
	 *  Return combo list of activated countries, into language of user
861
	 *
862
	 *  @param	string	$selected       		Id or Code or Label of preselected country
863
	 *  @param  string	$htmlname       		Name of html select object
864
	 *  @param  string	$htmloption     		More html options on select object
865
	 *  @param	integer	$maxlength				Max length for labels (0=no limit)
866
	 *  @param	string	$morecss				More css class
867
	 *  @param	string	$usecodeaskey			''=Use id as key (default), 'code3'=Use code on 3 alpha as key, 'code2"=Use code on 2 alpha as key
868
	 *  @param	int		$showempty				Show empty choice
869
	 *  @param	int		$disablefavorites		1=Disable favorites,
870
	 *  @param	int		$addspecialentries		1=Add dedicated entries for group of countries (like 'European Economic Community', ...)
871
	 *  @param	array	$exclude_country_code	Array of country code (iso2) to exclude
872
	 *  @param	int		$hideflags				Hide flags
873
	 *  @return string           				HTML string with select
874
	 */
875
	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)
876
	{
877
		// phpcs:enable
878
		global $conf, $langs, $mysoc;
879
880
		$langs->load("dict");
881
882
		$out = '';
883
		$countryArray = array();
884
		$favorite = array();
885
		$label = array();
886
		$atleastonefavorite = 0;
887
888
		$sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
889
		$sql .= " FROM ".MAIN_DB_PREFIX."c_country";
890
		$sql .= " WHERE active > 0";
891
		//$sql.= " ORDER BY code ASC";
892
893
		dol_syslog(get_class($this)."::select_country", LOG_DEBUG);
894
		$resql = $this->db->query($sql);
895
		if ($resql) {
896
			$out .= '<select id="select'.$htmlname.'" class="flat maxwidth200onsmartphone selectcountry'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" '.$htmloption.'>';
897
			$num = $this->db->num_rows($resql);
898
			$i = 0;
899
			if ($num) {
900
				while ($i < $num) {
901
					$obj = $this->db->fetch_object($resql);
902
903
					$countryArray[$i]['rowid'] = $obj->rowid;
904
					$countryArray[$i]['code_iso'] = $obj->code_iso;
905
					$countryArray[$i]['code_iso3'] 	= $obj->code_iso3;
906
					$countryArray[$i]['label'] = ($obj->code_iso && $langs->transnoentitiesnoconv("Country".$obj->code_iso) != "Country".$obj->code_iso ? $langs->transnoentitiesnoconv("Country".$obj->code_iso) : ($obj->label != '-' ? $obj->label : ''));
907
					$countryArray[$i]['favorite'] = $obj->favorite;
908
					$countryArray[$i]['eec'] = $obj->eec;
909
					$favorite[$i] = $obj->favorite;
910
					$label[$i] = dol_string_unaccent($countryArray[$i]['label']);
911
					$i++;
912
				}
913
914
				if (empty($disablefavorites)) {
915
					array_multisort($favorite, SORT_DESC, $label, SORT_ASC, $countryArray);
0 ignored issues
show
Bug introduced by
SORT_DESC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

915
					array_multisort($favorite, /** @scrutinizer ignore-type */ SORT_DESC, $label, SORT_ASC, $countryArray);
Loading history...
Bug introduced by
SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

915
					array_multisort($favorite, SORT_DESC, $label, /** @scrutinizer ignore-type */ SORT_ASC, $countryArray);
Loading history...
916
				} else {
917
					$countryArray = dol_sort_array($countryArray, 'label');
918
				}
919
920
				if ($showempty) {
921
					$out .= '<option value="">&nbsp;</option>'."\n";
922
				}
923
924
				if ($addspecialentries) {	// Add dedicated entries for groups of countries
925
					//if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
926
					$out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
927
					$out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
928
					if ($mysoc->isInEEC()) {
929
						$out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
930
					}
931
					$out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
932
					$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
933
				}
934
935
				foreach ($countryArray as $row) {
936
					//if (empty($showempty) && empty($row['rowid'])) continue;
937
					if (empty($row['rowid'])) {
938
						continue;
939
					}
940
					if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
941
						continue; // exclude some countries
942
					}
943
944
					if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
945
						$atleastonefavorite++;
946
					}
947
					if (empty($row['favorite']) && $atleastonefavorite) {
948
						$atleastonefavorite = 0;
949
						$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
950
					}
951
952
					$labeltoshow = '';
953
					if ($row['label']) {
954
						$labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
955
					} else {
956
						$labeltoshow .= '&nbsp;';
957
					}
958
					if ($row['code_iso']) {
959
						$labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
960
						if (empty($hideflags)) {
961
							$tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
962
							$labeltoshow = $tmpflag.' '.$labeltoshow;
963
						}
964
					}
965
966
					if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
967
						$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']).'">';
968
					} else {
969
						$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']).'">';
970
					}
971
					$out .= $labeltoshow;
972
					$out .= '</option>'."\n";
973
				}
974
			}
975
			$out .= '</select>';
976
		} else {
977
			dol_print_error($this->db);
978
		}
979
980
		// Make select dynamic
981
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
982
		$out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
983
984
		return $out;
985
	}
986
987
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
988
	/**
989
	 *  Return select list of incoterms
990
	 *
991
	 *  @param	string	$selected       		Id or Code of preselected incoterm
992
	 *  @param	string	$location_incoterms     Value of input location
993
	 *  @param	string	$page       			Defined the form action
994
	 *  @param  string	$htmlname       		Name of html select object
995
	 *  @param  string	$htmloption     		Options html on select object
996
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
997
	 *  @param	array	$events					Event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
998
	 *  @return string           				HTML string with select and input
999
	 */
1000
	public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array())
1001
	{
1002
		// phpcs:enable
1003
		global $conf, $langs;
1004
1005
		$langs->load("dict");
1006
1007
		$out = '';
1008
		$incotermArray = array();
1009
1010
		$sql = "SELECT rowid, code";
1011
		$sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms";
1012
		$sql .= " WHERE active > 0";
1013
		$sql .= " ORDER BY code ASC";
1014
1015
		dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
1016
		$resql = $this->db->query($sql);
1017
		if ($resql) {
1018
			if ($conf->use_javascript_ajax && !$forcecombo) {
1019
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1020
				$out .= ajax_combobox($htmlname, $events);
1021
			}
1022
1023
			if (!empty($page)) {
1024
				$out .= '<form method="post" action="'.$page.'">';
1025
				$out .= '<input type="hidden" name="action" value="set_incoterms">';
1026
				$out .= '<input type="hidden" name="token" value="'.newToken().'">';
1027
			}
1028
1029
			$out .= '<select id="'.$htmlname.'" class="flat selectincoterm width75" name="'.$htmlname.'" '.$htmloption.'>';
1030
			$out .= '<option value="0">&nbsp;</option>';
1031
			$num = $this->db->num_rows($resql);
1032
			$i = 0;
1033
			if ($num) {
1034
				$foundselected = false;
1035
1036
				while ($i < $num) {
1037
					$obj = $this->db->fetch_object($resql);
1038
					$incotermArray[$i]['rowid'] = $obj->rowid;
1039
					$incotermArray[$i]['code'] = $obj->code;
1040
					$i++;
1041
				}
1042
1043
				foreach ($incotermArray as $row) {
1044
					if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1045
						$out .= '<option value="'.$row['rowid'].'" selected>';
1046
					} else {
1047
						$out .= '<option value="'.$row['rowid'].'">';
1048
					}
1049
1050
					if ($row['code']) {
1051
						$out .= $row['code'];
1052
					}
1053
1054
					$out .= '</option>';
1055
				}
1056
			}
1057
			$out .= '</select>';
1058
1059
			if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $disableautocomplete seems to never exist and therefore empty should always be true.
Loading history...
1060
				$out .= ajax_multiautocompleter('location_incoterms', '', DOL_URL_ROOT.'/core/ajax/locationincoterms.php')."\n";
1061
				$moreattrib .= ' autocomplete="off"';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $moreattrib seems to be never defined.
Loading history...
1062
			}
1063
			$out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="'.$location_incoterms.'">'."\n";
1064
1065
			if (!empty($page)) {
1066
				$out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1067
			}
1068
		} else {
1069
			dol_print_error($this->db);
1070
		}
1071
1072
		return $out;
1073
	}
1074
1075
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1076
	/**
1077
	 *	Return list of types of lines (product or service)
1078
	 * 	Example: 0=product, 1=service, 9=other (for external module)
1079
	 *
1080
	 *	@param  string	$selected       Preselected type
1081
	 *	@param  string	$htmlname       Name of field in html form
1082
	 * 	@param	int		$showempty		Add an empty field
1083
	 * 	@param	int		$hidetext		Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
1084
	 * 	@param	integer	$forceall		1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service')
1085
	 *  @return	void
1086
	 */
1087
	public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1088
	{
1089
		// phpcs:enable
1090
		global $db, $langs, $user, $conf;
1091
1092
		// If product & services are enabled or both disabled.
1093
		if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled))
1094
			|| (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) {
1095
			if (empty($hidetext)) {
1096
				print $langs->trans("Type").': ';
1097
			}
1098
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1099
			if ($showempty) {
1100
				print '<option value="-1"';
1101
				if ($selected == -1) {
1102
					print ' selected';
1103
				}
1104
				print '>&nbsp;</option>';
1105
			}
1106
1107
			print '<option value="0"';
1108
			if (0 == $selected) {
1109
				print ' selected';
1110
			}
1111
			print '>'.$langs->trans("Product");
1112
1113
			print '<option value="1"';
1114
			if (1 == $selected) {
1115
				print ' selected';
1116
			}
1117
			print '>'.$langs->trans("Service");
1118
1119
			print '</select>';
1120
			print ajax_combobox('select_'.$htmlname);
1121
			//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1122
		}
1123
		if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) {
1124
			print $langs->trans("Service");
1125
			print '<input type="hidden" name="'.$htmlname.'" value="1">';
1126
		}
1127
		if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) {
1128
			print $langs->trans("Product");
1129
			print '<input type="hidden" name="'.$htmlname.'" value="0">';
1130
		}
1131
		if ($forceall < 0) {	// This should happened only for contracts when both predefined product and service are disabled.
1132
			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
1133
		}
1134
	}
1135
1136
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1137
	/**
1138
	 *	Load into cache cache_types_fees, array of types of fees
1139
	 *
1140
	 *	@return     int             Nb of lines loaded, <0 if KO
1141
	 */
1142
	public function load_cache_types_fees()
1143
	{
1144
		// phpcs:enable
1145
		global $langs;
1146
1147
		$num = count($this->cache_types_fees);
1148
		if ($num > 0) {
1149
			return 0; // Cache already loaded
1150
		}
1151
1152
		dol_syslog(__METHOD__, LOG_DEBUG);
1153
1154
		$langs->load("trips");
1155
1156
		$sql = "SELECT c.code, c.label";
1157
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
1158
		$sql .= " WHERE active > 0";
1159
1160
		$resql = $this->db->query($sql);
1161
		if ($resql) {
1162
			$num = $this->db->num_rows($resql);
1163
			$i = 0;
1164
1165
			while ($i < $num) {
1166
				$obj = $this->db->fetch_object($resql);
1167
1168
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1169
				$label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1170
				$this->cache_types_fees[$obj->code] = $label;
1171
				$i++;
1172
			}
1173
1174
			asort($this->cache_types_fees);
1175
1176
			return $num;
1177
		} else {
1178
			dol_print_error($this->db);
1179
			return -1;
1180
		}
1181
	}
1182
1183
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1184
	/**
1185
	 *	Return list of types of notes
1186
	 *
1187
	 *	@param	string		$selected		Preselected type
1188
	 *	@param  string		$htmlname		Name of field in form
1189
	 * 	@param	int			$showempty		Add an empty field
1190
	 * 	@return	void
1191
	 */
1192
	public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1193
	{
1194
		// phpcs:enable
1195
		global $user, $langs;
1196
1197
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1198
1199
		$this->load_cache_types_fees();
1200
1201
		print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1202
		if ($showempty) {
1203
			print '<option value="-1"';
1204
			if ($selected == -1) {
1205
				print ' selected';
1206
			}
1207
			print '>&nbsp;</option>';
1208
		}
1209
1210
		foreach ($this->cache_types_fees as $key => $value) {
1211
			print '<option value="'.$key.'"';
1212
			if ($key == $selected) {
1213
				print ' selected';
1214
			}
1215
			print '>';
1216
			print $value;
1217
			print '</option>';
1218
		}
1219
1220
		print '</select>';
1221
		if ($user->admin) {
1222
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1223
		}
1224
	}
1225
1226
1227
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1228
	/**
1229
	 *  Output html form to select a third party
1230
	 *
1231
	 *	@param	string	$selected       		Preselected type
1232
	 *	@param  string	$htmlname       		Name of field in form
1233
	 *  @param  string	$filter         		Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here (example: 's.rowid <> x', 's.client IN (1,3)')
1234
	 *	@param	string	$showempty				Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
1235
	 * 	@param	int		$showtype				Show third party type in combolist (customer, prospect or supplier)
1236
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
1237
	 *  @param	array	$events					Ajax event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1238
	 *	@param	int		$limit					Maximum number of elements
1239
	 *  @param	string	$morecss				Add more css styles to the SELECT component
1240
	 *	@param  string	$moreparam      		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1241
	 *	@param	string	$selected_input_value	Value of preselected input text (for use with ajax)
1242
	 *  @param	int		$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1243
	 *  @param	array	$ajaxoptions			Options for ajax_autocompleter
1244
	 * 	@param  bool	$multiple				add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
1245
	 *  @param	array	$excludeids				Exclude IDs from the select combo
1246
	 * 	@return	string							HTML string with select box for thirdparty.
1247
	 */
1248
	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())
1249
	{
1250
		// phpcs:enable
1251
		global $conf, $user, $langs;
1252
1253
		$out = '';
1254
1255
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1256
			if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
1257
				$ajaxoptions = array();
1258
			}
1259
1260
			require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1261
1262
			// No immediate load of all database
1263
			$placeholder = '';
1264
			if ($selected && empty($selected_input_value)) {
1265
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1266
				$societetmp = new Societe($this->db);
1267
				$societetmp->fetch($selected);
1268
				$selected_input_value = $societetmp->name;
1269
				unset($societetmp);
1270
			}
1271
			// mode 1
1272
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '');
1273
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1274
1275
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1276
			if (empty($hidelabel)) {
1277
				print $langs->trans("RefOrLabel").' : ';
1278
			} elseif ($hidelabel > 1) {
1279
				$placeholder = $langs->trans("RefOrLabel");
1280
				if ($hidelabel == 2) {
1281
					$out .= img_picto($langs->trans("Search"), 'search');
1282
				}
1283
			}
1284
			$out .= '<input type="text" class="'.$morecss.'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '').' '.(!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
1285
			if ($hidelabel == 3) {
1286
				$out .= img_picto($langs->trans("Search"), 'search');
1287
			}
1288
		} else {
1289
			// Immediate load of all database
1290
			$out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids);
1291
		}
1292
1293
		return $out;
1294
	}
1295
1296
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1297
	/**
1298
	 *  Output html form to select a third party.
1299
	 *  Note, you must use the select_company to get the component to select a third party. This function must only be called by select_company.
1300
	 *
1301
	 *	@param	string	$selected       Preselected type
1302
	 *	@param  string	$htmlname       Name of field in form
1303
	 *  @param  string	$filter         Optional filters criteras (example: 's.rowid NOT IN (x)', 's.client IN (1,3)'). Do not use a filter coming from input of users.
1304
	 *	@param	string	$showempty		Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
1305
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
1306
	 * 	@param	int		$forcecombo		Force to use standard HTML select component without beautification
1307
	 *  @param	array	$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1308
	 *  @param	string	$filterkey		Filter on key value
1309
	 *  @param	int		$outputmode		0=HTML select string, 1=Array
1310
	 *  @param	int		$limit			Limit number of answers
1311
	 *  @param	string	$morecss		Add more css styles to the SELECT component
1312
	 *	@param  string	$moreparam      Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1313
	 *	@param  bool	$multiple       add [] in the name of element and add 'multiple' attribut
1314
	 *  @param	array	$excludeids		Exclude IDs from the select combo
1315
	 * 	@return	string					HTML string with
1316
	 */
1317
	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())
1318
	{
1319
		// phpcs:enable
1320
		global $conf, $user, $langs;
1321
1322
		$out = '';
1323
		$num = 0;
1324
		$outarray = array();
1325
1326
		if ($selected === '') {
1327
			$selected = array();
1328
		} elseif (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
1329
			$selected = array($selected);
1330
		}
1331
1332
		// Clean $filter that may contains sql conditions so sql code
1333
		if (function_exists('testSqlAndScriptInject')) {
1334
			if (testSqlAndScriptInject($filter, 3) > 0) {
1335
				$filter = '';
1336
			}
1337
		}
1338
1339
		// We search companies
1340
		$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1341
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1342
			$sql .= ", s.address, s.zip, s.town";
1343
			$sql .= ", dictp.code as country_code";
1344
		}
1345
		$sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
1346
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1347
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid = s.fk_pays";
1348
		}
1349
		if (!$user->rights->societe->client->voir && !$user->socid) {
1350
			$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1351
		}
1352
		$sql .= " WHERE s.entity IN (".getEntity('societe').")";
1353
		if (!empty($user->socid)) {
1354
			$sql .= " AND s.rowid = ".((int) $user->socid);
1355
		}
1356
		if ($filter) {
1357
			$sql .= " AND (".$filter.")";
1358
		}
1359
		if (!$user->rights->societe->client->voir && !$user->socid) {
1360
			$sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1361
		}
1362
		if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1363
			$sql .= " AND s.status <> 0";
1364
		}
1365
		if (!empty($excludeids)) {
1366
			$sql .= " AND s.rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1367
		}
1368
		// Add criteria
1369
		if ($filterkey && $filterkey != '') {
1370
			$sql .= " AND (";
1371
			$prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1372
			// For natural search
1373
			$scrit = explode(' ', $filterkey);
1374
			$i = 0;
1375
			if (count($scrit) > 1) {
1376
				$sql .= "(";
1377
			}
1378
			foreach ($scrit as $crit) {
1379
				if ($i > 0) {
1380
					$sql .= " AND ";
1381
				}
1382
				$sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1383
				$i++;
1384
			}
1385
			if (count($scrit) > 1) {
1386
				$sql .= ")";
1387
			}
1388
			if (!empty($conf->barcode->enabled)) {
1389
				$sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1390
			}
1391
			$sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1392
			$sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1393
			$sql .= ")";
1394
		}
1395
		$sql .= $this->db->order("nom", "ASC");
1396
		$sql .= $this->db->plimit($limit, 0);
1397
1398
		// Build output string
1399
		dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1400
		$resql = $this->db->query($sql);
1401
		if ($resql) {
1402
			if (!$forcecombo) {
1403
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1404
				$out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1405
			}
1406
1407
			// Construct $out and $outarray
1408
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1409
1410
			$textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1411
			if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1412
				// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1413
				//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1414
				if ($showempty && !is_numeric($showempty)) {
1415
					$textifempty = $langs->trans($showempty);
1416
				} else {
1417
					$textifempty .= $langs->trans("All");
1418
				}
1419
			}
1420
			if ($showempty) {
1421
				$out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.($textifempty ? $textifempty : '&nbsp;').'</span>').'">'.$textifempty.'</option>'."\n";
1422
			}
1423
1424
			$num = $this->db->num_rows($resql);
1425
			$i = 0;
1426
			if ($num) {
1427
				while ($i < $num) {
1428
					$obj = $this->db->fetch_object($resql);
1429
					$label = '';
1430
					if ($conf->global->SOCIETE_ADD_REF_IN_LIST) {
1431
						if (($obj->client) && (!empty($obj->code_client))) {
1432
							$label = $obj->code_client.' - ';
1433
						}
1434
						if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1435
							$label .= $obj->code_fournisseur.' - ';
1436
						}
1437
						$label .= ' '.$obj->name;
1438
					} else {
1439
						$label = $obj->name;
1440
					}
1441
1442
					if (!empty($obj->name_alias)) {
1443
						$label .= ' ('.$obj->name_alias.')';
1444
					}
1445
1446
					if ($showtype) {
1447
						if ($obj->client || $obj->fournisseur) {
1448
							$label .= ' (';
1449
						}
1450
						if ($obj->client == 1 || $obj->client == 3) {
1451
							$label .= $langs->trans("Customer");
1452
						}
1453
						if ($obj->client == 2 || $obj->client == 3) {
1454
							$label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1455
						}
1456
						if ($obj->fournisseur) {
1457
							$label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1458
						}
1459
						if ($obj->client || $obj->fournisseur) {
1460
							$label .= ')';
1461
						}
1462
					}
1463
1464
					if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1465
						$label .= ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1466
						if (!empty($obj->country_code)) {
1467
							$label .= ', '.$langs->trans('Country'.$obj->country_code);
1468
						}
1469
					}
1470
1471
					if (empty($outputmode)) {
1472
						if (in_array($obj->rowid, $selected)) {
1473
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
1474
						} else {
1475
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
1476
						}
1477
					} else {
1478
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
1479
					}
1480
1481
					$i++;
1482
					if (($i % 10) == 0) {
1483
						$out .= "\n";
1484
					}
1485
				}
1486
			}
1487
			$out .= '</select>'."\n";
1488
		} else {
1489
			dol_print_error($this->db);
1490
		}
1491
1492
		$this->result = array('nbofthirdparties'=>$num);
1493
1494
		if ($outputmode) {
1495
			return $outarray;
1496
		}
1497
		return $out;
1498
	}
1499
1500
1501
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1502
	/**
1503
	 *  Return HTML combo list of absolute discounts
1504
	 *
1505
	 *  @param	string	$selected       Id remise fixe pre-selectionnee
1506
	 *  @param  string	$htmlname       Nom champ formulaire
1507
	 *  @param  string	$filter         Criteres optionnels de filtre
1508
	 *  @param	int		$socid			Id of thirdparty
1509
	 *  @param	int		$maxvalue		Max value for lines that can be selected
1510
	 *  @return	int						Return number of qualifed lines in list
1511
	 */
1512
	public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1513
	{
1514
		// phpcs:enable
1515
		global $langs, $conf;
1516
1517
		// On recherche les remises
1518
		$sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1519
		$sql .= " re.description, re.fk_facture_source";
1520
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1521
		$sql .= " WHERE re.fk_soc = ".(int) $socid;
1522
		$sql .= " AND re.entity = ".$conf->entity;
1523
		if ($filter) {
1524
			$sql .= " AND ".$filter;
1525
		}
1526
		$sql .= " ORDER BY re.description ASC";
1527
1528
		dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1529
		$resql = $this->db->query($sql);
1530
		if ($resql) {
1531
			print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1532
			$num = $this->db->num_rows($resql);
1533
1534
			$qualifiedlines = $num;
1535
1536
			$i = 0;
1537
			if ($num) {
1538
				print '<option value="0">&nbsp;</option>';
1539
				while ($i < $num) {
1540
					$obj = $this->db->fetch_object($resql);
1541
					$desc = dol_trunc($obj->description, 40);
1542
					if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
1543
						$desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1544
					}
1545
					if (preg_match('/\(DEPOSIT\)/', $desc)) {
1546
						$desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1547
					}
1548
					if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
1549
						$desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1550
					}
1551
					if (preg_match('/\(EXCESS PAID\)/', $desc)) {
1552
						$desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1553
					}
1554
1555
					$selectstring = '';
1556
					if ($selected > 0 && $selected == $obj->rowid) {
1557
						$selectstring = ' selected';
1558
					}
1559
1560
					$disabled = '';
1561
					if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1562
						$qualifiedlines--;
1563
						$disabled = ' disabled';
1564
					}
1565
1566
					if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1567
						$tmpfac = new Facture($this->db);
1568
						if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1569
							$desc = $desc.' - '.$tmpfac->ref;
1570
						}
1571
					}
1572
1573
					print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1574
					$i++;
1575
				}
1576
			}
1577
			print '</select>';
1578
			return $qualifiedlines;
1579
		} else {
1580
			dol_print_error($this->db);
1581
			return -1;
1582
		}
1583
	}
1584
1585
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1586
	/**
1587
	 *  Return list of all contacts (for a third party or all)
1588
	 *
1589
	 *  @param	int		$socid      	Id ot third party or 0 for all
1590
	 *  @param  string	$selected   	Id contact pre-selectionne
1591
	 *  @param  string	$htmlname  	    Name of HTML field ('none' for a not editable field)
1592
	 *  @param  int		$showempty      0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list
1593
	 *  @param  string	$exclude        List of contacts id to exclude
1594
	 *  @param	string	$limitto		Disable answers that are not id in this array list
1595
	 *  @param	integer	$showfunction   Add function into label
1596
	 *  @param	string	$moreclass		Add more class to class style
1597
	 *  @param	integer	$showsoc	    Add company into label
1598
	 *  @param	int		$forcecombo		Force to use combo box
1599
	 *  @param	array	$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1600
	 *  @param	bool	$options_only	Return options only (for ajax treatment)
1601
	 *  @param	string	$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1602
	 *  @param	string	$htmlid			Html id to use instead of htmlname
1603
	 *  @return	int						<0 if KO, Nb of contact in list if OK
1604
	 *  @deprecated						You can use selectcontacts directly (warning order of param was changed)
1605
	 */
1606
	public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '')
1607
	{
1608
		// phpcs:enable
1609
		print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1610
		return $this->num;
1611
	}
1612
1613
	/**
1614
	 *	Return HTML code of the SELECT of list of all contacts (for a third party or all).
1615
	 *  This also set the number of contacts found into $this->num
1616
	 *
1617
	 * @since 9.0 Add afterSelectContactOptions hook
1618
	 *
1619
	 *	@param	int			$socid      	Id ot third party or 0 for all or -1 for empty list
1620
	 *	@param  array|int	$selected   	Array of ID of pre-selected contact id
1621
	 *	@param  string		$htmlname  	    Name of HTML field ('none' for a not editable field)
1622
	 *	@param  int			$showempty     	0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list
1623
	 *	@param  string		$exclude        List of contacts id to exclude
1624
	 *	@param	string		$limitto		Disable answers that are not id in this array list
1625
	 *	@param	integer		$showfunction   Add function into label
1626
	 *	@param	string		$moreclass		Add more class to class style
1627
	 *	@param	bool		$options_only	Return options only (for ajax treatment)
1628
	 *	@param	integer		$showsoc	    Add company into label
1629
	 * 	@param	int			$forcecombo		Force to use combo box (so no ajax beautify effect)
1630
	 *  @param	array		$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1631
	 *  @param	string		$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1632
	 *  @param	string		$htmlid			Html id to use instead of htmlname
1633
	 *  @param	bool		$multiple		add [] in the name of element and add 'multiple' attribut
1634
	 *  @param	integer		$disableifempty Set tag 'disabled' on select if there is no choice
1635
	 *	@return	 int|string					<0 if KO, HTML with select string if OK.
1636
	 */
1637
	public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0)
1638
	{
1639
		global $conf, $langs, $hookmanager, $action;
1640
1641
		$langs->load('companies');
1642
1643
		if (empty($htmlid)) {
1644
			$htmlid = $htmlname;
1645
		}
1646
		$num = 0;
1647
1648
		if ($selected === '') {
1649
			$selected = array();
1650
		} elseif (!is_array($selected)) {
1651
			$selected = array($selected);
1652
		}
1653
		$out = '';
1654
1655
		if (!is_object($hookmanager)) {
1656
			include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1657
			$hookmanager = new HookManager($this->db);
1658
		}
1659
1660
		// We search third parties
1661
		$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";
1662
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1663
			$sql .= ", s.nom as company, s.town AS company_town";
1664
		}
1665
		$sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp";
1666
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1667
			$sql .= " LEFT OUTER JOIN  ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc";
1668
		}
1669
		$sql .= " WHERE sp.entity IN (".getEntity('socpeople').")";
1670
		if ($socid > 0 || $socid == -1) {
1671
			$sql .= " AND sp.fk_soc = ".((int) $socid);
1672
		}
1673
		if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1674
			$sql .= " AND sp.statut <> 0";
1675
		}
1676
		$sql .= " ORDER BY sp.lastname ASC";
1677
1678
		dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1679
		$resql = $this->db->query($sql);
1680
		if ($resql) {
1681
			$num = $this->db->num_rows($resql);
1682
1683
			if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1684
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1685
				$out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1686
			}
1687
1688
			if ($htmlname != 'none' && !$options_only) {
1689
				$out .= '<select class="flat'.($moreclass ? ' '.$moreclass : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1690
			}
1691
1692
			if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
1693
				$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1694
			}
1695
			if ($showempty == 2) {
1696
				$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1697
			}
1698
1699
			$i = 0;
1700
			if ($num) {
1701
				include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1702
				$contactstatic = new Contact($this->db);
1703
1704
				while ($i < $num) {
1705
					$obj = $this->db->fetch_object($resql);
1706
1707
					// Set email (or phones) and town extended infos
1708
					$extendedInfos = '';
1709
					if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1710
						$extendedInfos = array();
1711
						$email = trim($obj->email);
1712
						if (!empty($email)) {
1713
							$extendedInfos[] = $email;
1714
						} else {
1715
							$phone = trim($obj->phone);
1716
							$phone_perso = trim($obj->phone_perso);
1717
							$phone_mobile = trim($obj->phone_mobile);
1718
							if (!empty($phone)) {
1719
								$extendedInfos[] = $phone;
1720
							}
1721
							if (!empty($phone_perso)) {
1722
								$extendedInfos[] = $phone_perso;
1723
							}
1724
							if (!empty($phone_mobile)) {
1725
								$extendedInfos[] = $phone_mobile;
1726
							}
1727
						}
1728
						$contact_town = trim($obj->contact_town);
1729
						$company_town = trim($obj->company_town);
1730
						if (!empty($contact_town)) {
1731
							$extendedInfos[] = $contact_town;
1732
						} elseif (!empty($company_town)) {
1733
							$extendedInfos[] = $company_town;
1734
						}
1735
						$extendedInfos = implode(' - ', $extendedInfos);
1736
						if (!empty($extendedInfos)) {
1737
							$extendedInfos = ' - '.$extendedInfos;
1738
						}
1739
					}
1740
1741
					$contactstatic->id = $obj->rowid;
1742
					$contactstatic->lastname = $obj->lastname;
1743
					$contactstatic->firstname = $obj->firstname;
1744
					if ($obj->statut == 1) {
1745
						if ($htmlname != 'none') {
1746
							$disabled = 0;
1747
							if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1748
								$disabled = 1;
1749
							}
1750
							if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1751
								$disabled = 1;
1752
							}
1753
							if (!empty($selected) && in_array($obj->rowid, $selected)) {
1754
								$out .= '<option value="'.$obj->rowid.'"';
1755
								if ($disabled) {
1756
									$out .= ' disabled';
1757
								}
1758
								$out .= ' selected>';
1759
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1760
								if ($showfunction && $obj->poste) {
1761
									$out .= ' ('.$obj->poste.')';
1762
								}
1763
								if (($showsoc > 0) && $obj->company) {
1764
									$out .= ' - ('.$obj->company.')';
1765
								}
1766
								$out .= '</option>';
1767
							} else {
1768
								$out .= '<option value="'.$obj->rowid.'"';
1769
								if ($disabled) {
1770
									$out .= ' disabled';
1771
								}
1772
								$out .= '>';
1773
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1774
								if ($showfunction && $obj->poste) {
1775
									$out .= ' ('.$obj->poste.')';
1776
								}
1777
								if (($showsoc > 0) && $obj->company) {
1778
									$out .= ' - ('.$obj->company.')';
1779
								}
1780
								$out .= '</option>';
1781
							}
1782
						} else {
1783
							if (in_array($obj->rowid, $selected)) {
1784
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1785
								if ($showfunction && $obj->poste) {
1786
									$out .= ' ('.$obj->poste.')';
1787
								}
1788
								if (($showsoc > 0) && $obj->company) {
1789
									$out .= ' - ('.$obj->company.')';
1790
								}
1791
							}
1792
						}
1793
					}
1794
					$i++;
1795
				}
1796
			} else {
1797
				$labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1798
				$out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1799
				$out .= $labeltoshow;
1800
				$out .= '</option>';
1801
			}
1802
1803
			$parameters = array(
1804
				'socid'=>$socid,
1805
				'htmlname'=>$htmlname,
1806
				'resql'=>$resql,
1807
				'out'=>&$out,
1808
				'showfunction'=>$showfunction,
1809
				'showsoc'=>$showsoc,
1810
			);
1811
1812
			$reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1813
1814
			if ($htmlname != 'none' && !$options_only) {
1815
				$out .= '</select>';
1816
			}
1817
1818
			$this->num = $num;
1819
			return $out;
1820
		} else {
1821
			dol_print_error($this->db);
1822
			return -1;
1823
		}
1824
	}
1825
1826
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1827
	/**
1828
	 *	Return the HTML select list of users
1829
	 *
1830
	 *  @param	string			$selected       Id user preselected
1831
	 *  @param  string			$htmlname       Field name in form
1832
	 *  @param  int				$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
1833
	 *  @param  array			$exclude        Array list of users id to exclude
1834
	 * 	@param	int				$disabled		If select list must be disabled
1835
	 *  @param  array|string	$include        Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me
1836
	 * 	@param	int				$enableonly		Array list of users id to be enabled. All other must be disabled
1837
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1838
	 * 	@return	void
1839
	 *  @deprecated		Use select_dolusers instead
1840
	 *  @see select_dolusers()
1841
	 */
1842
	public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1843
	{
1844
		// phpcs:enable
1845
		print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1846
	}
1847
1848
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1849
	/**
1850
	 *	Return select list of users
1851
	 *
1852
	 *  @param	string			$selected       User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed)
1853
	 *  @param  string			$htmlname       Field name in form
1854
	 *  @param  int|string		$show_empty     0=list with no empty value, 1=add also an empty value into list
1855
	 *  @param  array			$exclude        Array list of users id to exclude
1856
	 * 	@param	int				$disabled		If select list must be disabled
1857
	 *  @param  array|string	$include        Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me
1858
	 * 	@param	array			$enableonly		Array list of users id to be enabled. If defined, it means that others will be disabled
1859
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1860
	 *  @param	int				$maxlength		Maximum length of string into list (0=no limit)
1861
	 *  @param	int				$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
1862
	 *  @param	string			$morefilter		Add more filters into sql request (Example: 'employee = 1'). This value must not come from user input.
1863
	 *  @param	integer			$show_every		0=default list, 1=add also a value "Everybody" at beginning of list
1864
	 *  @param	string			$enableonlytext	If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty.
1865
	 *  @param	string			$morecss		More css
1866
	 *  @param  int     		$noactive       Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
1867
	 *  @param  int				$outputmode     0=HTML select string, 1=Array
1868
	 *  @param  bool			$multiple       add [] in the name of element and add 'multiple' attribut
1869
	 * 	@return	string							HTML select string
1870
	 *  @see select_dolgroups()
1871
	 */
1872
	public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $noactive = 0, $outputmode = 0, $multiple = false)
1873
	{
1874
		// phpcs:enable
1875
		global $conf, $user, $langs, $hookmanager;
1876
1877
		// If no preselected user defined, we take current user
1878
		if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1879
			$selected = $user->id;
1880
		}
1881
1882
		if ($selected === '') {
1883
			$selected = array();
1884
		} elseif (!is_array($selected)) {
1885
			$selected = array($selected);
1886
		}
1887
1888
		$excludeUsers = null;
1889
		$includeUsers = null;
1890
1891
		// Permettre l'exclusion d'utilisateurs
1892
		if (is_array($exclude)) {
1893
			$excludeUsers = implode(",", $exclude);
1894
		}
1895
		// Permettre l'inclusion d'utilisateurs
1896
		if (is_array($include)) {
1897
			$includeUsers = implode(",", $include);
1898
		} elseif ($include == 'hierarchy') {
1899
			// Build list includeUsers to have only hierarchy
1900
			$includeUsers = implode(",", $user->getAllChildIds(0));
1901
		} elseif ($include == 'hierarchyme') {
1902
			// Build list includeUsers to have only hierarchy and current user
1903
			$includeUsers = implode(",", $user->getAllChildIds(1));
1904
		}
1905
1906
		$out = '';
1907
		$outarray = array();
1908
1909
		// Forge request to select users
1910
		$sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1911
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1912
			$sql .= ", e.label";
1913
		}
1914
		$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
1915
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1916
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid = u.entity";
1917
			if ($force_entity) {
1918
				$sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
1919
			} else {
1920
				$sql .= " WHERE u.entity IS NOT NULL";
1921
			}
1922
		} else {
1923
			if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1924
				$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug";
1925
				$sql .= " ON ug.fk_user = u.rowid";
1926
				$sql .= " WHERE ug.entity = ".$conf->entity;
1927
			} else {
1928
				$sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
1929
			}
1930
		}
1931
		if (!empty($user->socid)) {
1932
			$sql .= " AND u.fk_soc = ".((int) $user->socid);
1933
		}
1934
		if (is_array($exclude) && $excludeUsers) {
1935
			$sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
1936
		}
1937
		if ($includeUsers) {
1938
			$sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
1939
		}
1940
		if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) {
1941
			$sql .= " AND u.statut <> 0";
1942
		}
1943
		if (!empty($morefilter)) {
1944
			$sql .= " ".$morefilter;
1945
		}
1946
1947
		//Add hook to filter on user (for exemple on usergroup define in custom modules)
1948
		$reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $action seems to be never defined.
Loading history...
1949
		if (!empty($reshook)) {
1950
			$sql .= $hookmanager->resPrint;
1951
		}
1952
1953
		if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {	// MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
1954
			$sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
1955
		} else {
1956
			$sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
1957
		}
1958
1959
		dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
1960
1961
		$resql = $this->db->query($sql);
1962
		if ($resql) {
1963
			$num = $this->db->num_rows($resql);
1964
			$i = 0;
1965
			if ($num) {
1966
				// do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
1967
				$out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
1968
				if ($show_empty && !$multiple) {
1969
					$textforempty = ' ';
1970
					if (!empty($conf->use_javascript_ajax)) {
1971
						$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
1972
					}
1973
					if (!is_numeric($show_empty)) {
1974
						$textforempty = $show_empty;
1975
					}
1976
					$out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
1977
				}
1978
				if ($show_every) {
1979
					$out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
1980
				}
1981
1982
				$userstatic = new User($this->db);
1983
1984
				while ($i < $num) {
1985
					$obj = $this->db->fetch_object($resql);
1986
1987
					$userstatic->id = $obj->rowid;
1988
					$userstatic->lastname = $obj->lastname;
1989
					$userstatic->firstname = $obj->firstname;
1990
					$userstatic->photo = $obj->photo;
1991
					$userstatic->statut = $obj->status;
1992
					$userstatic->entity = $obj->entity;
1993
					$userstatic->admin = $obj->admin;
1994
1995
					$disableline = '';
1996
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
1997
						$disableline = ($enableonlytext ? $enableonlytext : '1');
1998
					}
1999
2000
					$labeltoshow = '';
2001
2002
					// $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2003
					$fullNameMode = 0;
2004
					if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2005
						$fullNameMode = 1; //Firstname+lastname
2006
					}
2007
					$labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2008
					if (empty($obj->firstname) && empty($obj->lastname)) {
2009
						$labeltoshow .= $obj->login;
2010
					}
2011
2012
					// Complete name with more info
2013
					$moreinfo = '';
2014
					if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2015
						$moreinfo .= ($moreinfo ? ' - ' : ' (').$obj->login;
2016
					}
2017
					if ($showstatus >= 0) {
2018
						if ($obj->status == 1 && $showstatus == 1) {
2019
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2020
						}
2021
						if ($obj->status == 0 && $showstatus == 1) {
2022
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2023
						}
2024
					}
2025
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2026
						if (!$obj->entity) {
2027
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2028
						} else {
2029
							if ($obj->entity != $conf->entity) {
2030
								$moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2031
							}
2032
						}
2033
					}
2034
					$moreinfo .= ($moreinfo ? ')' : '');
2035
					if ($disableline && $disableline != '1') {
2036
						$moreinfo .= ' - '.$disableline; // This is text from $enableonlytext parameter
2037
					}
2038
					$labeltoshow .= $moreinfo;
2039
2040
					$out .= '<option value="'.$obj->rowid.'"';
2041
					if ($disableline) {
2042
						$out .= ' disabled';
2043
					}
2044
					if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2045
						$out .= ' selected';
2046
					}
2047
					$out .= ' data-html="';
2048
					$outhtml = '';
2049
					// if (!empty($obj->photo)) {
2050
					$outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2051
					// }
2052
					if ($showstatus >= 0 && $obj->status == 0) {
2053
						$outhtml .= '<strike class="opacitymediumxxx">';
2054
					}
2055
					$outhtml .= $labeltoshow;
2056
					if ($showstatus >= 0 && $obj->status == 0) {
2057
						$outhtml .= '</strike>';
2058
					}
2059
					$out .= dol_escape_htmltag($outhtml);
2060
					$out .= '">';
2061
					$out .= $labeltoshow;
2062
					$out .= '</option>';
2063
2064
					$outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2065
2066
					$i++;
2067
				}
2068
			} else {
2069
				$out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2070
				$out .= '<option value="">'.$langs->trans("None").'</option>';
2071
			}
2072
			$out .= '</select>';
2073
2074
			if ($num) {
2075
				// Enhance with select2
2076
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2077
				$out .= ajax_combobox($htmlname);
2078
			}
2079
		} else {
2080
			dol_print_error($this->db);
2081
		}
2082
2083
		if ($outputmode) {
2084
			return $outarray;
2085
		}
2086
		return $out;
2087
	}
2088
2089
2090
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2091
	/**
2092
	 *	Return select list of users. Selected users are stored into session.
2093
	 *  List of users are provided into $_SESSION['assignedtouser'].
2094
	 *
2095
	 *  @param  string	$action         Value for $action
2096
	 *  @param  string	$htmlname       Field name in form
2097
	 *  @param  int		$show_empty     0=list without the empty value, 1=add empty value
2098
	 *  @param  array	$exclude        Array list of users id to exclude
2099
	 * 	@param	int		$disabled		If select list must be disabled
2100
	 *  @param  array	$include        Array list of users id to include or 'hierarchy' to have only supervised users
2101
	 * 	@param	array	$enableonly		Array list of users id to be enabled. All other must be disabled
2102
	 *  @param	int		$force_entity	'0' or Ids of environment to force
2103
	 *  @param	int		$maxlength		Maximum length of string into list (0=no limit)
2104
	 *  @param	int		$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2105
	 *  @param	string	$morefilter		Add more filters into sql request
2106
	 *  @param	int		$showproperties		Show properties of each attendees
2107
	 *  @param	array	$listofuserid		Array with properties of each user
2108
	 *  @param	array	$listofcontactid	Array with properties of each contact
2109
	 *  @param	array	$listofotherid		Array with properties of each other contact
2110
	 * 	@return	string					HTML select string
2111
	 *  @see select_dolgroups()
2112
	 */
2113
	public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
2114
	{
2115
		// phpcs:enable
2116
		global $conf, $user, $langs;
2117
2118
		$userstatic = new User($this->db);
2119
		$out = '';
2120
2121
2122
		$assignedtouser = array();
2123
		if (!empty($_SESSION['assignedtouser'])) {
2124
			$assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2125
		}
2126
		$nbassignetouser = count($assignedtouser);
2127
2128
		//if ($nbassignetouser && $action != 'view') $out .= '<br>';
2129
		if ($nbassignetouser) {
2130
			$out .= '<ul class="attendees">';
2131
		}
2132
		$i = 0;
2133
		$ownerid = 0;
2134
		foreach ($assignedtouser as $key => $value) {
2135
			if ($value['id'] == $ownerid) {
2136
				continue;
2137
			}
2138
2139
			$out .= '<li>';
2140
			$userstatic->fetch($value['id']);
2141
			$out .= $userstatic->getNomUrl(-1);
2142
			if ($i == 0) {
2143
				$ownerid = $value['id'];
2144
				$out .= ' ('.$langs->trans("Owner").')';
2145
			}
2146
			if ($nbassignetouser > 1 && $action != 'view') {
2147
				$out .= ' <input type="image" style="border: 0px;" src="'.img_picto($langs->trans("Remove"), 'delete', '', 0, 1).'" value="'.$userstatic->id.'" class="removedassigned reposition" id="removedassigned_'.$userstatic->id.'" name="removedassigned_'.$userstatic->id.'">';
2148
			}
2149
			// Show my availability
2150
			if ($showproperties) {
2151
				if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2152
					$out .= '<div class="myavailability inline-block">';
2153
					$out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;<span class="opacitymedium">'.$langs->trans("Availability").':</span>  </span><input id="transparency" class="paddingrightonly" '.($action == 'view' ? 'disabled' : '').' type="checkbox" name="transparency"'.($listofuserid[$ownerid]['transparency'] ? ' checked' : '').'><label for="transparency">'.$langs->trans("Busy").'</label>';
2154
					$out .= '</div>';
2155
				}
2156
			}
2157
			//$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2158
			//$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2159
2160
			$out .= '</li>';
2161
			$i++;
2162
		}
2163
		if ($nbassignetouser) {
2164
			$out .= '</ul>';
2165
		}
2166
2167
		// Method with no ajax
2168
		if ($action != 'view') {
2169
			$out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2170
			$out .= '<script type="text/javascript" language="javascript">jQuery(document).ready(function () {';
2171
			$out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2172
			$out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2173
			$out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2174
			$out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2175
			$out .= '});';
2176
			$out .= '})</script>';
2177
			$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2178
			$out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2179
			$out .= '<br>';
2180
		}
2181
2182
		return $out;
2183
	}
2184
2185
2186
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2187
	/**
2188
	 *  Return list of products for customer in Ajax if Ajax activated or go to select_produits_list
2189
	 *
2190
	 *  @param		int			$selected				Preselected products
2191
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
2192
	 *  @param		int|string	$filtertype				Filter on product type (''=nofilter, 0=product, 1=service)
2193
	 *  @param		int			$limit					Limit on number of returned lines
2194
	 *  @param		int			$price_level			Level of price to show
2195
	 *  @param		int			$status					Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell
2196
	 *  @param		int			$finished				2=all, 1=finished, 0=raw material
2197
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
2198
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
2199
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
2200
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
2201
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2202
	 * 	@param		int			$forcecombo				Force to use combo box
2203
	 *  @param      string      $morecss                Add more css on select
2204
	 *  @param      int         $hidepriceinlabel       1=Hide prices in label
2205
	 *  @param      string      $warehouseStatus        Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used
2206
	 *										            'warehouseopen' = count products from open warehouses,
2207
	 *										            'warehouseclosed' = count products from closed warehouses,
2208
	 *										            'warehouseinternal' = count products from warehouses for internal correct/transfer only
2209
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
2210
	 *  @param		string		$nooutput				No print, return the output into a string
2211
	 *  @return		void|string
2212
	 */
2213
	public function select_produits($selected = '', $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)
2214
	{
2215
		// phpcs:enable
2216
		global $langs, $conf;
2217
2218
		$out = '';
2219
2220
		// check parameters
2221
		$price_level = (!empty($price_level) ? $price_level : 0);
2222
		if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
2223
			$ajaxoptions = array();
2224
		}
2225
2226
		if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) {
2227
			if (!empty($conf->product->enabled) && empty($conf->service->enabled)) {
2228
				$filtertype = '0';
2229
			} elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) {
2230
				$filtertype = '1';
2231
			}
2232
		}
2233
2234
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2235
			$placeholder = '';
2236
2237
			if ($selected && empty($selected_input_value)) {
2238
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2239
				$producttmpselect = new Product($this->db);
2240
				$producttmpselect->fetch($selected);
2241
				$selected_input_value = $producttmpselect->ref;
2242
				unset($producttmpselect);
2243
			}
2244
			// handle case where product or service module is disabled + no filter specified
2245
			if ($filtertype == '') {
2246
				if (empty($conf->product->enabled)) { // when product module is disabled, show services only
2247
					$filtertype = 1;
2248
				} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2249
					$filtertype = 0;
2250
				}
2251
			}
2252
			// mode=1 means customers products
2253
			$urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
2254
			//Price by customer
2255
			if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2256
				$urloption .= '&socid='.$socid;
2257
			}
2258
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2259
2260
			if (!empty($conf->variants->enabled) && is_array($selected_combinations)) {
2261
				// Code to automatically insert with javascript the select of attributes under the select of product
2262
				// when a parent of variant has been selected.
2263
				$out .= '
2264
				<!-- script to auto show attributes select tags if a variant was selected -->
2265
				<script>
2266
					// auto show attributes fields
2267
					selected = '.json_encode($selected_combinations).';
2268
					combvalues = {};
2269
2270
					jQuery(document).ready(function () {
2271
2272
						jQuery("input[name=\'prod_entry_mode\']").change(function () {
2273
							if (jQuery(this).val() == \'free\') {
2274
								jQuery(\'div#attributes_box\').empty();
2275
							}
2276
						});
2277
2278
						jQuery("input#'.$htmlname.'").change(function () {
2279
2280
							if (!jQuery(this).val()) {
2281
								jQuery(\'div#attributes_box\').empty();
2282
								return;
2283
							}
2284
2285
							console.log("A change has started. We get variants fields to inject html select");
2286
2287
							jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2288
								id: jQuery(this).val()
2289
							}, function (data) {
2290
								jQuery(\'div#attributes_box\').empty();
2291
2292
								jQuery.each(data, function (key, val) {
2293
2294
									combvalues[val.id] = val.values;
2295
2296
									var span = jQuery(document.createElement(\'div\')).css({
2297
										\'display\': \'table-row\'
2298
									});
2299
2300
									span.append(
2301
										jQuery(document.createElement(\'div\')).text(val.label).css({
2302
											\'font-weight\': \'bold\',
2303
											\'display\': \'table-cell\'
2304
										})
2305
									);
2306
2307
									var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2308
										\'margin-left\': \'15px\',
2309
										\'white-space\': \'pre\'
2310
									}).append(
2311
										jQuery(document.createElement(\'option\')).val(\'\')
2312
									);
2313
2314
									jQuery.each(combvalues[val.id], function (key, val) {
2315
										var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2316
2317
										if (selected[val.fk_product_attribute] == val.id) {
2318
											tag.attr(\'selected\', \'selected\');
2319
										}
2320
2321
										html.append(tag);
2322
									});
2323
2324
									span.append(html);
2325
									jQuery(\'div#attributes_box\').append(span);
2326
								});
2327
							})
2328
						});
2329
2330
						'.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2331
					});
2332
				</script>
2333
                ';
2334
			}
2335
2336
			if (empty($hidelabel)) {
2337
				$out .= $langs->trans("RefOrLabel").' : ';
2338
			} elseif ($hidelabel > 1) {
2339
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2340
				if ($hidelabel == 2) {
2341
					$out .= img_picto($langs->trans("Search"), 'search');
2342
				}
2343
			}
2344
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
2345
			if ($hidelabel == 3) {
2346
				$out .= img_picto($langs->trans("Search"), 'search');
2347
			}
2348
		} else {
2349
			$out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus);
2350
		}
2351
2352
		if (empty($nooutput)) {
2353
			print $out;
2354
		} else {
2355
			return $out;
2356
		}
2357
	}
2358
2359
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2360
	/**
2361
	 *	Return list of products for a customer.
2362
	 *  Called by select_produits.
2363
	 *
2364
	 *	@param      int		$selected           Preselected product
2365
	 *	@param      string	$htmlname           Name of select html
2366
	 *  @param		string	$filtertype         Filter on product type (''=nofilter, 0=product, 1=service)
2367
	 *	@param      int		$limit              Limit on number of returned lines
2368
	 *	@param      int		$price_level        Level of price to show
2369
	 * 	@param      string	$filterkey          Filter on product
2370
	 *	@param		int		$status             -1=Return all products, 0=Products not on sell, 1=Products on sell
2371
	 *  @param      int		$finished           Filter on finished field: 2=No filter
2372
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
2373
	 *  @param      int		$socid     		    Thirdparty Id (to get also price dedicated to this customer)
2374
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2375
	 * 	@param		int		$forcecombo		    Force to use combo box
2376
	 *  @param      string  $morecss            Add more css on select
2377
	 *  @param      int     $hidepriceinlabel   1=Hide prices in label
2378
	 *  @param      string  $warehouseStatus    Warehouse status filter to group/count stock. Following comma separated filter options can be used.
2379
	 *										    'warehouseopen' = count products from open warehouses,
2380
	 *										    'warehouseclosed' = count products from closed warehouses,
2381
	 *										    'warehouseinternal' = count products from warehouses for internal correct/transfer only
2382
	 *  @return     array    				    Array of keys for json
2383
	 */
2384
	public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '')
2385
	{
2386
		// phpcs:enable
2387
		global $langs, $conf, $user, $db;
2388
2389
		$out = '';
2390
		$outarray = array();
2391
2392
		// Units
2393
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2394
			$langs->load('other');
2395
		}
2396
2397
		$warehouseStatusArray = array();
2398
		if (!empty($warehouseStatus)) {
2399
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2400
			if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2401
				$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2402
			}
2403
			if (preg_match('/warehouseopen/', $warehouseStatus)) {
2404
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2405
			}
2406
			if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2407
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2408
			}
2409
		}
2410
2411
		$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.duration, p.fk_price_expression";
2412
		if (count($warehouseStatusArray)) {
2413
			$selectFieldsGrouped = ", sum(".$this->db->ifsql("e.statut IS NULL", "0", "ps.reel").") as stock"; // e.statut is null if there is no record in stock
2414
		} else {
2415
			$selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2416
		}
2417
2418
		$sql = "SELECT ";
2419
		$sql .= $selectFields.$selectFieldsGrouped;
2420
2421
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2422
			//Product category
2423
			$sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie
2424
						FROM ".MAIN_DB_PREFIX."categorie_product
2425
						WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid
2426
						LIMIT 1
2427
				) AS categorie_product_id ";
2428
		}
2429
2430
		//Price by customer
2431
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2432
			$sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2433
			$sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.ref_customer as custref';
2434
			$selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custref";
2435
		}
2436
		// Units
2437
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2438
			$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";
2439
			$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';
2440
		}
2441
2442
		// Multilang : we add translation
2443
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2444
			$sql .= ", pl.label as label_translated";
2445
			$sql .= ", pl.description as description_translated";
2446
			$selectFields .= ", label_translated";
2447
			$selectFields .= ", description_translated";
2448
		}
2449
		// Price by quantity
2450
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2451
			$sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid";
2452
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2453
				$sql .= " AND price_level = ".((int) $price_level);
2454
			}
2455
			$sql .= " ORDER BY date_price";
2456
			$sql .= " DESC LIMIT 1) as price_rowid";
2457
			$sql .= ", (SELECT pp.price_by_qty FROM ".MAIN_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
2458
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2459
				$sql .= " AND price_level = ".((int) $price_level);
2460
			}
2461
			$sql .= " ORDER BY date_price";
2462
			$sql .= " DESC LIMIT 1) as price_by_qty";
2463
			$selectFields .= ", price_rowid, price_by_qty";
2464
		}
2465
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
2466
		if (count($warehouseStatusArray)) {
2467
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid";
2468
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2469
			$sql .= ' AND e.statut IN ('.$this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))).')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0.
2470
		}
2471
2472
		// include search in supplier ref
2473
		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2474
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2475
		}
2476
2477
		//Price by customer
2478
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2479
			$sql .= " LEFT JOIN  ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".((int) $socid)." AND pcp.fk_product=p.rowid";
2480
		}
2481
		// Units
2482
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2483
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
2484
		}
2485
		// Multilang : we add translation
2486
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2487
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid ";
2488
			if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2489
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2490
				$soc = new Societe($db);
2491
				$result = $soc->fetch($socid);
2492
				if ($result > 0 && !empty($soc->default_lang)) {
2493
					$sql .= " AND pl.lang='" . $this->db->escape($soc->default_lang) . "'";
2494
				} else {
2495
					$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2496
				}
2497
			} else {
2498
				$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2499
			}
2500
		}
2501
2502
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2503
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2504
		}
2505
2506
		$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2507
2508
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2509
			$sql .= " AND pac.rowid IS NULL";
2510
		}
2511
2512
		if ($finished == 0) {
2513
			$sql .= " AND p.finished = ".((int) $finished);
2514
		} elseif ($finished == 1) {
2515
			$sql .= " AND p.finished = ".((int) $finished);
2516
			if ($status >= 0) {
2517
				$sql .= " AND p.tosell = ".((int) $status);
2518
			}
2519
		} elseif ($status >= 0) {
2520
			$sql .= " AND p.tosell = ".((int) $status);
2521
		}
2522
		// Filter by product type
2523
		if (strval($filtertype) != '') {
2524
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
2525
		} elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only
2526
			$sql .= " AND p.fk_product_type = 1";
2527
		} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2528
			$sql .= " AND p.fk_product_type = 0";
2529
		}
2530
		// Add criteria on ref/label
2531
		if ($filterkey != '') {
2532
			$sql .= ' AND (';
2533
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2534
			// For natural search
2535
			$scrit = explode(' ', $filterkey);
2536
			$i = 0;
2537
			if (count($scrit) > 1) {
2538
				$sql .= "(";
2539
			}
2540
			foreach ($scrit as $crit) {
2541
				if ($i > 0) {
2542
					$sql .= " AND ";
2543
				}
2544
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2545
				if (!empty($conf->global->MAIN_MULTILANGS)) {
2546
					$sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2547
				}
2548
				if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) {
2549
					$sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2550
				}
2551
				if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2552
					$sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2553
					if (!empty($conf->global->MAIN_MULTILANGS)) {
2554
						$sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2555
					}
2556
				}
2557
				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2558
					$sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2559
				}
2560
				$sql .= ")";
2561
				$i++;
2562
			}
2563
			if (count($scrit) > 1) {
2564
				$sql .= ")";
2565
			}
2566
			if (!empty($conf->barcode->enabled)) {
2567
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2568
			}
2569
			$sql .= ')';
2570
		}
2571
		if (count($warehouseStatusArray)) {
2572
			$sql .= " GROUP BY ".$selectFields;
2573
		}
2574
2575
		//Sort by category
2576
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2577
			$sql .= " ORDER BY categorie_product_id ";
2578
			//ASC OR DESC order
2579
			($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2580
		} else {
2581
			$sql .= $this->db->order("p.ref");
2582
		}
2583
2584
		$sql .= $this->db->plimit($limit, 0);
2585
2586
		// Build output string
2587
		dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2588
		$result = $this->db->query($sql);
2589
		if ($result) {
2590
			require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2591
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2592
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2593
2594
			$num = $this->db->num_rows($result);
2595
2596
			$events = null;
2597
2598
			if (!$forcecombo) {
2599
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2600
				$out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT);
2601
			}
2602
2603
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2604
2605
			$textifempty = '';
2606
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2607
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2608
			if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2609
				if ($showempty && !is_numeric($showempty)) {
2610
					$textifempty = $langs->trans($showempty);
2611
				} else {
2612
					$textifempty .= $langs->trans("All");
2613
				}
2614
			} else {
2615
				if ($showempty && !is_numeric($showempty)) {
2616
					$textifempty = $langs->trans($showempty);
2617
				}
2618
			}
2619
			if ($showempty) {
2620
				$out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2621
			}
2622
2623
			$i = 0;
2624
			while ($num && $i < $num) {
2625
				$opt = '';
2626
				$optJson = array();
2627
				$objp = $this->db->fetch_object($result);
2628
2629
				if ((!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->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
2630
					$sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2631
					$sql .= " FROM ".MAIN_DB_PREFIX."product_price_by_qty";
2632
					$sql .= " WHERE fk_product_price = ".((int) $objp->price_rowid);
2633
					$sql .= " ORDER BY quantity ASC";
2634
2635
					dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2636
					$result2 = $this->db->query($sql);
2637
					if ($result2) {
2638
						$nb_prices = $this->db->num_rows($result2);
2639
						$j = 0;
2640
						while ($nb_prices && $j < $nb_prices) {
2641
							$objp2 = $this->db->fetch_object($result2);
2642
2643
							$objp->price_by_qty_rowid = $objp2->rowid;
2644
							$objp->price_by_qty_price_base_type = $objp2->price_base_type;
2645
							$objp->price_by_qty_quantity = $objp2->quantity;
2646
							$objp->price_by_qty_unitprice = $objp2->unitprice;
2647
							$objp->price_by_qty_remise_percent = $objp2->remise_percent;
2648
							// For backward compatibility
2649
							$objp->quantity = $objp2->quantity;
2650
							$objp->price = $objp2->price;
2651
							$objp->unitprice = $objp2->unitprice;
2652
							$objp->remise_percent = $objp2->remise_percent;
2653
							$objp->remise = $objp2->remise;
2654
2655
							$this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2656
2657
							$j++;
2658
2659
							// Add new entry
2660
							// "key" value of json key array is used by jQuery automatically as selected value
2661
							// "label" value of json key array is used by jQuery automatically as text for combo box
2662
							$out .= $opt;
2663
							array_push($outarray, $optJson);
2664
						}
2665
					}
2666
				} else {
2667
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
2668
						$price_product = new Product($this->db);
2669
						$price_product->fetch($objp->rowid, '', '', 1);
2670
						$priceparser = new PriceParser($this->db);
2671
						$price_result = $priceparser->parseProduct($price_product);
2672
						if ($price_result >= 0) {
2673
							$objp->price = $price_result;
2674
							$objp->unitprice = $price_result;
2675
							//Calculate the VAT
2676
							$objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2677
							$objp->price_ttc = price2num($objp->price_ttc, 'MU');
2678
						}
2679
					}
2680
2681
					$this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2682
					// Add new entry
2683
					// "key" value of json key array is used by jQuery automatically as selected value
2684
					// "label" value of json key array is used by jQuery automatically as text for combo box
2685
					$out .= $opt;
2686
					array_push($outarray, $optJson);
2687
				}
2688
2689
				$i++;
2690
			}
2691
2692
			$out .= '</select>';
2693
2694
			$this->db->free($result);
2695
2696
			if (empty($outputmode)) {
2697
				return $out;
2698
			}
2699
			return $outarray;
2700
		} else {
2701
			dol_print_error($db);
2702
		}
2703
	}
2704
2705
	/**
2706
	 * constructProductListOption.
2707
	 * This define value for &$opt and &$optJson.
2708
	 *
2709
	 * @param 	resource	$objp			    Resultset of fetch
2710
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
2711
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
2712
	 * @param 	int			$price_level	    Price level
2713
	 * @param 	string		$selected		    Preselected value
2714
	 * @param   int         $hidepriceinlabel   Hide price in label
2715
	 * @param   string      $filterkey          Filter key to highlight
2716
	 * @param	int			$novirtualstock 	Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on.
2717
	 * @return	void
2718
	 */
2719
	protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2720
	{
2721
		global $langs, $conf, $user, $db;
2722
2723
		$outkey = '';
2724
		$outval = '';
2725
		$outref = '';
2726
		$outlabel = '';
2727
		$outlabel_translated = '';
2728
		$outdesc = '';
2729
		$outdesc_translated = '';
2730
		$outbarcode = '';
2731
		$outorigin = '';
2732
		$outtype = '';
2733
		$outprice_ht = '';
2734
		$outprice_ttc = '';
2735
		$outpricebasetype = '';
2736
		$outtva_tx = '';
2737
		$outqty = 1;
2738
		$outdiscount = 0;
2739
2740
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2741
2742
		$label = $objp->label;
2743
		if (!empty($objp->label_translated)) {
2744
			$label = $objp->label_translated;
2745
		}
2746
		if (!empty($filterkey) && $filterkey != '') {
2747
			$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2748
		}
2749
2750
		$outkey = $objp->rowid;
2751
		$outref = $objp->ref;
2752
		$outrefcust = empty($objp->custref) ? '' : $objp->custref;
2753
		$outlabel = $objp->label;
2754
		$outdesc = $objp->description;
2755
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2756
			$outlabel_translated = $objp->label_translated;
2757
			$outdesc_translated = $objp->description_translated;
2758
		}
2759
		$outbarcode = $objp->barcode;
2760
		$outorigin = $objp->fk_country;
2761
		$outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2762
2763
		$outtype = $objp->fk_product_type;
2764
		$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2765
		$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2766
2767
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2768
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2769
		}
2770
2771
		// Units
2772
		$outvalUnits = '';
2773
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2774
			if (!empty($objp->unit_short)) {
2775
				$outvalUnits .= ' - '.$objp->unit_short;
2776
			}
2777
		}
2778
		if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2779
			if (!empty($objp->weight) && $objp->weight_units !== null) {
2780
				$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2781
				$outvalUnits .= ' - '.$unitToShow;
2782
			}
2783
			if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2784
				$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2785
				$outvalUnits .= ' - '.$unitToShow;
2786
			}
2787
			if (!empty($objp->surface) && $objp->surface_units !== null) {
2788
				$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2789
				$outvalUnits .= ' - '.$unitToShow;
2790
			}
2791
			if (!empty($objp->volume) && $objp->volume_units !== null) {
2792
				$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2793
				$outvalUnits .= ' - '.$unitToShow;
2794
			}
2795
		}
2796
		if ($outdurationvalue && $outdurationunit) {
2797
			$da = array(
2798
				'h' => $langs->trans('Hour'),
2799
				'd' => $langs->trans('Day'),
2800
				'w' => $langs->trans('Week'),
2801
				'm' => $langs->trans('Month'),
2802
				'y' => $langs->trans('Year')
2803
			);
2804
			if (isset($da[$outdurationunit])) {
2805
				$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2806
			}
2807
		}
2808
2809
		$opt = '<option value="'.$objp->rowid.'"';
2810
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
2811
		if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2812
			$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.'"';
2813
		}
2814
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2815
			if (!empty($user->rights->stock->lire)) {
2816
				if ($objp->stock > 0) {
2817
					$opt .= ' class="product_line_stock_ok"';
2818
				} elseif ($objp->stock <= 0) {
2819
					$opt .= ' class="product_line_stock_too_low"';
2820
				}
2821
			}
2822
		}
2823
		if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2824
			$opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2825
			$opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2826
		}
2827
		$opt .= '>';
2828
		$opt .= $objp->ref;
2829
		if (! empty($objp->custref)) {
2830
			$opt.= ' (' . $objp->custref . ')';
2831
		}
2832
		if ($outbarcode) {
2833
			$opt .= ' ('.$outbarcode.')';
2834
		}
2835
		$opt .= ' - '.dol_trunc($label, $maxlengtharticle);
2836
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2837
			$opt .= ' ('.getCountry($outorigin, 1).')';
2838
		}
2839
2840
		$objRef = $objp->ref;
2841
		if (! empty($objp->custref)) {
2842
			$objRef .= ' (' . $objp->custref . ')';
2843
		}
2844
		if (!empty($filterkey) && $filterkey != '') {
2845
			$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2846
		}
2847
		$outval .= $objRef;
2848
		if ($outbarcode) {
2849
			$outval .= ' ('.$outbarcode.')';
2850
		}
2851
		$outval .= ' - '.dol_trunc($label, $maxlengtharticle);
2852
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2853
			$outval .= ' ('.getCountry($outorigin, 1).')';
2854
		}
2855
2856
		// Units
2857
		$opt .= $outvalUnits;
2858
		$outval .= $outvalUnits;
2859
2860
		$found = 0;
2861
2862
		// Multiprice
2863
		// If we need a particular price level (from 1 to 6)
2864
		if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
2865
			$sql = "SELECT price, price_ttc, price_base_type, tva_tx";
2866
			$sql .= " FROM ".MAIN_DB_PREFIX."product_price";
2867
			$sql .= " WHERE fk_product = ".((int) $objp->rowid);
2868
			$sql .= " AND entity IN (".getEntity('productprice').")";
2869
			$sql .= " AND price_level = ".((int) $price_level);
2870
			$sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
2871
			$sql .= " LIMIT 1";
2872
2873
			dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
2874
			$result2 = $this->db->query($sql);
2875
			if ($result2) {
2876
				$objp2 = $this->db->fetch_object($result2);
2877
				if ($objp2) {
2878
					$found = 1;
2879
					if ($objp2->price_base_type == 'HT') {
2880
						$opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2881
						$outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2882
					} else {
2883
						$opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2884
						$outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2885
					}
2886
					$outprice_ht = price($objp2->price);
2887
					$outprice_ttc = price($objp2->price_ttc);
2888
					$outpricebasetype = $objp2->price_base_type;
2889
					$outtva_tx = $objp2->tva_tx;
2890
				}
2891
			} else {
2892
				dol_print_error($this->db);
2893
			}
2894
		}
2895
2896
		// Price by quantity
2897
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
2898
			$found = 1;
2899
			$outqty = $objp->quantity;
2900
			$outdiscount = $objp->remise_percent;
2901
			if ($objp->quantity == 1) {
2902
				$opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
2903
				$outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
2904
				$opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
2905
				$outval .= $langs->transnoentities("Unit");
2906
			} else {
2907
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2908
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2909
				$opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
2910
				$outval .= $langs->transnoentities("Units");
2911
			}
2912
2913
			$outprice_ht = price($objp->unitprice);
2914
			$outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
2915
			$outpricebasetype = $objp->price_base_type;
2916
			$outtva_tx = $objp->tva_tx;
2917
		}
2918
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
2919
			$opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2920
			$outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2921
		}
2922
		if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
2923
			$opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
2924
			$outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
2925
		}
2926
2927
		// Price by customer
2928
		if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
2929
			if (!empty($objp->idprodcustprice)) {
2930
				$found = 1;
2931
2932
				if ($objp->custprice_base_type == 'HT') {
2933
					$opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2934
					$outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2935
				} else {
2936
					$opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2937
					$outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2938
				}
2939
2940
				$outprice_ht = price($objp->custprice);
2941
				$outprice_ttc = price($objp->custprice_ttc);
2942
				$outpricebasetype = $objp->custprice_base_type;
2943
				$outtva_tx = $objp->custtva_tx;
2944
			}
2945
		}
2946
2947
		// If level no defined or multiprice not found, we used the default price
2948
		if (empty($hidepriceinlabel) && !$found) {
2949
			if ($objp->price_base_type == 'HT') {
2950
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2951
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2952
			} else {
2953
				$opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2954
				$outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2955
			}
2956
			$outprice_ht = price($objp->price);
2957
			$outprice_ttc = price($objp->price_ttc);
2958
			$outpricebasetype = $objp->price_base_type;
2959
			$outtva_tx = $objp->tva_tx;
2960
		}
2961
2962
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2963
			if (!empty($user->rights->stock->lire)) {
2964
				$opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
2965
2966
				if ($objp->stock > 0) {
2967
					$outval .= ' - <span class="product_line_stock_ok">';
2968
				} elseif ($objp->stock <= 0) {
2969
					$outval .= ' - <span class="product_line_stock_too_low">';
2970
				}
2971
				$outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
2972
				$outval .= '</span>';
2973
				if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
2974
					$langs->load("stocks");
2975
2976
					$tmpproduct = new Product($this->db);
2977
					$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
2978
					$tmpproduct->load_virtual_stock();
2979
					$virtualstock = $tmpproduct->stock_theorique;
2980
2981
					$opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
2982
2983
					$outval .= ' - '.$langs->transnoentities("VirtualStock").':';
2984
					if ($virtualstock > 0) {
2985
						$outval .= '<span class="product_line_stock_ok">';
2986
					} elseif ($virtualstock <= 0) {
2987
						$outval .= '<span class="product_line_stock_too_low">';
2988
					}
2989
					$outval .= $virtualstock;
2990
					$outval .= '</span>';
2991
2992
					unset($tmpproduct);
2993
				}
2994
			}
2995
		}
2996
2997
		$opt .= "</option>\n";
2998
		$optJson = array(
2999
			'key'=>$outkey,
3000
			'value'=>$outref,
3001
			'label'=>$outval,
3002
			'label2'=>$outlabel,
3003
			'desc'=>$outdesc,
3004
			'type'=>$outtype,
3005
			'price_ht'=>price2num($outprice_ht),
3006
			'price_ttc'=>price2num($outprice_ttc),
3007
			'pricebasetype'=>$outpricebasetype,
3008
			'tva_tx'=>$outtva_tx, 'qty'=>$outqty,
3009
			'discount'=>$outdiscount,
3010
			'duration_value'=>$outdurationvalue,
3011
			'duration_unit'=>$outdurationunit,
3012
			'pbq'=>$outpbq,
3013
			'labeltrans'=>$outlabel_translated,
3014
			'desctrans'=>$outdesc_translated,
3015
			'ref_customer'=>$outrefcust
3016
		);
3017
	}
3018
3019
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3020
	/**
3021
	 *	Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list)
3022
	 *
3023
	 *	@param	int		$socid			Id third party
3024
	 *	@param  string	$selected       Preselected product
3025
	 *	@param  string	$htmlname       Name of HTML Select
3026
	 *  @param	string	$filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
3027
	 *	@param  string	$filtre			For a SQL filter
3028
	 *	@param	array	$ajaxoptions	Options for ajax_autocompleter
3029
	 *  @param	int		$hidelabel		Hide label (0=no, 1=yes)
3030
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3031
	 *  @param	string	$morecss		More CSS
3032
	 *  @param	string	$placeholder	Placeholder
3033
	 *	@return	void
3034
	 */
3035
	public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3036
	{
3037
		// phpcs:enable
3038
		global $langs, $conf;
3039
		global $price_level, $status, $finished;
3040
3041
		if (!isset($status)) {
3042
			$status = 1;
3043
		}
3044
3045
		$selected_input_value = '';
3046
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3047
			if ($selected > 0) {
3048
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3049
				$producttmpselect = new Product($this->db);
3050
				$producttmpselect->fetch($selected);
3051
				$selected_input_value = $producttmpselect->ref;
3052
				unset($producttmpselect);
3053
			}
3054
3055
			// mode=2 means suppliers products
3056
			$urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3057
			print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3058
			print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3059
		} else {
3060
			print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3061
		}
3062
	}
3063
3064
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3065
	/**
3066
	 *	Return list of suppliers products
3067
	 *
3068
	 *	@param	int		$socid   			Id of supplier thirdparty (0 = no filter)
3069
	 *	@param  int		$selected       	Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD')
3070
	 *	@param  string	$htmlname       	Name of HTML select
3071
	 *  @param	string	$filtertype     	Filter on product type (''=nofilter, 0=product, 1=service)
3072
	 *	@param  string	$filtre         	Generic filter. Data must not come from user input.
3073
	 *	@param  string	$filterkey      	Filter of produdts
3074
	 *  @param  int		$statut         	-1=Return all products, 0=Products not on buy, 1=Products on buy
3075
	 *  @param  int		$outputmode     	0=HTML select string, 1=Array
3076
	 *  @param  int     $limit          	Limit of line number
3077
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3078
	 *  @param	string	$morecss			Add more CSS
3079
	 *  @param	int		$showstockinlist	Show stock information (slower).
3080
	 *  @param	string	$placeholder		Placeholder
3081
	 *  @return array           			Array of keys for json
3082
	 */
3083
	public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '', $showstockinlist = 0, $placeholder = '')
3084
	{
3085
		// phpcs:enable
3086
		global $langs, $conf, $db, $user;
3087
3088
		$out = '';
3089
		$outarray = array();
3090
3091
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3092
3093
		$langs->load('stocks');
3094
		// Units
3095
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3096
			$langs->load('other');
3097
		}
3098
3099
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock,";
3100
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3101
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name,";
3102
		$sql .= " pfp.supplier_reputation";
3103
		// if we use supplier description of the products
3104
		if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3105
			$sql .= " ,pfp.desc_fourn as description";
3106
		} else {
3107
			$sql .= " ,p.description";
3108
		}
3109
		// Units
3110
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3111
			$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";
3112
		}
3113
		if (!empty($conf->barcode->enabled)) {
3114
			$sql .= ", pfp.barcode";
3115
		}
3116
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3117
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3118
		if ($socid > 0) {
3119
			$sql .= " AND pfp.fk_soc = ".((int) $socid);
3120
		}
3121
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3122
		// Units
3123
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3124
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
3125
		}
3126
		$sql .= " WHERE p.entity IN (".getEntity('product').")";
3127
		if ($statut != -1) {
3128
			$sql .= " AND p.tobuy = ".((int) $statut);
3129
		}
3130
		if (strval($filtertype) != '') {
3131
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
3132
		}
3133
		if (!empty($filtre)) {
3134
			$sql .= " ".$filtre;
3135
		}
3136
		// Add criteria on ref/label
3137
		if ($filterkey != '') {
3138
			$sql .= ' AND (';
3139
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3140
			// For natural search
3141
			$scrit = explode(' ', $filterkey);
3142
			$i = 0;
3143
			if (count($scrit) > 1) {
3144
				$sql .= "(";
3145
			}
3146
			foreach ($scrit as $crit) {
3147
				if ($i > 0) {
3148
					$sql .= " AND ";
3149
				}
3150
				$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)."%'";
3151
				if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3152
					$sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3153
				}
3154
				$sql .= ")";
3155
				$i++;
3156
			}
3157
			if (count($scrit) > 1) {
3158
				$sql .= ")";
3159
			}
3160
			if (!empty($conf->barcode->enabled)) {
3161
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3162
				$sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3163
			}
3164
			$sql .= ')';
3165
		}
3166
		$sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3167
		$sql .= $this->db->plimit($limit, 0);
3168
3169
		// Build output string
3170
3171
		dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3172
		$result = $this->db->query($sql);
3173
		if ($result) {
3174
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3175
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3176
3177
			$num = $this->db->num_rows($result);
3178
3179
			//$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';	// remove select to have id same with combo and ajax
3180
			$out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3181
			if (!$selected) {
3182
				$out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3183
			} else {
3184
				$out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3185
			}
3186
3187
			$i = 0;
3188
			while ($i < $num) {
3189
				$objp = $this->db->fetch_object($result);
3190
3191
				$outkey = $objp->idprodfournprice; // id in table of price
3192
				if (!$outkey && $alsoproductwithnosupplierprice) {
3193
					$outkey = 'idprod_'.$objp->rowid; // id of product
3194
				}
3195
3196
				$outref = $objp->ref;
3197
				$outval = '';
3198
				$outbarcode = $objp->barcode;
3199
				$outqty = 1;
3200
				$outdiscount = 0;
3201
				$outtype = $objp->fk_product_type;
3202
				$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3203
				$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3204
3205
				// Units
3206
				$outvalUnits = '';
3207
				if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3208
					if (!empty($objp->unit_short)) {
3209
						$outvalUnits .= ' - '.$objp->unit_short;
3210
					}
3211
					if (!empty($objp->weight) && $objp->weight_units !== null) {
3212
						$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3213
						$outvalUnits .= ' - '.$unitToShow;
3214
					}
3215
					if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3216
						$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3217
						$outvalUnits .= ' - '.$unitToShow;
3218
					}
3219
					if (!empty($objp->surface) && $objp->surface_units !== null) {
3220
						$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3221
						$outvalUnits .= ' - '.$unitToShow;
3222
					}
3223
					if (!empty($objp->volume) && $objp->volume_units !== null) {
3224
						$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3225
						$outvalUnits .= ' - '.$unitToShow;
3226
					}
3227
					if ($outdurationvalue && $outdurationunit) {
3228
						$da = array(
3229
							'h' => $langs->trans('Hour'),
3230
							'd' => $langs->trans('Day'),
3231
							'w' => $langs->trans('Week'),
3232
							'm' => $langs->trans('Month'),
3233
							'y' => $langs->trans('Year')
3234
						);
3235
						if (isset($da[$outdurationunit])) {
3236
							$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3237
						}
3238
					}
3239
				}
3240
3241
				$objRef = $objp->ref;
3242
				if ($filterkey && $filterkey != '') {
3243
					$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3244
				}
3245
				$objRefFourn = $objp->ref_fourn;
3246
				if ($filterkey && $filterkey != '') {
3247
					$objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3248
				}
3249
				$label = $objp->label;
3250
				if ($filterkey && $filterkey != '') {
3251
					$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3252
				}
3253
3254
				$optlabel = $objp->ref;
3255
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3256
					$optlabel .= ' <span class=\'opacitymedium\'>('.$objp->ref_fourn.')</span>';
3257
				}
3258
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3259
					$optlabel .= ' ('.$outbarcode.')';
3260
				}
3261
				$optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3262
3263
				$outvallabel = $objRef;
3264
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3265
					$outvallabel .= ' ('.$objRefFourn.')';
3266
				}
3267
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3268
					$outvallabel .= ' ('.$outbarcode.')';
3269
				}
3270
				$outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3271
3272
				// Units
3273
				$optlabel .= $outvalUnits;
3274
				$outvallabel .= $outvalUnits;
3275
3276
				if (!empty($objp->idprodfournprice)) {
3277
					$outqty = $objp->quantity;
3278
					$outdiscount = $objp->remise_percent;
3279
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3280
						$prod_supplier = new ProductFournisseur($this->db);
3281
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3282
						$prod_supplier->id = $objp->fk_product;
3283
						$prod_supplier->fourn_qty = $objp->quantity;
3284
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3285
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3286
						$priceparser = new PriceParser($this->db);
3287
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3288
						if ($price_result >= 0) {
3289
							$objp->fprice = $price_result;
3290
							if ($objp->quantity >= 1) {
3291
								$objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3292
							}
3293
						}
3294
					}
3295
					if ($objp->quantity == 1) {
3296
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3297
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3298
						$optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3299
						$outvallabel .= $langs->transnoentities("Unit");
3300
					} else {
3301
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3302
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3303
						$optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3304
						$outvallabel .= ' '.$langs->transnoentities("Units");
3305
					}
3306
3307
					if ($objp->quantity > 1) {
3308
						$optlabel .= " (".price($objp->unitprice * (!empty($conf->global->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
3309
						$outvallabel .= " (".price($objp->unitprice * (!empty($conf->global->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
3310
					}
3311
					if ($objp->remise_percent >= 1) {
3312
						$optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3313
						$outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3314
					}
3315
					if ($objp->duration) {
3316
						$optlabel .= " - ".$objp->duration;
3317
						$outvallabel .= " - ".$objp->duration;
3318
					}
3319
					if (!$socid) {
3320
						$optlabel .= " - ".dol_trunc($objp->name, 8);
3321
						$outvallabel .= " - ".dol_trunc($objp->name, 8);
3322
					}
3323
					if ($objp->supplier_reputation) {
3324
						//TODO dictionary
3325
						$reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3326
3327
						$optlabel .= " - ".$reputations[$objp->supplier_reputation];
3328
						$outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3329
					}
3330
				} else {
3331
					if (empty($alsoproductwithnosupplierprice)) {     // No supplier price defined for couple product/supplier
3332
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3333
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3334
					} else // No supplier price defined for product, even on other suppliers
3335
					{
3336
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3337
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3338
					}
3339
				}
3340
3341
				if (!empty($conf->stock->enabled) && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3342
					$novirtualstock = ($showstockinlist == 2);
3343
3344
					if (!empty($user->rights->stock->lire)) {
3345
						$outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3346
3347
						if ($objp->stock > 0) {
3348
							$optlabel .= ' - <span class="product_line_stock_ok">';
3349
						} elseif ($objp->stock <= 0) {
3350
							$optlabel .= ' - <span class="product_line_stock_too_low">';
3351
						}
3352
						$optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3353
						$optlabel .= '</span>';
3354
						if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
3355
							$langs->load("stocks");
3356
3357
							$tmpproduct = new Product($this->db);
3358
							$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3359
							$tmpproduct->load_virtual_stock();
3360
							$virtualstock = $tmpproduct->stock_theorique;
3361
3362
							$outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3363
3364
							$optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3365
							if ($virtualstock > 0) {
3366
								$optlabel .= '<span class="product_line_stock_ok">';
3367
							} elseif ($virtualstock <= 0) {
3368
								$optlabel .= '<span class="product_line_stock_too_low">';
3369
							}
3370
							$optlabel .= $virtualstock;
3371
							$optlabel .= '</span>';
3372
3373
							unset($tmpproduct);
3374
						}
3375
					}
3376
				}
3377
3378
				$opt = '<option value="'.$outkey.'"';
3379
				if ($selected && $selected == $objp->idprodfournprice) {
3380
					$opt .= ' selected';
3381
				}
3382
				if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3383
					$opt .= ' disabled';
3384
				}
3385
				if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3386
					$opt .= ' data-product-id="'.$objp->rowid.'" data-price-id="'.$objp->idprodfournprice.'" data-qty="'.$objp->quantity.'" data-up="'.$objp->unitprice.'" data-discount="'.$outdiscount.'"';
3387
				}
3388
				$opt .= ' data-description="'.dol_escape_htmltag($objp->description, 0, 1).'"';
3389
				$opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"';
3390
				$opt .= '>';
3391
3392
				$opt .= $optlabel;
3393
				$outval .= $outvallabel;
3394
3395
				$opt .= "</option>\n";
3396
3397
3398
				// Add new entry
3399
				// "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
3400
				// "label" value of json key array is used by jQuery automatically as text for combo box
3401
				$out .= $opt;
3402
				array_push(
3403
					$outarray,
3404
					array('key'=>$outkey,
3405
						'value'=>$outref,
3406
						'label'=>$outval,
3407
						'qty'=>$outqty,
3408
						'price_ht'=>price2num($objp->unitprice, 'MT'),
3409
						'discount'=>$outdiscount,
3410
						'type'=>$outtype,
3411
						'duration_value'=>$outdurationvalue,
3412
						'duration_unit'=>$outdurationunit,
3413
						'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3414
						'description'=>$objp->description
3415
					)
3416
				);
3417
				// Exemple of var_dump $outarray
3418
				// array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3419
				//           ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3420
				//      	 ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3421
				//}
3422
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3423
				//$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3424
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3425
3426
				$i++;
3427
			}
3428
			$out .= '</select>';
3429
3430
			$this->db->free($result);
3431
3432
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3433
			$out .= ajax_combobox($htmlname);
3434
3435
			if (empty($outputmode)) {
3436
				return $out;
3437
			}
3438
			return $outarray;
3439
		} else {
3440
			dol_print_error($this->db);
3441
		}
3442
	}
3443
3444
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3445
	/**
3446
	 *	Return list of suppliers prices for a product
3447
	 *
3448
	 *  @param	    int		$productid       	Id of product
3449
	 *  @param      string	$htmlname        	Name of HTML field
3450
	 *  @param      int		$selected_supplier  Pre-selected supplier if more than 1 result
3451
	 *  @return	    string
3452
	 */
3453
	public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3454
	{
3455
		// phpcs:enable
3456
		global $langs, $conf;
3457
3458
		$langs->load('stocks');
3459
3460
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3461
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3462
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3463
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3464
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3465
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3466
		$sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3467
		$sql .= " AND p.tobuy = 1";
3468
		$sql .= " AND s.fournisseur = 1";
3469
		$sql .= " AND p.rowid = ".((int) $productid);
3470
		$sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3471
3472
		dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3473
		$result = $this->db->query($sql);
3474
3475
		if ($result) {
3476
			$num = $this->db->num_rows($result);
3477
3478
			$form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3479
3480
			if (!$num) {
3481
				$form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3482
			} else {
3483
				require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3484
				$form .= '<option value="0">&nbsp;</option>';
3485
3486
				$i = 0;
3487
				while ($i < $num) {
3488
					$objp = $this->db->fetch_object($result);
3489
3490
					$opt = '<option value="'.$objp->idprodfournprice.'"';
3491
					//if there is only one supplier, preselect it
3492
					if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier)) {
3493
						$opt .= ' selected';
3494
					}
3495
					$opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3496
3497
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3498
						$prod_supplier = new ProductFournisseur($this->db);
3499
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3500
						$prod_supplier->id = $productid;
3501
						$prod_supplier->fourn_qty = $objp->quantity;
3502
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3503
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3504
						$priceparser = new PriceParser($this->db);
3505
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3506
						if ($price_result >= 0) {
3507
							$objp->fprice = $price_result;
3508
							if ($objp->quantity >= 1) {
3509
								$objp->unitprice = $objp->fprice / $objp->quantity;
3510
							}
3511
						}
3512
					}
3513
					if ($objp->quantity == 1) {
3514
						$opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3515
					}
3516
3517
					$opt .= $objp->quantity.' ';
3518
3519
					if ($objp->quantity == 1) {
3520
						$opt .= $langs->trans("Unit");
3521
					} else {
3522
						$opt .= $langs->trans("Units");
3523
					}
3524
					if ($objp->quantity > 1) {
3525
						$opt .= " - ";
3526
						$opt .= price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit");
3527
					}
3528
					if ($objp->duration) {
3529
						$opt .= " - ".$objp->duration;
3530
					}
3531
					$opt .= "</option>\n";
3532
3533
					$form .= $opt;
3534
					$i++;
3535
				}
3536
			}
3537
3538
			$form .= '</select>';
3539
			$this->db->free($result);
3540
			return $form;
3541
		} else {
3542
			dol_print_error($this->db);
3543
		}
3544
	}
3545
3546
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3547
	/**
3548
	 *    Return list of delivery address
3549
	 *
3550
	 *    @param    string	$selected          	Id contact pre-selectionn
3551
	 *    @param    int		$socid				Id of company
3552
	 *    @param    string	$htmlname          	Name of HTML field
3553
	 *    @param    int		$showempty         	Add an empty field
3554
	 *    @return	integer|null
3555
	 */
3556
	public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3557
	{
3558
		// phpcs:enable
3559
		// looking for users
3560
		$sql = "SELECT a.rowid, a.label";
3561
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a";
3562
		$sql .= " WHERE a.fk_soc = ".((int) $socid);
3563
		$sql .= " ORDER BY a.label ASC";
3564
3565
		dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3566
		$resql = $this->db->query($sql);
3567
		if ($resql) {
3568
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3569
			if ($showempty) {
3570
				print '<option value="0">&nbsp;</option>';
3571
			}
3572
			$num = $this->db->num_rows($resql);
3573
			$i = 0;
3574
			if ($num) {
3575
				while ($i < $num) {
3576
					$obj = $this->db->fetch_object($resql);
3577
3578
					if ($selected && $selected == $obj->rowid) {
3579
						print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3580
					} else {
3581
						print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3582
					}
3583
					$i++;
3584
				}
3585
			}
3586
			print '</select>';
3587
			return $num;
3588
		} else {
3589
			dol_print_error($this->db);
3590
		}
3591
	}
3592
3593
3594
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3595
	/**
3596
	 *      Load into cache list of payment terms
3597
	 *
3598
	 *      @return     int             Nb of lines loaded, <0 if KO
3599
	 */
3600
	public function load_cache_conditions_paiements()
3601
	{
3602
		// phpcs:enable
3603
		global $langs;
3604
3605
		$num = count($this->cache_conditions_paiements);
3606
		if ($num > 0) {
3607
			return 0; // Cache already loaded
3608
		}
3609
3610
		dol_syslog(__METHOD__, LOG_DEBUG);
3611
3612
		$sql = "SELECT rowid, code, libelle as label";
3613
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term';
3614
		$sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3615
		$sql .= " AND active > 0";
3616
		$sql .= " ORDER BY sortorder";
3617
3618
		$resql = $this->db->query($sql);
3619
		if ($resql) {
3620
			$num = $this->db->num_rows($resql);
3621
			$i = 0;
3622
			while ($i < $num) {
3623
				$obj = $this->db->fetch_object($resql);
3624
3625
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3626
				$label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3627
				$this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3628
				$this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3629
				$i++;
3630
			}
3631
3632
			//$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1);		// We use the field sortorder of table
3633
3634
			return $num;
3635
		} else {
3636
			dol_print_error($this->db);
3637
			return -1;
3638
		}
3639
	}
3640
3641
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3642
	/**
3643
	 *      Charge dans cache la liste des délais de livraison possibles
3644
	 *
3645
	 *      @return     int             Nb of lines loaded, <0 if KO
3646
	 */
3647
	public function load_cache_availability()
3648
	{
3649
		// phpcs:enable
3650
		global $langs;
3651
3652
		$num = count($this->cache_availability);
3653
		if ($num > 0) {
3654
			return 0; // Cache already loaded
3655
		}
3656
3657
		dol_syslog(__METHOD__, LOG_DEBUG);
3658
3659
		$langs->load('propal');
3660
3661
		$sql = "SELECT rowid, code, label, position";
3662
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_availability';
3663
		$sql .= " WHERE active > 0";
3664
3665
		$resql = $this->db->query($sql);
3666
		if ($resql) {
3667
			$num = $this->db->num_rows($resql);
3668
			$i = 0;
3669
			while ($i < $num) {
3670
				$obj = $this->db->fetch_object($resql);
3671
3672
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3673
				$label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3674
				$this->cache_availability[$obj->rowid]['code'] = $obj->code;
3675
				$this->cache_availability[$obj->rowid]['label'] = $label;
3676
				$this->cache_availability[$obj->rowid]['position'] = $obj->position;
3677
				$i++;
3678
			}
3679
3680
			$this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3681
3682
			return $num;
3683
		} else {
3684
			dol_print_error($this->db);
3685
			return -1;
3686
		}
3687
	}
3688
3689
	/**
3690
	 *      Retourne la liste des types de delais de livraison possibles
3691
	 *
3692
	 *      @param	int		$selected       Id du type de delais pre-selectionne
3693
	 *      @param  string	$htmlname       Nom de la zone select
3694
	 *      @param  string	$filtertype     To add a filter
3695
	 *		@param	int		$addempty		Add empty entry
3696
	 * 		@param	string	$morecss		More CSS
3697
	 *		@return	void
3698
	 */
3699
	public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3700
	{
3701
		global $langs, $user;
3702
3703
		$this->load_cache_availability();
3704
3705
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3706
3707
		print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3708
		if ($addempty) {
3709
			print '<option value="0">&nbsp;</option>';
3710
		}
3711
		foreach ($this->cache_availability as $id => $arrayavailability) {
3712
			if ($selected == $id) {
3713
				print '<option value="'.$id.'" selected>';
3714
			} else {
3715
				print '<option value="'.$id.'">';
3716
			}
3717
			print dol_escape_htmltag($arrayavailability['label']);
3718
			print '</option>';
3719
		}
3720
		print '</select>';
3721
		if ($user->admin) {
3722
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3723
		}
3724
		print ajax_combobox($htmlname);
3725
	}
3726
3727
	/**
3728
	 *      Load into cache cache_demand_reason, array of input reasons
3729
	 *
3730
	 *      @return     int             Nb of lines loaded, <0 if KO
3731
	 */
3732
	public function loadCacheInputReason()
3733
	{
3734
		global $langs;
3735
3736
		$num = count($this->cache_demand_reason);
3737
		if ($num > 0) {
3738
			return 0; // Cache already loaded
3739
		}
3740
3741
		$sql = "SELECT rowid, code, label";
3742
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason';
3743
		$sql .= " WHERE active > 0";
3744
3745
		$resql = $this->db->query($sql);
3746
		if ($resql) {
3747
			$num = $this->db->num_rows($resql);
3748
			$i = 0;
3749
			$tmparray = array();
3750
			while ($i < $num) {
3751
				$obj = $this->db->fetch_object($resql);
3752
3753
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3754
				$label = ($obj->label != '-' ? $obj->label : '');
3755
				if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
3756
					$label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3757
				}
3758
				if ($langs->trans($obj->code) != $obj->code) {
3759
					$label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3760
				}
3761
3762
				$tmparray[$obj->rowid]['id']   = $obj->rowid;
3763
				$tmparray[$obj->rowid]['code'] = $obj->code;
3764
				$tmparray[$obj->rowid]['label'] = $label;
3765
				$i++;
3766
			}
3767
3768
			$this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
3769
3770
			unset($tmparray);
3771
			return $num;
3772
		} else {
3773
			dol_print_error($this->db);
3774
			return -1;
3775
		}
3776
	}
3777
3778
	/**
3779
	 *	Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
3780
	 *  List found into table c_input_reason loaded by loadCacheInputReason
3781
	 *
3782
	 *  @param	int		$selected        Id or code of type origin to select by default
3783
	 *  @param  string	$htmlname        Nom de la zone select
3784
	 *  @param  string	$exclude         To exclude a code value (Example: SRC_PROP)
3785
	 *	@param	int		$addempty		 Add an empty entry
3786
	 *  @param  string	$morecss		 Add more css to the HTML select component
3787
	 *  @param	int		$notooltip		 Do not show the tooltip for admin
3788
	 *	@return	void
3789
	 */
3790
	public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
3791
	{
3792
		global $langs, $user;
3793
3794
		$this->loadCacheInputReason();
3795
3796
		print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3797
		if ($addempty) {
3798
			print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
3799
		}
3800
		foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
3801
			if ($arraydemandreason['code'] == $exclude) {
3802
				continue;
3803
			}
3804
3805
			if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
3806
				print '<option value="'.$arraydemandreason['id'].'" selected>';
3807
			} else {
3808
				print '<option value="'.$arraydemandreason['id'].'">';
3809
			}
3810
			$label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
3811
			print $langs->trans($label);
3812
			print '</option>';
3813
		}
3814
		print '</select>';
3815
		if ($user->admin && empty($notooltip)) {
3816
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3817
		}
3818
		print ajax_combobox('select_'.$htmlname);
3819
	}
3820
3821
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3822
	/**
3823
	 *      Charge dans cache la liste des types de paiements possibles
3824
	 *
3825
	 *      @return     int                 Nb of lines loaded, <0 if KO
3826
	 */
3827
	public function load_cache_types_paiements()
3828
	{
3829
		// phpcs:enable
3830
		global $langs;
3831
3832
		$num = count($this->cache_types_paiements);
3833
		if ($num > 0) {
3834
			return $num; // Cache already loaded
3835
		}
3836
3837
		dol_syslog(__METHOD__, LOG_DEBUG);
3838
3839
		$this->cache_types_paiements = array();
3840
3841
		$sql = "SELECT id, code, libelle as label, type, active";
3842
		$sql .= " FROM ".MAIN_DB_PREFIX."c_paiement";
3843
		$sql .= " WHERE entity IN (".getEntity('c_paiement').")";
3844
3845
		$resql = $this->db->query($sql);
3846
		if ($resql) {
3847
			$num = $this->db->num_rows($resql);
3848
			$i = 0;
3849
			while ($i < $num) {
3850
				$obj = $this->db->fetch_object($resql);
3851
3852
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3853
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3854
				$this->cache_types_paiements[$obj->id]['id'] = $obj->id;
3855
				$this->cache_types_paiements[$obj->id]['code'] = $obj->code;
3856
				$this->cache_types_paiements[$obj->id]['label'] = $label;
3857
				$this->cache_types_paiements[$obj->id]['type'] = $obj->type;
3858
				$this->cache_types_paiements[$obj->id]['active'] = $obj->active;
3859
				$i++;
3860
			}
3861
3862
			$this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
3863
3864
			return $num;
3865
		} else {
3866
			dol_print_error($this->db);
3867
			return -1;
3868
		}
3869
	}
3870
3871
3872
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3873
	/**
3874
	 *      Return list of payment modes.
3875
	 *      Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want.
3876
	 *      See instead to force the default value by the caller.
3877
	 *
3878
	 *      @param	int		$selected		Id of payment term to preselect by default
3879
	 *      @param	string	$htmlname		Nom de la zone select
3880
	 *      @param	int		$filtertype		Not used
3881
	 *		@param	int		$addempty		Add an empty entry
3882
	 * 		@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
3883
	 * 		@param	string	$morecss			Add more CSS on select tag
3884
	 *		@return	void
3885
	 */
3886
	public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '')
3887
	{
3888
		// phpcs:enable
3889
		global $langs, $user, $conf;
3890
3891
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3892
3893
		$this->load_cache_conditions_paiements();
3894
3895
		// Set default value if not already set by caller
3896
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
3897
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
3898
		}
3899
3900
		print '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3901
		if ($addempty) {
3902
			print '<option value="0">&nbsp;</option>';
3903
		}
3904
		foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
3905
			if ($selected == $id) {
3906
				print '<option value="'.$id.'" selected>';
3907
			} else {
3908
				print '<option value="'.$id.'">';
3909
			}
3910
			print $arrayconditions['label'];
3911
			print '</option>';
3912
		}
3913
		print '</select>';
3914
		if ($user->admin && empty($noinfoadmin)) {
3915
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3916
		}
3917
		print ajax_combobox($htmlname);
3918
	}
3919
3920
3921
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3922
	/**
3923
	 *      Return list of payment methods
3924
	 *      Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want.
3925
	 *
3926
	 *      @param	string	$selected       Id or code or preselected payment mode
3927
	 *      @param  string	$htmlname       Name of select field
3928
	 *      @param  string	$filtertype     To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
3929
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
3930
	 *      @param  int		$empty			1=can be empty, 0 otherwise
3931
	 * 		@param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
3932
	 *      @param  int		$maxlength      Max length of label
3933
	 *      @param  int     $active         Active or not, -1 = all
3934
	 *      @param  string  $morecss        Add more CSS on select tag
3935
	 *      @param	int		$nooutput		1=Return string, do not send to output
3936
	 * 		@return	void
3937
	 */
3938
	public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
3939
	{
3940
		// phpcs:enable
3941
		global $langs, $user, $conf;
3942
3943
		$out = '';
3944
3945
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
3946
3947
		$filterarray = array();
3948
		if ($filtertype == 'CRDT') {
3949
			$filterarray = array(0, 2, 3);
3950
		} elseif ($filtertype == 'DBIT') {
3951
			$filterarray = array(1, 2, 3);
3952
		} elseif ($filtertype != '' && $filtertype != '-1') {
3953
			$filterarray = explode(',', $filtertype);
3954
		}
3955
3956
		$this->load_cache_types_paiements();
3957
3958
		// Set default value if not already set by caller
3959
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
3960
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
3961
		}
3962
3963
		$out .= '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3964
		if ($empty) {
3965
			$out .= '<option value="">&nbsp;</option>';
3966
		}
3967
		foreach ($this->cache_types_paiements as $id => $arraytypes) {
3968
			// If not good status
3969
			if ($active >= 0 && $arraytypes['active'] != $active) {
3970
				continue;
3971
			}
3972
3973
			// On passe si on a demande de filtrer sur des modes de paiments particuliers
3974
			if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
3975
				continue;
3976
			}
3977
3978
			// We discard empty line if showempty is on because an empty line has already been output.
3979
			if ($empty && empty($arraytypes['code'])) {
3980
				continue;
3981
			}
3982
3983
			if ($format == 0) {
3984
				$out .= '<option value="'.$id.'"';
3985
			} elseif ($format == 1) {
3986
				$out .= '<option value="'.$arraytypes['code'].'"';
3987
			} elseif ($format == 2) {
3988
				$out .= '<option value="'.$arraytypes['code'].'"';
3989
			} elseif ($format == 3) {
3990
				$out .= '<option value="'.$id.'"';
3991
			}
3992
			// Print attribute selected or not
3993
			if ($format == 1 || $format == 2) {
3994
				if ($selected == $arraytypes['code']) {
3995
					$out .= ' selected';
3996
				}
3997
			} else {
3998
				if ($selected == $id) {
3999
					$out .= ' selected';
4000
				}
4001
			}
4002
			$out .= '>';
4003
			if ($format == 0) {
4004
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4005
			} elseif ($format == 1) {
4006
				$value = $arraytypes['code'];
4007
			} elseif ($format == 2) {
4008
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4009
			} elseif ($format == 3) {
4010
				$value = $arraytypes['code'];
4011
			}
4012
			$out .= $value ? $value : '&nbsp;';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
4013
			$out .= '</option>';
4014
		}
4015
		$out .= '</select>';
4016
		if ($user->admin && !$noadmininfo) {
4017
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4018
		}
4019
		$out .= ajax_combobox('select'.$htmlname);
4020
4021
		if (empty($nooutput)) {
4022
			print $out;
4023
		} else {
4024
			return $out;
4025
		}
4026
	}
4027
4028
4029
	/**
4030
	 *  Selection HT or TTC
4031
	 *
4032
	 *  @param	string	$selected       Id pre-selectionne
4033
	 *  @param  string	$htmlname       Nom de la zone select
4034
	 *  @param	string	$addjscombo		Add js combo
4035
	 * 	@return	string					Code of HTML select to chose tax or not
4036
	 */
4037
	public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4038
	{
4039
		global $langs;
4040
4041
		$return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4042
		$options = array(
4043
			'HT'=>$langs->trans("HT"),
4044
			'TTC'=>$langs->trans("TTC")
4045
		);
4046
		foreach ($options as $id => $value) {
4047
			if ($selected == $id) {
4048
				$return .= '<option value="'.$id.'" selected>'.$value;
4049
			} else {
4050
				$return .= '<option value="'.$id.'">'.$value;
4051
			}
4052
			$return .= '</option>';
4053
		}
4054
		$return .= '</select>';
4055
		if ($addjscombo) {
4056
			$return .= ajax_combobox('select_'.$htmlname);
4057
		}
4058
4059
		return $return;
4060
	}
4061
4062
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4063
	/**
4064
	 *      Load in cache list of transport mode
4065
	 *
4066
	 *      @return     int                 Nb of lines loaded, <0 if KO
4067
	 */
4068
	public function load_cache_transport_mode()
4069
	{
4070
		// phpcs:enable
4071
		global $langs;
4072
4073
		$num = count($this->cache_transport_mode);
4074
		if ($num > 0) {
4075
			return $num; // Cache already loaded
4076
		}
4077
4078
		dol_syslog(__METHOD__, LOG_DEBUG);
4079
4080
		$this->cache_transport_mode = array();
4081
4082
		$sql = "SELECT rowid, code, label, active";
4083
		$sql .= " FROM ".MAIN_DB_PREFIX."c_transport_mode";
4084
		$sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4085
4086
		$resql = $this->db->query($sql);
4087
		if ($resql) {
4088
			$num = $this->db->num_rows($resql);
4089
			$i = 0;
4090
			while ($i < $num) {
4091
				$obj = $this->db->fetch_object($resql);
4092
4093
				// If traduction exist, we use it else we take the default label
4094
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4095
				$this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4096
				$this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4097
				$this->cache_transport_mode[$obj->rowid]['label'] = $label;
4098
				$this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4099
				$i++;
4100
			}
4101
4102
			$this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4103
4104
			return $num;
4105
		} else {
4106
			dol_print_error($this->db);
4107
			return -1;
4108
		}
4109
	}
4110
4111
	/**
4112
	 *      Return list of transport mode for intracomm report
4113
	 *
4114
	 *      @param	string	$selected       Id of the transport mode pre-selected
4115
	 *      @param  string	$htmlname       Name of the select field
4116
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
4117
	 *      @param  int		$empty			1=can be empty, 0 else
4118
	 *      @param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
4119
	 *      @param  int		$maxlength      Max length of label
4120
	 *      @param  int     $active         Active or not, -1 = all
4121
	 *      @param  string  $morecss        Add more CSS on select tag
4122
	 * 		@return	void
4123
	 */
4124
	public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4125
	{
4126
		global $langs, $user;
4127
4128
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4129
4130
		$this->load_cache_transport_mode();
4131
4132
		print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4133
		if ($empty) {
4134
			print '<option value="">&nbsp;</option>';
4135
		}
4136
		foreach ($this->cache_transport_mode as $id => $arraytypes) {
4137
			// If not good status
4138
			if ($active >= 0 && $arraytypes['active'] != $active) {
4139
				continue;
4140
			}
4141
4142
			// We discard empty line if showempty is on because an empty line has already been output.
4143
			if ($empty && empty($arraytypes['code'])) {
4144
				continue;
4145
			}
4146
4147
			if ($format == 0) {
4148
				print '<option value="'.$id.'"';
4149
			} elseif ($format == 1) {
4150
				print '<option value="'.$arraytypes['code'].'"';
4151
			} elseif ($format == 2) {
4152
				print '<option value="'.$arraytypes['code'].'"';
4153
			} elseif ($format == 3) {
4154
				print '<option value="'.$id.'"';
4155
			}
4156
			// If text is selected, we compare with code, else with id
4157
			if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4158
				print ' selected';
4159
			} elseif ($selected == $id) {
4160
				print ' selected';
4161
			}
4162
			print '>';
4163
			if ($format == 0) {
4164
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4165
			} elseif ($format == 1) {
4166
				$value = $arraytypes['code'];
4167
			} elseif ($format == 2) {
4168
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4169
			} elseif ($format == 3) {
4170
				$value = $arraytypes['code'];
4171
			}
4172
			print $value ? $value : '&nbsp;';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
4173
			print '</option>';
4174
		}
4175
		print '</select>';
4176
		if ($user->admin && !$noadmininfo) {
4177
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4178
		}
4179
	}
4180
4181
	/**
4182
	 *  Return a HTML select list of shipping mode
4183
	 *
4184
	 *  @param	string	$selected           Id shipping mode pre-selected
4185
	 *  @param  string	$htmlname           Name of select zone
4186
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4187
	 *  @param  int		$useempty           1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4188
	 *  @param  string	$moreattrib         To add more attribute on select
4189
	 *	@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
4190
	 *  @param	string	$morecss			More CSS
4191
	 * 	@return	void
4192
	 */
4193
	public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4194
	{
4195
		global $langs, $conf, $user;
4196
4197
		$langs->load("admin");
4198
		$langs->load("deliveries");
4199
4200
		$sql = "SELECT rowid, code, libelle as label";
4201
		$sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode";
4202
		$sql .= " WHERE active > 0";
4203
		if ($filtre) {
4204
			$sql .= " AND ".$filtre;
4205
		}
4206
		$sql .= " ORDER BY libelle ASC";
4207
4208
		dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4209
		$result = $this->db->query($sql);
4210
		if ($result) {
4211
			$num = $this->db->num_rows($result);
4212
			$i = 0;
4213
			if ($num) {
4214
				print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4215
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4216
					print '<option value="-1">&nbsp;</option>';
4217
				}
4218
				while ($i < $num) {
4219
					$obj = $this->db->fetch_object($result);
4220
					if ($selected == $obj->rowid) {
4221
						print '<option value="'.$obj->rowid.'" selected>';
4222
					} else {
4223
						print '<option value="'.$obj->rowid.'">';
4224
					}
4225
					print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4226
					print '</option>';
4227
					$i++;
4228
				}
4229
				print "</select>";
4230
				if ($user->admin  && empty($noinfoadmin)) {
4231
					print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4232
				}
4233
4234
				print ajax_combobox('select'.$htmlname);
4235
			} else {
4236
				print $langs->trans("NoShippingMethodDefined");
4237
			}
4238
		} else {
4239
			dol_print_error($this->db);
4240
		}
4241
	}
4242
4243
	/**
4244
	 *    Display form to select shipping mode
4245
	 *
4246
	 *    @param	string	$page        Page
4247
	 *    @param    int		$selected    Id of shipping mode
4248
	 *    @param    string	$htmlname    Name of select html field
4249
	 *    @param    int		$addempty    1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4250
	 *    @return	void
4251
	 */
4252
	public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4253
	{
4254
		global $langs, $db;
4255
4256
		$langs->load("deliveries");
4257
4258
		if ($htmlname != "none") {
4259
			print '<form method="POST" action="'.$page.'">';
4260
			print '<input type="hidden" name="action" value="setshippingmethod">';
4261
			print '<input type="hidden" name="token" value="'.newToken().'">';
4262
			$this->selectShippingMethod($selected, $htmlname, '', $addempty);
4263
			print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4264
			print '</form>';
4265
		} else {
4266
			if ($selected) {
4267
				$code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code');
4268
				print $langs->trans("SendingMethod".strtoupper($code));
4269
			} else {
4270
				print "&nbsp;";
4271
			}
4272
		}
4273
	}
4274
4275
	/**
4276
	 * Creates HTML last in cycle situation invoices selector
4277
	 *
4278
	 * @param     string  $selected   		Preselected ID
4279
	 * @param     int     $socid      		Company ID
4280
	 *
4281
	 * @return    string                     HTML select
4282
	 */
4283
	public function selectSituationInvoices($selected = '', $socid = 0)
4284
	{
4285
		global $langs;
4286
4287
		$langs->load('bills');
4288
4289
		$opt = '<option value ="" selected></option>';
4290
		$sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc';
4291
		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture';
4292
		$sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4293
		$sql .= ' AND situation_counter >= 1';
4294
		$sql .= ' AND fk_soc = '.(int) $socid;
4295
		$sql .= ' AND type <> 2';
4296
		$sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4297
		$resql = $this->db->query($sql);
4298
4299
		if ($resql && $this->db->num_rows($resql) > 0) {
4300
			// Last seen cycle
4301
			$ref = 0;
4302
			while ($obj = $this->db->fetch_object($resql)) {
4303
				//Same cycle ?
4304
				if ($obj->situation_cycle_ref != $ref) {
4305
					// Just seen this cycle
4306
					$ref = $obj->situation_cycle_ref;
4307
					//not final ?
4308
					if ($obj->situation_final != 1) {
4309
						//Not prov?
4310
						if (substr($obj->ref, 1, 4) != 'PROV') {
4311
							if ($selected == $obj->rowid) {
4312
								$opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4313
							} else {
4314
								$opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4315
							}
4316
						}
4317
					}
4318
				}
4319
			}
4320
		} else {
4321
				dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4322
		}
4323
		if ($opt == '<option value ="" selected></option>') {
4324
			$opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4325
		}
4326
		return $opt;
4327
	}
4328
4329
	/**
4330
	 *      Creates HTML units selector (code => label)
4331
	 *
4332
	 *      @param	string	$selected       Preselected Unit ID
4333
	 *      @param  string	$htmlname       Select name
4334
	 *      @param	int		$showempty		Add a nempty line
4335
	 *      @param  string  $unit_type      Restrict to one given unit type
4336
	 * 		@return	string                  HTML select
4337
	 */
4338
	public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4339
	{
4340
		global $langs;
4341
4342
		$langs->load('products');
4343
4344
		$return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4345
4346
		$sql = 'SELECT rowid, label, code from '.MAIN_DB_PREFIX.'c_units';
4347
		$sql .= ' WHERE active > 0';
4348
		if (!empty($unit_type)) {
4349
			$sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4350
		}
4351
4352
		$resql = $this->db->query($sql);
4353
		if ($resql && $this->db->num_rows($resql) > 0) {
4354
			if ($showempty) {
4355
				$return .= '<option value="none"></option>';
4356
			}
4357
4358
			while ($res = $this->db->fetch_object($resql)) {
4359
				$unitLabel = $res->label;
4360
				if (!empty($langs->tab_translate['unit'.$res->code])) {	// check if Translation is available before
4361
					$unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4362
				}
4363
4364
				if ($selected == $res->rowid) {
4365
					$return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4366
				} else {
4367
					$return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4368
				}
4369
			}
4370
			$return .= '</select>';
4371
		}
4372
		return $return;
4373
	}
4374
4375
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4376
	/**
4377
	 *  Return a HTML select list of bank accounts
4378
	 *
4379
	 *  @param	string	$selected           Id account pre-selected
4380
	 *  @param  string	$htmlname           Name of select zone
4381
	 *  @param  int		$status             Status of searched accounts (0=open, 1=closed, 2=both)
4382
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4383
	 *  @param  int		$useempty           1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4384
	 *  @param  string	$moreattrib         To add more attribute on select
4385
	 *  @param	int		$showcurrency		Show currency in label
4386
	 *  @param	string	$morecss			More CSS
4387
	 *  @param	int		$nooutput			1=Return string, do not send to output
4388
	 * 	@return	int							<0 if error, Num of bank account found if OK (0, 1, 2, ...)
4389
	 */
4390
	public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4391
	{
4392
		// phpcs:enable
4393
		global $langs, $conf;
4394
4395
		$out = '';
4396
4397
		$langs->load("admin");
4398
		$num = 0;
4399
4400
		$sql = "SELECT rowid, label, bank, clos as status, currency_code";
4401
		$sql .= " FROM ".MAIN_DB_PREFIX."bank_account";
4402
		$sql .= " WHERE entity IN (".getEntity('bank_account').")";
4403
		if ($status != 2) {
4404
			$sql .= " AND clos = ".(int) $status;
4405
		}
4406
		if ($filtre) {
4407
			$sql .= " AND ".$filtre;
4408
		}
4409
		$sql .= " ORDER BY label";
4410
4411
		dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4412
		$result = $this->db->query($sql);
4413
		if ($result) {
4414
			$num = $this->db->num_rows($result);
4415
			$i = 0;
4416
			if ($num) {
4417
				$out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4418
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4419
					$out .= '<option value="-1">&nbsp;</option>';
4420
				}
4421
4422
				while ($i < $num) {
4423
					$obj = $this->db->fetch_object($result);
4424
					if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4425
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4426
					} else {
4427
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4428
					}
4429
					$out .= trim($obj->label);
4430
					if ($showcurrency) {
4431
						$out .= ' ('.$obj->currency_code.')';
4432
					}
4433
					if ($status == 2 && $obj->status == 1) {
4434
						$out .= ' ('.$langs->trans("Closed").')';
4435
					}
4436
					$out .= '</option>';
4437
					$i++;
4438
				}
4439
				$out .= "</select>";
4440
				$out .= ajax_combobox('select'.$htmlname);
4441
			} else {
4442
				if ($status == 0) {
4443
					$out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4444
				} else {
4445
					$out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4446
				}
4447
			}
4448
		} else {
4449
			dol_print_error($this->db);
4450
		}
4451
4452
		// Output or return
4453
		if (empty($nooutput)) {
4454
			print $out;
4455
		} else {
4456
			return $out;
4457
		}
4458
4459
		return $num;
4460
	}
4461
4462
	/**
4463
	 *  Return a HTML select list of establishment
4464
	 *
4465
	 *  @param	string	$selected           Id establishment pre-selected
4466
	 *  @param  string	$htmlname           Name of select zone
4467
	 *  @param  int		$status             Status of searched establishment (0=open, 1=closed, 2=both)
4468
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4469
	 *  @param  int		$useempty           1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4470
	 *  @param  string	$moreattrib         To add more attribute on select
4471
	 * 	@return	int							<0 if error, Num of establishment found if OK (0, 1, 2, ...)
4472
	 */
4473
	public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4474
	{
4475
		global $langs, $conf;
4476
4477
		$langs->load("admin");
4478
		$num = 0;
4479
4480
		$sql = "SELECT rowid, name, fk_country, status, entity";
4481
		$sql .= " FROM ".MAIN_DB_PREFIX."establishment";
4482
		$sql .= " WHERE 1=1";
4483
		if ($status != 2) {
4484
			$sql .= " AND status = ".(int) $status;
4485
		}
4486
		if ($filtre) {
4487
			$sql .= " AND ".$filtre;
4488
		}
4489
		$sql .= " ORDER BY name";
4490
4491
		dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4492
		$result = $this->db->query($sql);
4493
		if ($result) {
4494
			$num = $this->db->num_rows($result);
4495
			$i = 0;
4496
			if ($num) {
4497
				print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4498
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4499
					print '<option value="-1">&nbsp;</option>';
4500
				}
4501
4502
				while ($i < $num) {
4503
					$obj = $this->db->fetch_object($result);
4504
					if ($selected == $obj->rowid) {
4505
						print '<option value="'.$obj->rowid.'" selected>';
4506
					} else {
4507
						print '<option value="'.$obj->rowid.'">';
4508
					}
4509
					print trim($obj->name);
4510
					if ($status == 2 && $obj->status == 1) {
4511
						print ' ('.$langs->trans("Closed").')';
4512
					}
4513
					print '</option>';
4514
					$i++;
4515
				}
4516
				print "</select>";
4517
			} else {
4518
				if ($status == 0) {
4519
					print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4520
				} else {
4521
					print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4522
				}
4523
			}
4524
		} else {
4525
			dol_print_error($this->db);
4526
		}
4527
	}
4528
4529
	/**
4530
	 *    Display form to select bank account
4531
	 *
4532
	 *    @param	string	$page        Page
4533
	 *    @param    int		$selected    Id of bank account
4534
	 *    @param    string	$htmlname    Name of select html field
4535
	 *    @param    int		$addempty    1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4536
	 *    @return	void
4537
	 */
4538
	public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4539
	{
4540
		global $langs;
4541
		if ($htmlname != "none") {
4542
			print '<form method="POST" action="'.$page.'">';
4543
			print '<input type="hidden" name="action" value="setbankaccount">';
4544
			print '<input type="hidden" name="token" value="'.newToken().'">';
4545
			print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4546
			$nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4547
			if ($nbaccountfound > 0) {
4548
				print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4549
			}
4550
			print '</form>';
4551
		} else {
4552
			$langs->load('banks');
4553
4554
			if ($selected) {
4555
				require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4556
				$bankstatic = new Account($this->db);
4557
				$result = $bankstatic->fetch($selected);
4558
				if ($result) {
4559
					print $bankstatic->getNomUrl(1);
4560
				}
4561
			} else {
4562
				print "&nbsp;";
4563
			}
4564
		}
4565
	}
4566
4567
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4568
	/**
4569
	 *    Return list of categories having choosed type
4570
	 *
4571
	 *    @param	string|int	            $type				Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
4572
	 *    @param    string		            $selected    		Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1.
4573
	 *    @param    string		            $htmlname			HTML field name
4574
	 *    @param    int			            $maxlength      	Maximum length for labels
4575
	 *    @param    int|string|array    	$markafterid        Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id.
4576
	 *                                                          $markafterid can be an :
4577
	 *                                                          - int (id of category)
4578
	 *                                                          - string (categories ids seprated by comma)
4579
	 *                                                          - array (list of categories ids)
4580
	 *    @param	int			            $outputmode			0=HTML select string, 1=Array
4581
	 *    @param	int			            $include			[=0] Removed or 1=Keep only
4582
	 *    @param	string					$morecss			More CSS
4583
	 *    @return	string
4584
	 *    @see select_categories()
4585
	 */
4586
	public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4587
	{
4588
		// phpcs:enable
4589
		global $conf, $langs;
4590
		$langs->load("categories");
4591
4592
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4593
4594
		// For backward compatibility
4595
		if (is_numeric($type)) {
4596
			dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4597
		}
4598
4599
		if ($type === Categorie::TYPE_BANK_LINE) {
4600
			// TODO Move this into common category feature
4601
			$cate_arbo = array();
4602
			$sql = "SELECT c.label, c.rowid";
4603
			$sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c";
4604
			$sql .= " WHERE entity = ".$conf->entity;
4605
			$sql .= " ORDER BY c.label";
4606
			$result = $this->db->query($sql);
4607
			if ($result) {
4608
				$num = $this->db->num_rows($result);
4609
				$i = 0;
4610
				while ($i < $num) {
4611
					$objp = $this->db->fetch_object($result);
4612
					if ($objp) {
4613
						$cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label);
4614
					}
4615
					$i++;
4616
				}
4617
				$this->db->free($result);
4618
			} else {
4619
				dol_print_error($this->db);
4620
			}
4621
		} else {
4622
			$cat = new Categorie($this->db);
4623
			$cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4624
		}
4625
4626
		$output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4627
		$outarray = array();
4628
		if (is_array($cate_arbo)) {
4629
			if (!count($cate_arbo)) {
4630
				$output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4631
			} else {
4632
				$output .= '<option value="-1">&nbsp;</option>';
4633
				foreach ($cate_arbo as $key => $value) {
4634
					if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4635
						$add = 'selected ';
4636
					} else {
4637
						$add = '';
4638
					}
4639
					$output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'">'.dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle').'</option>';
4640
4641
					$outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4642
				}
4643
			}
4644
		}
4645
		$output .= '</select>';
4646
		$output .= "\n";
4647
4648
		if ($outputmode) {
4649
			return $outarray;
4650
		}
4651
		return $output;
4652
	}
4653
4654
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4655
	/**
4656
	 *     Show a confirmation HTML form or AJAX popup
4657
	 *
4658
	 *     @param	string		$page        	   	Url of page to call if confirmation is OK
4659
	 *     @param	string		$title       	   	Title
4660
	 *     @param	string		$question    	   	Question
4661
	 *     @param 	string		$action      	   	Action
4662
	 *	   @param	array		$formquestion	   	An array with forms complementary inputs
4663
	 * 	   @param	string		$selectedchoice		"" or "no" or "yes"
4664
	 * 	   @param	int			$useajax		   	0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=preoutput confirm box with div id=dialog-confirm-xxx
4665
	 *     @param	int			$height          	Force height of box
4666
	 *     @param	int			$width				Force width of box
4667
	 *     @return 	void
4668
	 *     @deprecated
4669
	 *     @see formconfirm()
4670
	 */
4671
	public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4672
	{
4673
		// phpcs:enable
4674
		dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4675
		print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4676
	}
4677
4678
	/**
4679
	 *     Show a confirmation HTML form or AJAX popup.
4680
	 *     Easiest way to use this is with useajax=1.
4681
	 *     If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters)
4682
	 *     just after calling this method. For example:
4683
	 *       print '<script type="text/javascript">'."\n";
4684
	 *       print 'jQuery(document).ready(function() {'."\n";
4685
	 *       print 'jQuery(".xxxlink").click(function(e) { jQuery("#aparamid").val(jQuery(this).attr("rel")); jQuery("#dialog-confirm-xxx").dialog("open"); return false; });'."\n";
4686
	 *       print '});'."\n";
4687
	 *       print '</script>'."\n";
4688
	 *
4689
	 *     @param  	string			$page        	   	Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated)
4690
	 *     @param	string			$title       	   	Title
4691
	 *     @param	string			$question    	   	Question
4692
	 *     @param 	string			$action      	   	Action
4693
	 *	   @param  	array|string	$formquestion	   	An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>))
4694
	 *													type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', 'other' or 'onecolumn'...
4695
	 * 	   @param  	string			$selectedchoice  	'' or 'no', or 'yes' or '1' or '0'
4696
	 * 	   @param  	int|string		$useajax		   	0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx
4697
	 *     @param  	int|string		$height          	Force height of box (0 = auto)
4698
	 *     @param	int				$width				Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones.
4699
	 *     @param	int				$disableformtag		1=Disable form tag. Can be used if we are already inside a <form> section.
4700
	 *     @return 	string      		    			HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form
4701
	 */
4702
	public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0)
4703
	{
4704
		global $langs, $conf;
4705
4706
		$more = '<!-- formconfirm before calling page='.dol_escape_htmltag($page).' -->';
4707
		$formconfirm = '';
4708
		$inputok = array();
4709
		$inputko = array();
4710
4711
		// Clean parameters
4712
		$newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
4713
		if ($conf->browser->layout == 'phone') {
4714
			$width = '95%';
4715
		}
4716
4717
		// Set height automatically if not defined
4718
		if (empty($height)) {
4719
			$height = 220;
4720
			if (is_array($formquestion) && count($formquestion) > 2) {
4721
				$height += ((count($formquestion) - 2) * 24);
4722
			}
4723
		}
4724
4725
		if (is_array($formquestion) && !empty($formquestion)) {
4726
			// First add hidden fields and value
4727
			foreach ($formquestion as $key => $input) {
4728
				if (is_array($input) && !empty($input)) {
4729
					if ($input['type'] == 'hidden') {
4730
						$more .= '<input type="hidden" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'" value="'.dol_escape_htmltag($input['value']).'">'."\n";
4731
					}
4732
				}
4733
			}
4734
4735
			// Now add questions
4736
			$moreonecolumn = '';
4737
			$more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
4738
			foreach ($formquestion as $key => $input) {
4739
				if (is_array($input) && !empty($input)) {
4740
					$size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : '');	// deprecated. Use morecss instead.
4741
					$moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4742
					$morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4743
4744
					if ($input['type'] == 'text') {
4745
						$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="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4746
					} elseif ($input['type'] == 'password')	{
4747
						$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="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4748
					} elseif ($input['type'] == 'textarea') {
4749
						/*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
4750
						$more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
4751
						$more .= $input['value'];
4752
						$more .= '</textarea>';
4753
						$more .= '</div></div>'."\n";*/
4754
						$moreonecolumn .= '<div class="margintoponly">';
4755
						$moreonecolumn .= $input['label'].'<br>';
4756
						$moreonecolumn .= '<textarea name="'.dol_escape_htmltag($input['name']).'" id="'.dol_escape_htmltag($input['name']).'" class="'.$morecss.'"'.$moreattr.'>';
4757
						$moreonecolumn .= $input['value'];
4758
						$moreonecolumn .= '</textarea>';
4759
						$moreonecolumn .= '</div>';
4760
					} elseif ($input['type'] == 'select') {
4761
						if (empty($morecss)) {
4762
							$morecss = 'minwidth100';
4763
						}
4764
4765
						$show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
4766
						$key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
4767
						$value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
4768
						$translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
4769
						$maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
4770
						$disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
4771
						$sort = isset($input['select_sort']) ? $input['select_sort'] : '';
4772
4773
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4774
						if (!empty($input['label'])) {
4775
							$more .= $input['label'].'</div><div class="tagtd left">';
4776
						}
4777
						$more .= $this->selectarray($input['name'], $input['values'], $input['default'], $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
4778
						$more .= '</div></div>'."\n";
4779
					} elseif ($input['type'] == 'checkbox') {
4780
						$more .= '<div class="tagtr">';
4781
						$more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
4782
						$more .= '<input type="checkbox" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$moreattr;
4783
						if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0') {
4784
							$more .= ' checked';
4785
						}
4786
						if (is_bool($input['value']) && $input['value']) {
4787
							$more .= ' checked';
4788
						}
4789
						if (isset($input['disabled'])) {
4790
							$more .= ' disabled';
4791
						}
4792
						$more .= ' /></div>';
4793
						$more .= '</div>'."\n";
4794
					} elseif ($input['type'] == 'radio') {
4795
						$i = 0;
4796
						foreach ($input['values'] as $selkey => $selval) {
4797
							$more .= '<div class="tagtr">';
4798
							if ($i == 0) {
4799
								$more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
4800
							} else {
4801
								$more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
4802
							}
4803
							$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;
4804
							if ($input['disabled']) {
4805
								$more .= ' disabled';
4806
							}
4807
							if (isset($input['default']) && $input['default'] === $selkey) {
4808
								$more .= ' checked="checked"';
4809
							}
4810
							$more .= ' /> ';
4811
							$more .= '<label for="'.dol_escape_htmltag($input['name'].$selkey).'">'.$selval.'</label>';
4812
							$more .= '</div></div>'."\n";
4813
							$i++;
4814
						}
4815
					} elseif ($input['type'] == 'date') {
4816
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
4817
						$more .= '<div class="tagtd">';
4818
						$addnowlink = (empty($input['datenow']) ? 0 : 1);
4819
						$more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, $addnowlink);
4820
						$more .= '</div></div>'."\n";
4821
						$formquestion[] = array('name'=>$input['name'].'day');
4822
						$formquestion[] = array('name'=>$input['name'].'month');
4823
						$formquestion[] = array('name'=>$input['name'].'year');
4824
						$formquestion[] = array('name'=>$input['name'].'hour');
4825
						$formquestion[] = array('name'=>$input['name'].'min');
4826
					} elseif ($input['type'] == 'other') {
4827
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4828
						if (!empty($input['label'])) {
4829
							$more .= $input['label'].'</div><div class="tagtd">';
4830
						}
4831
						$more .= $input['value'];
4832
						$more .= '</div></div>'."\n";
4833
					} elseif ($input['type'] == 'onecolumn') {
4834
						$moreonecolumn .= '<div class="margintoponly">';
4835
						$moreonecolumn .= $input['value'];
4836
						$moreonecolumn .= '</div>'."\n";
4837
					} elseif ($input['type'] == 'hidden') {
4838
						// Do nothing more, already added by a previous loop
4839
					} else {
4840
						$more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
4841
					}
4842
				}
4843
			}
4844
			$more .= '</div>'."\n";
4845
			$more .= $moreonecolumn;
4846
		}
4847
4848
		// JQUI method dialog is broken with jmobile, we use standard HTML.
4849
		// 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
4850
		// See page product/card.php for example
4851
		if (!empty($conf->dol_use_jmobile)) {
4852
			$useajax = 0;
4853
		}
4854
		if (empty($conf->use_javascript_ajax)) {
4855
			$useajax = 0;
4856
		}
4857
4858
		if ($useajax) {
4859
			$autoOpen = true;
4860
			$dialogconfirm = 'dialog-confirm';
4861
			$button = '';
4862
			if (!is_numeric($useajax)) {
4863
				$button = $useajax;
4864
				$useajax = 1;
4865
				$autoOpen = false;
4866
				$dialogconfirm .= '-'.$button;
4867
			}
4868
			$pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes';
4869
			$pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : '');
4870
4871
			// Add input fields into list of fields to read during submit (inputok and inputko)
4872
			if (is_array($formquestion)) {
4873
				foreach ($formquestion as $key => $input) {
4874
					//print "xx ".$key." rr ".is_array($input)."<br>\n";
4875
					// Add name of fields to propagate with the GET when submitting the form with button OK.
4876
					if (is_array($input) && isset($input['name'])) {
4877
						if (strpos($input['name'], ',') > 0) {
4878
							$inputok = array_merge($inputok, explode(',', $input['name']));
4879
						} else {
4880
							array_push($inputok, $input['name']);
4881
						}
4882
					}
4883
					// Add name of fields to propagate with the GET when submitting the form with button KO.
4884
					if (isset($input['inputko']) && $input['inputko'] == 1) {
4885
						array_push($inputko, $input['name']);
4886
					}
4887
				}
4888
			}
4889
4890
			// Show JQuery confirm box.
4891
			$formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
4892
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4893
				$formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
4894
			}
4895
			if (!empty($more)) {
4896
				$formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
4897
			}
4898
			$formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
4899
			$formconfirm .= '</div>'."\n";
4900
4901
			$formconfirm .= "\n<!-- begin ajax formconfirm page=".$page." -->\n";
4902
			$formconfirm .= '<script type="text/javascript">'."\n";
4903
			$formconfirm .= 'jQuery(document).ready(function() {
4904
            $(function() {
4905
            	$( "#'.$dialogconfirm.'" ).dialog(
4906
            	{
4907
                    autoOpen: '.($autoOpen ? "true" : "false").',';
4908
			if ($newselectedchoice == 'no') {
4909
				$formconfirm .= '
4910
						open: function() {
4911
            				$(this).parent().find("button.ui-button:eq(2)").focus();
4912
						},';
4913
			}
4914
			$formconfirm .= '
4915
                    resizable: false,
4916
                    height: "'.$height.'",
4917
                    width: "'.$width.'",
4918
                    modal: true,
4919
                    closeOnEscape: false,
4920
                    buttons: {
4921
                        "'.dol_escape_js($langs->transnoentities("Yes")).'": function() {
4922
                        	var options = "&token='.urlencode(newToken()).'";
4923
                        	var inputok = '.json_encode($inputok).';	/* List of fields into form */
4924
                         	var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
4925
                         	if (inputok.length>0) {
4926
                         		$.each(inputok, function(i, inputname) {
4927
                         			var more = "";
4928
									var inputvalue;
4929
                         			if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
4930
										inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
4931
									} else {
4932
                         		    	if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4933
                         				inputvalue = $("#" + inputname + more).val();
4934
									}
4935
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4936
									console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
4937
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4938
                         		});
4939
                         	}
4940
                         	var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "") + options;
4941
            				if (pageyes.length > 0) { location.href = urljump; }
4942
                            $(this).dialog("close");
4943
                        },
4944
                        "'.dol_escape_js($langs->transnoentities("No")).'": function() {
4945
                        	var options = "&token='.urlencode(newToken()).'";
4946
                         	var inputko = '.json_encode($inputko).';	/* List of fields into form */
4947
                         	var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
4948
                         	if (inputko.length>0) {
4949
                         		$.each(inputko, function(i, inputname) {
4950
                         			var more = "";
4951
                         			if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4952
                         			var inputvalue = $("#" + inputname + more).val();
4953
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4954
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4955
                         		});
4956
                         	}
4957
                         	var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "") + options;
4958
                         	//alert(urljump);
4959
            				if (pageno.length > 0) { location.href = urljump; }
4960
                            $(this).dialog("close");
4961
                        }
4962
                    }
4963
                }
4964
                );
4965
4966
            	var button = "'.$button.'";
4967
            	if (button.length > 0) {
4968
                	$( "#" + button ).click(function() {
4969
                		$("#'.$dialogconfirm.'").dialog("open");
4970
        			});
4971
                }
4972
            });
4973
            });
4974
            </script>';
4975
			$formconfirm .= "<!-- end ajax formconfirm -->\n";
4976
		} else {
4977
			$formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
4978
4979
			if (empty($disableformtag)) {
4980
				$formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
4981
			}
4982
4983
			$formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
4984
			$formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
4985
4986
			$formconfirm .= '<table class="valid centpercent">'."\n";
4987
4988
			// Line title
4989
			$formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
4990
			$formconfirm .= img_picto('', 'recent').' '.$title;
4991
			$formconfirm .= '</td></tr>'."\n";
4992
4993
			// Line text
4994
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4995
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
4996
			}
4997
4998
			// Line form fields
4999
			if ($more) {
5000
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
5001
				$formconfirm .= $more;
5002
				$formconfirm .= '</td></tr>'."\n";
5003
			}
5004
5005
			// Line with question
5006
			$formconfirm .= '<tr class="valid">';
5007
			$formconfirm .= '<td class="valid">'.$question.'</td>';
5008
			$formconfirm .= '<td class="valid center">';
5009
			$formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly');
5010
			$formconfirm .= '<input class="button valignmiddle confirmvalidatebutton" type="submit" value="'.$langs->trans("Validate").'">';
5011
			$formconfirm .= '</td>';
5012
			$formconfirm .= '</tr>'."\n";
5013
5014
			$formconfirm .= '</table>'."\n";
5015
5016
			if (empty($disableformtag)) {
5017
				$formconfirm .= "</form>\n";
5018
			}
5019
			$formconfirm .= '<br>';
5020
5021
			if (empty($conf->use_javascript_ajax)) {
5022
				$formconfirm .= '<!-- code to disable button to avoid double clic -->';
5023
				$formconfirm .= '<script type="text/javascript">'."\n";
5024
				$formconfirm .= '
5025
				$(document).ready(function () {
5026
					$(".confirmvalidatebutton").on("click", function() {
5027
						console.log("We click on button");
5028
						$(this).attr("disabled", "disabled");
5029
						setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5030
						//console.log($(this).closest("form"));
5031
						$(this).closest("form").submit();
5032
					});
5033
				});
5034
				';
5035
				$formconfirm .= '</script>'."\n";
5036
			}
5037
5038
			$formconfirm .= "<!-- end formconfirm -->\n";
5039
		}
5040
5041
		return $formconfirm;
5042
	}
5043
5044
5045
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5046
	/**
5047
	 *    Show a form to select a project
5048
	 *
5049
	 *    @param	int		$page        		Page
5050
	 *    @param	int		$socid       		Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id)
5051
	 *    @param    int		$selected    		Id pre-selected project
5052
	 *    @param    string	$htmlname    		Name of select field
5053
	 *    @param	int		$discard_closed		Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable)
5054
	 *    @param	int		$maxlength			Max length
5055
	 *    @param	int		$forcefocus			Force focus on field (works with javascript only)
5056
	 *    @param    int     $nooutput           No print is done. String is returned.
5057
	 *    @return	string                      Return html content
5058
	 */
5059
	public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0)
5060
	{
5061
		// phpcs:enable
5062
		global $langs;
5063
5064
		require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5065
		require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5066
5067
		$out = '';
5068
5069
		$formproject = new FormProjets($this->db);
5070
5071
		$langs->load("project");
5072
		if ($htmlname != "none") {
5073
			$out .= "\n";
5074
			$out .= '<form method="post" action="'.$page.'">';
5075
			$out .= '<input type="hidden" name="action" value="classin">';
5076
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5077
			$out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1);
5078
			$out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5079
			$out .= '</form>';
5080
		} else {
5081
			if ($selected) {
5082
				$projet = new Project($this->db);
5083
				$projet->fetch($selected);
5084
				//print '<a href="'.DOL_URL_ROOT.'/projet/card.php?id='.$selected.'">'.$projet->title.'</a>';
5085
				$out .= $projet->getNomUrl(0, '', 1);
5086
			} else {
5087
				$out .= "&nbsp;";
5088
			}
5089
		}
5090
5091
		if (empty($nooutput)) {
5092
			print $out;
5093
			return '';
5094
		}
5095
		return $out;
5096
	}
5097
5098
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5099
	/**
5100
	 *	Show a form to select payment conditions
5101
	 *
5102
	 *  @param	int		$page        	Page
5103
	 *  @param  string	$selected    	Id condition pre-selectionne
5104
	 *  @param  string	$htmlname    	Name of select html field
5105
	 *	@param	int		$addempty		Add empty entry
5106
	 *  @return	void
5107
	 */
5108
	public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0)
5109
	{
5110
		// phpcs:enable
5111
		global $langs;
5112
		if ($htmlname != "none") {
5113
			print '<form method="post" action="'.$page.'">';
5114
			print '<input type="hidden" name="action" value="setconditions">';
5115
			print '<input type="hidden" name="token" value="'.newToken().'">';
5116
			$this->select_conditions_paiements($selected, $htmlname, -1, $addempty);
5117
			print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5118
			print '</form>';
5119
		} else {
5120
			if ($selected) {
5121
				$this->load_cache_conditions_paiements();
5122
				if (isset($this->cache_conditions_paiements[$selected])) {
5123
					print $this->cache_conditions_paiements[$selected]['label'];
5124
				} else {
5125
					$langs->load('errors');
5126
					print $langs->trans('ErrorNotInDictionaryPaymentConditions');
5127
				}
5128
			} else {
5129
				print "&nbsp;";
5130
			}
5131
		}
5132
	}
5133
5134
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5135
	/**
5136
	 *  Show a form to select a delivery delay
5137
	 *
5138
	 *  @param  int		$page        	Page
5139
	 *  @param  string	$selected    	Id condition pre-selectionne
5140
	 *  @param  string	$htmlname    	Name of select html field
5141
	 *	@param	int		$addempty		Ajoute entree vide
5142
	 *  @return	void
5143
	 */
5144
	public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5145
	{
5146
		// phpcs:enable
5147
		global $langs;
5148
		if ($htmlname != "none") {
5149
			print '<form method="post" action="'.$page.'">';
5150
			print '<input type="hidden" name="action" value="setavailability">';
5151
			print '<input type="hidden" name="token" value="'.newToken().'">';
5152
			$this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5153
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5154
			print '</form>';
5155
		} else {
5156
			if ($selected) {
5157
				$this->load_cache_availability();
5158
				print $this->cache_availability[$selected]['label'];
5159
			} else {
5160
				print "&nbsp;";
5161
			}
5162
		}
5163
	}
5164
5165
	/**
5166
	 *  Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
5167
	 *  List found into table c_input_reason loaded by loadCacheInputReason
5168
	 *
5169
	 *  @param  string	$page        	Page
5170
	 *  @param  string	$selected    	Id condition pre-selectionne
5171
	 *  @param  string	$htmlname    	Name of select html field
5172
	 *  @param	int		$addempty		Add empty entry
5173
	 *  @return	void
5174
	 */
5175
	public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5176
	{
5177
		global $langs;
5178
		if ($htmlname != "none") {
5179
			print '<form method="post" action="'.$page.'">';
5180
			print '<input type="hidden" name="action" value="setdemandreason">';
5181
			print '<input type="hidden" name="token" value="'.newToken().'">';
5182
			$this->selectInputReason($selected, $htmlname, -1, $addempty);
5183
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5184
			print '</form>';
5185
		} else {
5186
			if ($selected) {
5187
				$this->loadCacheInputReason();
5188
				foreach ($this->cache_demand_reason as $key => $val) {
5189
					if ($val['id'] == $selected) {
5190
						print $val['label'];
5191
						break;
5192
					}
5193
				}
5194
			} else {
5195
				print "&nbsp;";
5196
			}
5197
		}
5198
	}
5199
5200
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5201
	/**
5202
	 *    Show a form + html select a date
5203
	 *
5204
	 *    @param	string		$page        	Page
5205
	 *    @param	string		$selected    	Date preselected
5206
	 *    @param    string		$htmlname    	Html name of date input fields or 'none'
5207
	 *    @param    int			$displayhour 	Display hour selector
5208
	 *    @param    int			$displaymin		Display minutes selector
5209
	 *    @param	int			$nooutput		1=No print output, return string
5210
	 *    @return	string
5211
	 *    @see		selectDate()
5212
	 */
5213
	public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0)
5214
	{
5215
		// phpcs:enable
5216
		global $langs;
5217
5218
		$ret = '';
5219
5220
		if ($htmlname != "none") {
5221
			$ret .= '<form method="post" action="'.$page.'" name="form'.$htmlname.'">';
5222
			$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5223
			$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5224
			$ret .= '<table class="nobordernopadding">';
5225
			$ret .= '<tr><td>';
5226
			$ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5227
			$ret .= '</td>';
5228
			$ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5229
			$ret .= '</tr></table></form>';
5230
		} else {
5231
			if ($displayhour) {
5232
				$ret .= dol_print_date($selected, 'dayhour');
5233
			} else {
5234
				$ret .= dol_print_date($selected, 'day');
5235
			}
5236
		}
5237
5238
		if (empty($nooutput)) {
5239
			print $ret;
5240
		}
5241
		return $ret;
5242
	}
5243
5244
5245
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5246
	/**
5247
	 *  Show a select form to choose a user
5248
	 *
5249
	 *  @param	string	$page        	Page
5250
	 *  @param  string	$selected    	Id of user preselected
5251
	 *  @param  string	$htmlname    	Name of input html field. If 'none', we just output the user link.
5252
	 *  @param  array	$exclude		List of users id to exclude
5253
	 *  @param  array	$include        List of users id to include
5254
	 *  @return	void
5255
	 */
5256
	public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5257
	{
5258
		// phpcs:enable
5259
		global $langs;
5260
5261
		if ($htmlname != "none") {
5262
			print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5263
			print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5264
			print '<input type="hidden" name="token" value="'.newToken().'">';
5265
			print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5266
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5267
			print '</form>';
5268
		} else {
5269
			if ($selected) {
5270
				require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5271
				$theuser = new User($this->db);
5272
				$theuser->fetch($selected);
5273
				print $theuser->getNomUrl(1);
5274
			} else {
5275
				print "&nbsp;";
5276
			}
5277
		}
5278
	}
5279
5280
5281
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5282
	/**
5283
	 *    Show form with payment mode
5284
	 *
5285
	 *    @param	string	$page        	Page
5286
	 *    @param    int		$selected    	Id mode pre-selectionne
5287
	 *    @param    string	$htmlname    	Name of select html field
5288
	 *    @param  	string	$filtertype		To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz))
5289
	 *    @param    int     $active         Active or not, -1 = all
5290
	 *    @param   int     $addempty       1=Add empty entry
5291
	 *    @return	void
5292
	 */
5293
	public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0)
5294
	{
5295
		// phpcs:enable
5296
		global $langs;
5297
		if ($htmlname != "none") {
5298
			print '<form method="POST" action="'.$page.'">';
5299
			print '<input type="hidden" name="action" value="setmode">';
5300
			print '<input type="hidden" name="token" value="'.newToken().'">';
5301
			$this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active);
5302
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5303
			print '</form>';
5304
		} else {
5305
			if ($selected) {
5306
				$this->load_cache_types_paiements();
5307
				print $this->cache_types_paiements[$selected]['label'];
5308
			} else {
5309
				print "&nbsp;";
5310
			}
5311
		}
5312
	}
5313
5314
	/**
5315
	 *    Show form with transport mode
5316
	 *
5317
	 *    @param	string	$page        	Page
5318
	 *    @param    int		$selected    	Id mode pre-select
5319
	 *    @param    string	$htmlname    	Name of select html field
5320
	 *    @param    int     $active         Active or not, -1 = all
5321
	 *    @param    int     $addempty       1=Add empty entry
5322
	 *    @return	void
5323
	 */
5324
	public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5325
	{
5326
		global $langs;
5327
		if ($htmlname != "none") {
5328
			print '<form method="POST" action="'.$page.'">';
5329
			print '<input type="hidden" name="action" value="settransportmode">';
5330
			print '<input type="hidden" name="token" value="'.newToken().'">';
5331
			$this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5332
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5333
			print '</form>';
5334
		} else {
5335
			if ($selected) {
5336
				$this->load_cache_transport_mode();
5337
				print $this->cache_transport_mode[$selected]['label'];
5338
			} else {
5339
				print "&nbsp;";
5340
			}
5341
		}
5342
	}
5343
5344
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5345
	/**
5346
	 *    Show form with multicurrency code
5347
	 *
5348
	 *    @param	string	$page        	Page
5349
	 *    @param    string	$selected    	code pre-selectionne
5350
	 *    @param    string	$htmlname    	Name of select html field
5351
	 *    @return	void
5352
	 */
5353
	public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5354
	{
5355
		// phpcs:enable
5356
		global $langs;
5357
		if ($htmlname != "none") {
5358
			print '<form method="POST" action="'.$page.'">';
5359
			print '<input type="hidden" name="action" value="setmulticurrencycode">';
5360
			print '<input type="hidden" name="token" value="'.newToken().'">';
5361
			print $this->selectMultiCurrency($selected, $htmlname, 0);
5362
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5363
			print '</form>';
5364
		} else {
5365
			dol_include_once('/core/lib/company.lib.php');
5366
			print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5367
		}
5368
	}
5369
5370
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5371
	/**
5372
	 *    Show form with multicurrency rate
5373
	 *
5374
	 *    @param	string	$page        	Page
5375
	 *    @param    double	$rate	    	Current rate
5376
	 *    @param    string	$htmlname    	Name of select html field
5377
	 *    @param    string  $currency       Currency code to explain the rate
5378
	 *    @return	void
5379
	 */
5380
	public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5381
	{
5382
		// phpcs:enable
5383
		global $langs, $mysoc, $conf;
5384
5385
		if ($htmlname != "none") {
5386
			print '<form method="POST" action="'.$page.'">';
5387
			print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5388
			print '<input type="hidden" name="token" value="'.newToken().'">';
5389
			print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5390
			print '<select name="calculation_mode">';
5391
			print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5392
			print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5393
			print '</select> ';
5394
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5395
			print '</form>';
5396
		} else {
5397
			if (!empty($rate)) {
5398
				print price($rate, 1, $langs, 1, 0);
5399
				if ($currency && $rate != 1) {
5400
					print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5401
				}
5402
			} else {
5403
				print 1;
5404
			}
5405
		}
5406
	}
5407
5408
5409
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5410
	/**
5411
	 *	Show a select box with available absolute discounts
5412
	 *
5413
	 *  @param  string	$page        	Page URL where form is shown
5414
	 *  @param  int		$selected    	Value pre-selected
5415
	 *	@param  string	$htmlname    	Name of SELECT component. If 'none', not changeable. Example 'remise_id'.
5416
	 *	@param	int		$socid			Third party id
5417
	 * 	@param	float	$amount			Total amount available
5418
	 * 	@param	string	$filter			SQL filter on discounts
5419
	 * 	@param	int		$maxvalue		Max value for lines that can be selected
5420
	 *  @param  string	$more           More string to add
5421
	 *  @param  int     $hidelist       1=Hide list
5422
	 *  @param	int		$discount_type	0 => customer discount, 1 => supplier discount
5423
	 *  @return	void
5424
	 */
5425
	public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5426
	{
5427
		// phpcs:enable
5428
		global $conf, $langs;
5429
		if ($htmlname != "none") {
5430
			print '<form method="post" action="'.$page.'">';
5431
			print '<input type="hidden" name="action" value="setabsolutediscount">';
5432
			print '<input type="hidden" name="token" value="'.newToken().'">';
5433
			print '<div class="inline-block">';
5434
			if (!empty($discount_type)) {
5435
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5436
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5437
						$translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5438
					} else {
5439
						$translationKey = 'HasCreditNoteFromSupplier';
5440
					}
5441
				} else {
5442
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5443
						$translationKey = 'HasAbsoluteDiscountFromSupplier';
5444
					} else {
5445
						$translationKey = 'HasCreditNoteFromSupplier';
5446
					}
5447
				}
5448
			} else {
5449
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5450
					if (!$filter || $filter == "fk_facture_source IS NULL") {
5451
						$translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5452
					} else {
5453
						$translationKey = 'CompanyHasCreditNote';
5454
					}
5455
				} else {
5456
					if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5457
						$translationKey = 'CompanyHasAbsoluteDiscount';
5458
					} else {
5459
						$translationKey = 'CompanyHasCreditNote';
5460
					}
5461
				}
5462
			}
5463
			print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5464
			if (empty($hidelist)) {
5465
				print ' ';
5466
			}
5467
			print '</div>';
5468
			if (empty($hidelist)) {
5469
				print '<div class="inline-block" style="padding-right: 10px">';
5470
				$newfilter = 'discount_type='.intval($discount_type);
5471
				if (!empty($discount_type)) {
5472
					$newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5473
				} else {
5474
					$newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5475
				}
5476
				if ($filter) {
5477
					$newfilter .= ' AND ('.$filter.')';
5478
				}
5479
				$nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5480
				if ($nbqualifiedlines > 0) {
5481
					print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5482
					if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5483
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5484
					}
5485
					if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5486
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5487
					}
5488
5489
					print '>';
5490
				}
5491
				print '</div>';
5492
			}
5493
			if ($more) {
5494
				print '<div class="inline-block">';
5495
				print $more;
5496
				print '</div>';
5497
			}
5498
			print '</form>';
5499
		} else {
5500
			if ($selected) {
5501
				print $selected;
5502
			} else {
5503
				print "0";
5504
			}
5505
		}
5506
	}
5507
5508
5509
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5510
	/**
5511
	 *  Show forms to select a contact
5512
	 *
5513
	 *  @param	string		$page        	Page
5514
	 *  @param	Societe		$societe		Filter on third party
5515
	 *  @param    int			$selected    	Id contact pre-selectionne
5516
	 *  @param    string		$htmlname    	Name of HTML select. If 'none', we just show contact link.
5517
	 *  @return	void
5518
	 */
5519
	public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5520
	{
5521
		// phpcs:enable
5522
		global $langs, $conf;
5523
5524
		if ($htmlname != "none") {
5525
			print '<form method="post" action="'.$page.'">';
5526
			print '<input type="hidden" name="action" value="set_contact">';
5527
			print '<input type="hidden" name="token" value="'.newToken().'">';
5528
			print '<table class="nobordernopadding">';
5529
			print '<tr><td>';
5530
			print $this->selectcontacts($societe->id, $selected, $htmlname);
5531
			$num = $this->num;
5532
			if ($num == 0) {
5533
				$addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5534
				print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5535
			}
5536
			print '</td>';
5537
			print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5538
			print '</tr></table></form>';
5539
		} else {
5540
			if ($selected) {
5541
				require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5542
				$contact = new Contact($this->db);
5543
				$contact->fetch($selected);
5544
				print $contact->getFullName($langs);
5545
			} else {
5546
				print "&nbsp;";
5547
			}
5548
		}
5549
	}
5550
5551
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5552
	/**
5553
	 *  Output html select to select thirdparty
5554
	 *
5555
	 *  @param	string	$page       	Page
5556
	 *  @param  string	$selected   	Id preselected
5557
	 *  @param  string	$htmlname		Name of HTML select
5558
	 *  @param  string	$filter         Optional filters criteras. Do not use a filter coming from input of users.
5559
	 *	@param	int		$showempty		Add an empty field
5560
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
5561
	 * 	@param	int		$forcecombo		Force to use combo box
5562
	 *  @param	array	$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
5563
	 *  @param  int     $nooutput       No print output. Return it only.
5564
	 *  @param	array	$excludeids		Exclude IDs from the select combo
5565
	 *  @return	void|string
5566
	 */
5567
	public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array())
5568
	{
5569
		// phpcs:enable
5570
		global $langs;
5571
5572
		$out = '';
5573
		if ($htmlname != "none") {
5574
			$out .= '<form method="post" action="'.$page.'">';
5575
			$out .= '<input type="hidden" name="action" value="set_thirdparty">';
5576
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5577
			$out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
5578
			$out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5579
			$out .= '</form>';
5580
		} else {
5581
			if ($selected) {
5582
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5583
				$soc = new Societe($this->db);
5584
				$soc->fetch($selected);
5585
				$out .= $soc->getNomUrl($langs);
5586
			} else {
5587
				$out .= "&nbsp;";
5588
			}
5589
		}
5590
5591
		if ($nooutput) {
5592
			return $out;
5593
		} else {
5594
			print $out;
5595
		}
5596
	}
5597
5598
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5599
	/**
5600
	 *    Retourne la liste des devises, dans la langue de l'utilisateur
5601
	 *
5602
	 *    @param	string	$selected    preselected currency code
5603
	 *    @param    string	$htmlname    name of HTML select list
5604
	 *    @deprecated
5605
	 *    @return	void
5606
	 */
5607
	public function select_currency($selected = '', $htmlname = 'currency_id')
5608
	{
5609
		// phpcs:enable
5610
		print $this->selectCurrency($selected, $htmlname);
5611
	}
5612
5613
	/**
5614
	 *  Retourne la liste des devises, dans la langue de l'utilisateur
5615
	 *
5616
	 *  @param	string	$selected    preselected currency code
5617
	 *  @param  string	$htmlname    name of HTML select list
5618
	 *  @param  string  $mode        0 = Add currency symbol into label, 1 = Add 3 letter iso code
5619
	 * 	@return	string
5620
	 */
5621
	public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0)
5622
	{
5623
		global $conf, $langs, $user;
5624
5625
		$langs->loadCacheCurrencies('');
5626
5627
		$out = '';
5628
5629
		if ($selected == 'euro' || $selected == 'euros') {
5630
			$selected = 'EUR'; // Pour compatibilite
5631
		}
5632
5633
		$out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
5634
		foreach ($langs->cache_currencies as $code_iso => $currency) {
5635
			$labeltoshow = $currency['label'];
5636
			if ($mode == 1) {
5637
				$labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
5638
			} else {
5639
				$labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
5640
			}
5641
5642
			if ($selected && $selected == $code_iso) {
5643
				$out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
5644
			} else {
5645
				$out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
5646
			}
5647
			$out .= $labeltoshow;
5648
			$out .= '</option>';
5649
		}
5650
		$out .= '</select>';
5651
		if ($user->admin) {
5652
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5653
		}
5654
5655
		// Make select dynamic
5656
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5657
		$out .= ajax_combobox($htmlname);
5658
5659
		return $out;
5660
	}
5661
5662
	/**
5663
	 *	Return array of currencies in user language
5664
	 *
5665
	 *  @param	string	$selected    preselected currency code
5666
	 *  @param  string	$htmlname    name of HTML select list
5667
	 *  @param  integer	$useempty    1=Add empty line
5668
	 *  @param string $filter Optional filters criteras (example: 'code <> x', ' in (1,3)')
5669
	 *  @param bool $excludeConfCurrency false  = If company current currency not in table, we add it into list. Should always be available.  true = we are in currency_rate update , we don't want to see conf->currency in select
5670
	 * 	@return	string
5671
	 */
5672
	public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false)
5673
	{
5674
		global $db, $conf, $langs, $user;
5675
5676
		$langs->loadCacheCurrencies(''); // Load ->cache_currencies
5677
5678
		$TCurrency = array();
5679
5680
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency';
5681
		$sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
5682
		if ($filter) {
5683
			$sql .= " AND ".$filter;
5684
		}
5685
		$resql = $this->db->query($sql);
5686
		if ($resql) {
5687
			while ($obj = $this->db->fetch_object($resql)) {
5688
				$TCurrency[$obj->code] = $obj->code;
5689
			}
5690
		}
5691
5692
		$out = '';
5693
		$out .= '<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">';
5694
		if ($useempty) {
5695
			$out .= '<option value="">&nbsp;</option>';
5696
		}
5697
		// If company current currency not in table, we add it into list. Should always be available.
5698
		if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
5699
			$TCurrency[$conf->currency] = $conf->currency;
5700
		}
5701
		if (count($TCurrency) > 0) {
5702
			foreach ($langs->cache_currencies as $code_iso => $currency) {
5703
				if (isset($TCurrency[$code_iso])) {
5704
					if (!empty($selected) && $selected == $code_iso) {
5705
						$out .= '<option value="'.$code_iso.'" selected="selected">';
5706
					} else {
5707
						$out .= '<option value="'.$code_iso.'">';
5708
					}
5709
5710
					$out .= $currency['label'];
5711
					$out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
5712
					$out .= '</option>';
5713
				}
5714
			}
5715
		}
5716
5717
		$out .= '</select>';
5718
		// Make select dynamic
5719
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5720
		$out .= ajax_combobox($htmlname);
5721
5722
		return $out;
5723
	}
5724
5725
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5726
	/**
5727
	 *  Load into the cache vat rates of a country
5728
	 *
5729
	 *  @param	string	$country_code		Country code with quotes ("'CA'", or "'CA,IN,...'")
5730
	 *  @return	int							Nb of loaded lines, 0 if already loaded, <0 if KO
5731
	 */
5732
	public function load_cache_vatrates($country_code)
5733
	{
5734
		// phpcs:enable
5735
		global $langs;
5736
5737
		$num = count($this->cache_vatrates);
5738
		if ($num > 0) {
5739
			return $num; // Cache already loaded
5740
		}
5741
5742
		dol_syslog(__METHOD__, LOG_DEBUG);
5743
5744
		$sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
5745
		$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5746
		$sql .= " WHERE t.fk_pays = c.rowid";
5747
		$sql .= " AND t.active > 0";
5748
		$sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
5749
		$sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
5750
5751
		$resql = $this->db->query($sql);
5752
		if ($resql) {
5753
			$num = $this->db->num_rows($resql);
5754
			if ($num) {
5755
				for ($i = 0; $i < $num; $i++) {
5756
					$obj = $this->db->fetch_object($resql);
5757
					$this->cache_vatrates[$i]['rowid']	= $obj->rowid;
5758
					$this->cache_vatrates[$i]['code'] = $obj->code;
5759
					$this->cache_vatrates[$i]['txtva']	= $obj->taux;
5760
					$this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
5761
					$this->cache_vatrates[$i]['localtax1']	    = $obj->localtax1;
5762
					$this->cache_vatrates[$i]['localtax1_type']	= $obj->localtax1_type;
5763
					$this->cache_vatrates[$i]['localtax2']	    = $obj->localtax2;
5764
					$this->cache_vatrates[$i]['localtax2_type']	= $obj->localtax1_type;
5765
5766
					$this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
5767
					$this->cache_vatrates[$i]['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
5768
					$positiverates = '';
5769
					if ($obj->taux) {
5770
						$positiverates .= ($positiverates ? '/' : '').$obj->taux;
5771
					}
5772
					if ($obj->localtax1) {
5773
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
5774
					}
5775
					if ($obj->localtax2) {
5776
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
5777
					}
5778
					if (empty($positiverates)) {
5779
						$positiverates = '0';
5780
					}
5781
					$this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
5782
				}
5783
5784
				return $num;
5785
			} else {
5786
				$this->error = '<font class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</font>';
5787
				return -1;
5788
			}
5789
		} else {
5790
			$this->error = '<font class="error">'.$this->db->error().'</font>';
5791
			return -2;
5792
		}
5793
	}
5794
5795
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5796
	/**
5797
	 *  Output an HTML select vat rate.
5798
	 *  The name of this function should be selectVat. We keep bad name for compatibility purpose.
5799
	 *
5800
	 *  @param	string	      $htmlname           Name of HTML select field
5801
	 *  @param  float|string  $selectedrate       Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing.
5802
	 *  @param  Societe	      $societe_vendeuse   Thirdparty seller
5803
	 *  @param  Societe	      $societe_acheteuse  Thirdparty buyer
5804
	 *  @param  int		      $idprod             Id product. O if unknown of NA.
5805
	 *  @param  int		      $info_bits          Miscellaneous information on line (1 for NPR)
5806
	 *  @param  int|string    $type               ''=Unknown, 0=Product, 1=Service (Used if idprod not defined)
5807
	 *                  		                  Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5808
	 *                  					      Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5809
	 *                  					      Si (vendeur et acheteur dans Communaute europeenne) et bien vendu = moyen de transports neuf (auto, bateau, avion), TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle.
5810
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle.
5811
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle.
5812
	 *                  					      Sinon la TVA proposee par defaut=0. Fin de regle.
5813
	 *  @param	bool	     $options_only		  Return HTML options lines only (for ajax treatment)
5814
	 *  @param  int          $mode                0=Use vat rate as key in combo list, 1=Add VAT code after vat rate into key, -1=Use id of vat line as key
5815
	 *  @return	string
5816
	 */
5817
	public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
5818
	{
5819
		// phpcs:enable
5820
		global $langs, $conf, $mysoc;
5821
5822
		$langs->load('errors');
5823
5824
		$return = '';
5825
5826
		// Define defaultnpr, defaultttx and defaultcode
5827
		$defaultnpr = ($info_bits & 0x01);
5828
		$defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
5829
		$defaulttx = str_replace('*', '', $selectedrate);
5830
		$defaultcode = '';
5831
		$reg = array();
5832
		if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5833
			$defaultcode = $reg[1];
5834
			$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5835
		}
5836
		//var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
5837
5838
		// Check parameters
5839
		if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
5840
			if ($societe_vendeuse->id == $mysoc->id) {
5841
				$return .= '<font class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</font>';
5842
			} else {
5843
				$return .= '<font class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</font>';
5844
			}
5845
			return $return;
5846
		}
5847
5848
		//var_dump($societe_acheteuse);
5849
		//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";
5850
		//exit;
5851
5852
		// Define list of countries to use to search VAT rates to show
5853
		// First we defined code_country to use to find list
5854
		if (is_object($societe_vendeuse)) {
5855
			$code_country = "'".$societe_vendeuse->country_code."'";
5856
		} else {
5857
			$code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
5858
		}
5859
		if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {    // If option to have vat for end customer for services is on
5860
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
5861
			if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
5862
				// We also add the buyer
5863
				if (is_numeric($type)) {
5864
					if ($type == 1) { // We know product is a service
5865
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5866
					}
5867
				} elseif (!$idprod) {  // We don't know type of product
5868
					$code_country .= ",'".$societe_acheteuse->country_code."'";
5869
				} else {
5870
					$prodstatic = new Product($this->db);
5871
					$prodstatic->fetch($idprod);
5872
					if ($prodstatic->type == Product::TYPE_SERVICE) {   // We know product is a service
5873
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5874
					}
5875
				}
5876
			}
5877
		}
5878
5879
		// Now we get list
5880
		$num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
5881
5882
		if ($num > 0) {
5883
			// Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
5884
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
5885
				$tmpthirdparty = new Societe($this->db);
5886
				$defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5887
				$defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5888
				if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5889
					$defaultcode = $reg[1];
5890
					$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5891
				}
5892
				if (empty($defaulttx)) {
5893
					$defaultnpr = 0;
5894
				}
5895
			}
5896
5897
			// Si taux par defaut n'a pu etre determine, on prend dernier de la liste.
5898
			// Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant
5899
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
5900
				if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
5901
					$defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
5902
				} else {
5903
					$defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS);
5904
				}
5905
			}
5906
5907
			// Disabled if seller is not subject to VAT
5908
			$disabled = false;
5909
			$title = '';
5910
			if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
5911
				// Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
5912
				// of using supplier invoices (this is a very bad idea !)
5913
				if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
5914
					$title = ' title="'.$langs->trans('VATIsNotUsed').'"';
5915
					$disabled = true;
5916
				}
5917
			}
5918
5919
			if (!$options_only) {
5920
				$return .= '<select class="flat minwidth75imp" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
5921
			}
5922
5923
			$selectedfound = false;
5924
			foreach ($this->cache_vatrates as $rate) {
5925
				// Keep only 0 if seller is not subject to VAT
5926
				if ($disabled && $rate['txtva'] != 0) {
5927
					continue;
5928
				}
5929
5930
				// Define key to use into select list
5931
				$key = $rate['txtva'];
5932
				$key .= $rate['nprtva'] ? '*' : '';
5933
				if ($mode > 0 && $rate['code']) {
5934
					$key .= ' ('.$rate['code'].')';
5935
				}
5936
				if ($mode < 0) {
5937
					$key = $rate['rowid'];
5938
				}
5939
5940
				$return .= '<option value="'.$key.'"';
5941
				if (!$selectedfound) {
5942
					if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
5943
						if ($defaultcode == $rate['code']) {
5944
							$return .= ' selected';
5945
							$selectedfound = true;
5946
						}
5947
					} elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
5948
						$return .= ' selected';
5949
						$selectedfound = true;
5950
					}
5951
				}
5952
				$return .= '>';
5953
				//if (! empty($conf->global->MAIN_VAT_SHOW_POSITIVE_RATES))
5954
				if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
5955
					$return .= $rate['labelpositiverates'];
5956
				} else {
5957
					$return .= vatrate($rate['label']);
5958
				}
5959
				//$return.=($rate['code']?' '.$rate['code']:'');
5960
				$return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the *  (old behaviour only if new vat code is not used)
5961
5962
				$return .= '</option>';
5963
			}
5964
5965
			if (!$options_only) {
5966
				$return .= '</select>';
5967
			}
5968
		} else {
5969
			$return .= $this->error;
5970
		}
5971
5972
		$this->num = $num;
5973
		return $return;
5974
	}
5975
5976
5977
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5978
	/**
5979
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5980
	 *  Fields are preselected with :
5981
	 *            	- set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5982
	 *            	- local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5983
	 *            	- Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5984
	 *
5985
	 *	@param	integer	    $set_time 		Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
5986
	 *	@param	string		$prefix			Prefix for fields name
5987
	 *	@param	int			$h				1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty
5988
	 *	@param	int			$m				1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty
5989
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5990
	 *	@param	string		$form_name 		Not used
5991
	 *	@param	int			$d				1=Show days, month, years
5992
	 * 	@param	int			$addnowlink		Add a link "Now"
5993
	 * 	@param	int			$nooutput		Do not output html string but return it
5994
	 * 	@param 	int			$disabled		Disable input fields
5995
	 *  @param  int			$fullday        When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59
5996
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another select_date field.
5997
	 *  @param  datetime    $adddateof      Add a link "Date of invoice" using the following date.
5998
	 *  @return	string|void					Nothing or string if nooutput is 1
5999
	 *  @deprecated
6000
	 *  @see    selectDate(), form_date(), select_month(), select_year(), select_dayofweek()
6001
	 */
6002
	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 = '', $addplusone = '', $adddateof = '')
6003
	{
6004
		// phpcs:enable
6005
		$retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6006
		if (!empty($nooutput)) {
6007
			return $retstring;
6008
		}
6009
		print $retstring;
6010
		return;
6011
	}
6012
6013
	/**
6014
	 *  Show 2 HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6015
	 *  Fields are preselected with :
6016
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6017
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6018
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6019
	 *
6020
	 *  @param  integer     $set_time       Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
6021
	 *  @param  integer     $set_time_end       Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
6022
	 *  @param	string		$prefix			Prefix for fields name
6023
	 *  @param	string		$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6024
	 * 	@return string                      Html for selectDate
6025
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6026
	 */
6027
	public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0)
6028
	{
6029
		global $langs;
6030
6031
		$ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6032
		$ret .= '<br>';
6033
		$ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6034
		return $ret;
6035
	}
6036
6037
	/**
6038
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6039
	 *  Fields are preselected with :
6040
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6041
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6042
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6043
	 *
6044
	 *  @param  integer     $set_time       Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
6045
	 *  @param	string		$prefix			Prefix for fields name
6046
	 *  @param	int			$h				1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty
6047
	 *	@param	int			$m				1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty
6048
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6049
	 *	@param	string		$form_name 		Not used
6050
	 *	@param	int			$d				1=Show days, month, years
6051
	 * 	@param	int			$addnowlink		Add a link "Now", 1 with server time, 2 with local computer time
6052
	 * 	@param 	int			$disabled		Disable input fields
6053
	 *  @param  int			$fullday        When a checkbox with id #fullday is checked, hours are set with 00:00 (if value if 'fulldaystart') or 23:59 (if value is 'fulldayend')
6054
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another selectDate field.
6055
	 *  @param  datetime    $adddateof      Add a link "Date of ..." using the following date. See also $labeladddateof for the label used.
6056
	 *  @param  string      $openinghours   Specify hour start and hour end for the select ex 8,20
6057
	 *  @param  int         $stepminutes    Specify step for minutes between 1 and 30
6058
	 *  @param	string		$labeladddateof Label to use for the $adddateof parameter.
6059
	 *  @param	string 		$placeholder    Placeholder
6060
	 *  @param	mixed		$gm				'auto' (for backward compatibility, avoid this), 'gmt' or 'tzserver' or 'tzuserrel'
6061
	 * 	@return string                      Html for selectDate
6062
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6063
	 */
6064
	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')
6065
	{
6066
		global $conf, $langs;
6067
6068
		if ($gm === 'auto') {
6069
			$gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6070
		}
6071
6072
		$retstring = '';
6073
6074
		if ($prefix == '') {
6075
			$prefix = 're';
6076
		}
6077
		if ($h == '') {
6078
			$h = 0;
6079
		}
6080
		if ($m == '') {
6081
			$m = 0;
6082
		}
6083
		$emptydate = 0;
6084
		$emptyhours = 0;
6085
		if ($stepminutes <= 0 || $stepminutes > 30) {
6086
			$stepminutes = 1;
6087
		}
6088
		if ($empty == 1) {
6089
			$emptydate = 1;
6090
			$emptyhours = 1;
6091
		}
6092
		if ($empty == 2) {
6093
			$emptydate = 0;
6094
			$emptyhours = 1;
6095
		}
6096
		$orig_set_time = $set_time;
6097
6098
		if ($set_time === '' && $emptydate == 0) {
6099
			include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6100
			if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6101
				$set_time = dol_now($gm);
6102
			} else {
6103
				$set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6104
			}
6105
		}
6106
6107
		// Analysis of the pre-selection date
6108
		$reg = array();
6109
		if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) {	// deprecated usage
6110
			// Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6111
			$syear	= (!empty($reg[1]) ? $reg[1] : '');
6112
			$smonth = (!empty($reg[2]) ? $reg[2] : '');
6113
			$sday	= (!empty($reg[3]) ? $reg[3] : '');
6114
			$shour	= (!empty($reg[4]) ? $reg[4] : '');
6115
			$smin	= (!empty($reg[5]) ? $reg[5] : '');
6116
		} elseif (strval($set_time) != '' && $set_time != -1) {
6117
			// set_time est un timestamps (0 possible)
6118
			$syear = dol_print_date($set_time, "%Y", $gm);
6119
			$smonth = dol_print_date($set_time, "%m", $gm);
6120
			$sday = dol_print_date($set_time, "%d", $gm);
6121
			if ($orig_set_time != '') {
6122
				$shour = dol_print_date($set_time, "%H", $gm);
6123
				$smin = dol_print_date($set_time, "%M", $gm);
6124
				$ssec = dol_print_date($set_time, "%S", $gm);
6125
			} else {
6126
				$shour = '';
6127
				$smin = '';
6128
				$ssec = '';
6129
			}
6130
		} else {
6131
			// Date est '' ou vaut -1
6132
			$syear = '';
6133
			$smonth = '';
6134
			$sday = '';
6135
			$shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6136
			$smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6137
			$ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6138
		}
6139
		if ($h == 3) {
6140
			$shour = '';
6141
		}
6142
		if ($m == 3) {
6143
			$smin = '';
6144
		}
6145
6146
		$nowgmt = dol_now('gmt');
6147
		//var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6148
6149
		// You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6150
		$usecalendar = 'combo';
6151
		if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6152
			$usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6153
		}
6154
6155
		if ($d) {
6156
			// Show date with popup
6157
			if ($usecalendar != 'combo') {
6158
				$formated_date = '';
6159
				//print "e".$set_time." t ".$conf->format_date_short;
6160
				if (strval($set_time) != '' && $set_time != -1) {
6161
					//$formated_date=dol_print_date($set_time,$conf->format_date_short);
6162
					$formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6163
				}
6164
6165
				// Calendrier popup version eldy
6166
				if ($usecalendar == "eldy") {
6167
					// Input area to enter date manually
6168
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6169
					$retstring .= ($disabled ? ' disabled' : '');
6170
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6171
					$retstring .= '>';
6172
6173
					// Icon calendar
6174
					$retstringbuttom = '';
6175
					if (!$disabled) {
6176
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
6177
						$base = DOL_URL_ROOT.'/core/';
6178
						$retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
6179
						$retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
6180
					} else {
6181
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6182
					}
6183
					$retstring = $retstringbuttom.$retstring;
6184
6185
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6186
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6187
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6188
				} elseif ($usecalendar == 'jquery') {
6189
					if (!$disabled) {
6190
						// Output javascript for datepicker
6191
						$retstring .= "<script type='text/javascript'>";
6192
						$retstring .= "$(function(){ $('#".$prefix."').datepicker({
6193
							dateFormat: '".$langs->trans("FormatDateShortJQueryInput")."',
6194
							autoclose: true,
6195
							todayHighlight: true,";
6196
						if (!empty($conf->dol_use_jmobile)) {
6197
							$retstring .= "
6198
								beforeShow: function (input, datePicker) {
6199
									input.disabled = true;
6200
								},
6201
								onClose: function (dateText, datePicker) {
6202
									this.disabled = false;
6203
								},
6204
								";
6205
						}
6206
						// Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
6207
						if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS)) {
6208
							$retstring .= "
6209
								showOn: 'button',	/* both has problem with autocompletion */
6210
								buttonImage: '".DOL_URL_ROOT."/theme/".$conf->theme."/img/object_calendarday.png',
6211
								buttonImageOnly: true";
6212
						}
6213
						$retstring .= "
6214
							}) });";
6215
						$retstring .= "</script>";
6216
					}
6217
6218
					// Zone de saisie manuelle de la date
6219
					$retstring .= '<div class="nowrap inline-block divfordateinput">';
6220
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6221
					$retstring .= ($disabled ? ' disabled' : '');
6222
					$retstring .= ($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '');
6223
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6224
					$retstring .= '>';
6225
6226
					// Icone calendrier
6227
					if (!$disabled) {
6228
						/* Not required. Managed by option buttonImage of jquery
6229
						$retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
6230
						$retstring.="<script type='text/javascript'>";
6231
						$retstring.="jQuery(document).ready(function() {";
6232
						$retstring.='	jQuery("#'.$prefix.'id").click(function() {';
6233
						$retstring.="    	jQuery('#".$prefix."').focus();";
6234
						$retstring.='    });';
6235
						$retstring.='});';
6236
						$retstring.="</script>";*/
6237
					} else {
6238
						$retstringbutton = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6239
						$retsring = $retstringbutton.$retstring;
6240
					}
6241
6242
					$retstring .= '</div>';
6243
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6244
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6245
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6246
				} else {
6247
					$retstring .= "Bad value of MAIN_POPUP_CALENDAR";
6248
				}
6249
			} else {
6250
				// Show date with combo selects
6251
				// Day
6252
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
6253
6254
				if ($emptydate || $set_time == -1) {
6255
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6256
				}
6257
6258
				for ($day = 1; $day <= 31; $day++) {
6259
					$retstring .= '<option value="'.$day.'"'.($day == $sday ? ' selected' : '').'>'.$day.'</option>';
6260
				}
6261
6262
				$retstring .= "</select>";
6263
6264
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
6265
				if ($emptydate || $set_time == -1) {
6266
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6267
				}
6268
6269
				// Month
6270
				for ($month = 1; $month <= 12; $month++) {
6271
					$retstring .= '<option value="'.$month.'"'.($month == $smonth ? ' selected' : '').'>';
6272
					$retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
6273
					$retstring .= "</option>";
6274
				}
6275
				$retstring .= "</select>";
6276
6277
				// Year
6278
				if ($emptydate || $set_time == -1) {
6279
					$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.'">';
6280
				} else {
6281
					$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
6282
6283
					for ($year = $syear - 10; $year < $syear + 10; $year++) {
6284
						$retstring .= '<option value="'.$year.'"'.($year == $syear ? ' selected' : '').'>'.$year.'</option>';
6285
					}
6286
					$retstring .= "</select>\n";
6287
				}
6288
			}
6289
		}
6290
6291
		if ($d && $h) {
6292
			$retstring .= ($h == 2 ? '<br>' : ' ');
6293
			$retstring .= '<span class="nowraponall">';
6294
		}
6295
6296
		if ($h) {
6297
			$hourstart = 0;
6298
			$hourend = 24;
6299
			if ($openinghours != '') {
6300
				$openinghours = explode(',', $openinghours);
6301
				$hourstart = $openinghours[0];
6302
				$hourend = $openinghours[1];
6303
				if ($hourend < $hourstart) {
6304
					$hourend = $hourstart;
6305
				}
6306
			}
6307
			// Show hour
6308
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'hour' : '').'" id="'.$prefix.'hour" name="'.$prefix.'hour">';
6309
			if ($emptyhours) {
6310
				$retstring .= '<option value="-1">&nbsp;</option>';
6311
			}
6312
			for ($hour = $hourstart; $hour < $hourend; $hour++) {
6313
				if (strlen($hour) < 2) {
6314
					$hour = "0".$hour;
6315
				}
6316
				$retstring .= '<option value="'.$hour.'"'.(($hour == $shour) ? ' selected' : '').'>'.$hour;
6317
				//$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
6318
				$retstring .= '</option>';
6319
			}
6320
			$retstring .= '</select>';
6321
			//if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
6322
			if ($m) {
6323
				$retstring .= ":";
6324
			}
6325
		}
6326
6327
		if ($m) {
6328
			// Show minutes
6329
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'min' : '').'" id="'.$prefix.'min" name="'.$prefix.'min">';
6330
			if ($emptyhours) {
6331
				$retstring .= '<option value="-1">&nbsp;</option>';
6332
			}
6333
			for ($min = 0; $min < 60; $min += $stepminutes) {
6334
				if (strlen($min) < 2) {
6335
					$min = "0".$min;
6336
				}
6337
				$retstring .= '<option value="'.$min.'"'.(($min == $smin) ? ' selected' : '').'>'.$min.(empty($conf->dol_optimize_smallscreen) ? '' : '').'</option>';
6338
			}
6339
			$retstring .= '</select>';
6340
6341
			$retstring .= '<input type="hidden" name="'.$prefix.'sec" value="'.$ssec.'">';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ssec does not seem to be defined for all execution paths leading up to this point.
Loading history...
6342
		}
6343
6344
		if ($d && $h) {
6345
			$retstring .= '</span>';
6346
		}
6347
6348
		// Add a "Now" link
6349
		if ($conf->use_javascript_ajax && $addnowlink) {
6350
			// Script which will be inserted in the onClick of the "Now" link
6351
			$reset_scripts = "";
6352
			if ($addnowlink == 2) { // local computer time
6353
				// pad add leading 0 on numbers
6354
				$reset_scripts .= "Number.prototype.pad = function(size) {
6355
                        var s = String(this);
6356
                        while (s.length < (size || 2)) {s = '0' + s;}
6357
                        return s;
6358
                    };
6359
                    var d = new Date();";
6360
			}
6361
6362
			// Generate the date part, depending on the use or not of the javascript calendar
6363
			if ($addnowlink == 1) { // server time expressed in user time setup
6364
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6365
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6366
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6367
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6368
			} elseif ($addnowlink == 2) {
6369
				/* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
6370
				 * This break application for foreign languages.
6371
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
6372
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
6373
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
6374
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
6375
				*/
6376
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6377
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6378
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6379
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6380
			}
6381
			/*if ($usecalendar == "eldy")
6382
			{
6383
				$base=DOL_URL_ROOT.'/core/';
6384
				$reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
6385
			}
6386
			else
6387
			{
6388
				$reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
6389
				$reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
6390
				$reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
6391
			}*/
6392
			// Update the hour part
6393
			if ($h) {
6394
				if ($fullday) {
6395
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6396
				}
6397
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
6398
				if ($addnowlink == 1) {
6399
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6400
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6401
				} elseif ($addnowlink == 2) {
6402
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());';
6403
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6404
				}
6405
6406
				if ($fullday) {
6407
					$reset_scripts .= ' } ';
6408
				}
6409
			}
6410
			// Update the minute part
6411
			if ($m) {
6412
				if ($fullday) {
6413
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6414
				}
6415
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
6416
				if ($addnowlink == 1) {
6417
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6418
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6419
				} elseif ($addnowlink == 2) {
6420
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());';
6421
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6422
				}
6423
				if ($fullday) {
6424
					$reset_scripts .= ' } ';
6425
				}
6426
			}
6427
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6428
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6429
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonNow" type="button" name="_useless" value="now" onClick="'.$reset_scripts.'">';
6430
				$retstring .= $langs->trans("Now");
6431
				$retstring .= '</button> ';
6432
			}
6433
		}
6434
6435
		// Add a "Plus one hour" link
6436
		if ($conf->use_javascript_ajax && $addplusone) {
6437
			// Script which will be inserted in the onClick of the "Add plusone" link
6438
			$reset_scripts = "";
6439
6440
			// Generate the date part, depending on the use or not of the javascript calendar
6441
			$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel').'\');';
6442
			$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6443
			$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6444
			$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6445
			// Update the hour part
6446
			if ($h) {
6447
				if ($fullday) {
6448
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6449
				}
6450
				$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6451
				if ($fullday) {
6452
					$reset_scripts .= ' } ';
6453
				}
6454
			}
6455
			// Update the minute part
6456
			if ($m) {
6457
				if ($fullday) {
6458
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6459
				}
6460
				$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6461
				if ($fullday) {
6462
					$reset_scripts .= ' } ';
6463
				}
6464
			}
6465
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6466
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6467
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="'.$reset_scripts.'">';
6468
				$retstring .= $langs->trans("DateStartPlusOne");
6469
				$retstring .= '</button> ';
6470
			}
6471
		}
6472
6473
		// Add a link to set data
6474
		if ($conf->use_javascript_ajax && $adddateof) {
6475
			$tmparray = dol_getdate($adddateof);
6476
			if (empty($labeladddateof)) {
6477
				$labeladddateof = $langs->trans("DateInvoice");
6478
			}
6479
			$retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="console.log(\'Click on now link\'); jQuery(\'#re\').val(\''.dol_print_date($adddateof, 'dayinputnoreduce').'\');jQuery(\'#reday\').val(\''.$tmparray['mday'].'\');jQuery(\'#remonth\').val(\''.$tmparray['mon'].'\');jQuery(\'#reyear\').val(\''.$tmparray['year'].'\');">'.$labeladddateof.'</a>';
6480
		}
6481
6482
		return $retstring;
6483
	}
6484
6485
	/**
6486
	 * selectTypeDuration
6487
	 *
6488
	 * @param   string   	$prefix     	Prefix
6489
	 * @param   string   	$selected   	Selected duration type
6490
	 * @param	array		$excludetypes	Array of duration types to exclude. Example array('y', 'm')
6491
	 * @return  string      	         	HTML select string
6492
	 */
6493
	public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
6494
	{
6495
		global $langs;
6496
6497
		$TDurationTypes = array(
6498
			'y'=>$langs->trans('Years'),
6499
			'm'=>$langs->trans('Month'),
6500
			'w'=>$langs->trans('Weeks'),
6501
			'd'=>$langs->trans('Days'),
6502
			'h'=>$langs->trans('Hours'),
6503
			'i'=>$langs->trans('Minutes')
6504
		);
6505
6506
		// Removed undesired duration types
6507
		foreach ($excludetypes as $value) {
6508
			unset($TDurationTypes[$value]);
6509
		}
6510
6511
		$retstring = '<select class="flat" id="select_'.$prefix.'type_duration" name="'.$prefix.'type_duration">';
6512
		foreach ($TDurationTypes as $key => $typeduration) {
6513
			$retstring .= '<option value="'.$key.'"';
6514
			if ($key == $selected) {
6515
				$retstring .= " selected";
6516
			}
6517
			$retstring .= ">".$typeduration."</option>";
6518
		}
6519
		$retstring .= "</select>";
6520
6521
		$retstring .= ajax_combobox('select_'.$prefix.'type_duration');
6522
6523
		return $retstring;
6524
	}
6525
6526
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6527
	/**
6528
	 *  Function to show a form to select a duration on a page
6529
	 *
6530
	 *	@param	string		$prefix   		Prefix for input fields
6531
	 *	@param  int			$iSecond  		Default preselected duration (number of seconds or '')
6532
	 * 	@param	int			$disabled       Disable the combo box
6533
	 * 	@param	string		$typehour		If 'select' then input hour and input min is a combo,
6534
	 *						            	If 'text' input hour is in text and input min is a text,
6535
	 *						            	If 'textselect' input hour is in text and input min is a combo
6536
	 *  @param	integer		$minunderhours	If 1, show minutes selection under the hours
6537
	 * 	@param	int			$nooutput		Do not output html string but return it
6538
	 *  @return	string|void
6539
	 */
6540
	public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
6541
	{
6542
		// phpcs:enable
6543
		global $langs;
6544
6545
		$retstring = '';
6546
6547
		$hourSelected = 0;
6548
		$minSelected = 0;
6549
6550
		// Hours
6551
		if ($iSecond != '') {
6552
			require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6553
6554
			$hourSelected = convertSecondToTime($iSecond, 'allhour');
6555
			$minSelected = convertSecondToTime($iSecond, 'min');
6556
		}
6557
6558
		if ($typehour == 'select') {
6559
			$retstring .= '<select class="flat" id="select_'.$prefix.'hour" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').'>';
6560
			for ($hour = 0; $hour < 25; $hour++) {	// For a duration, we allow 24 hours
6561
				$retstring .= '<option value="'.$hour.'"';
6562
				if ($hourSelected == $hour) {
6563
					$retstring .= " selected";
6564
				}
6565
				$retstring .= ">".$hour."</option>";
6566
			}
6567
			$retstring .= "</select>";
6568
		} elseif ($typehour == 'text' || $typehour == 'textselect') {
6569
			$retstring .= '<input placeholder="'.$langs->trans('HourShort').'" type="number" min="0" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputhour" value="'.(($hourSelected != '') ? ((int) $hourSelected) : '').'">';
6570
		} else {
6571
			return 'BadValueForParameterTypeHour';
6572
		}
6573
6574
		if ($typehour != 'text') {
6575
			$retstring .= ' '.$langs->trans('HourShort');
6576
		} else {
6577
			$retstring .= '<span class="hideonsmartphone">:</span>';
6578
		}
6579
6580
		// Minutes
6581
		if ($minunderhours) {
6582
			$retstring .= '<br>';
6583
		} else {
6584
			$retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
6585
		}
6586
6587
		if ($typehour == 'select' || $typehour == 'textselect') {
6588
			$retstring .= '<select class="flat" id="select_'.$prefix.'min" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').'>';
6589
			for ($min = 0; $min <= 55; $min = $min + 5) {
6590
				$retstring .= '<option value="'.$min.'"';
6591
				if ($minSelected == $min) {
6592
					$retstring .= ' selected';
6593
				}
6594
				$retstring .= '>'.$min.'</option>';
6595
			}
6596
			$retstring .= "</select>";
6597
		} elseif ($typehour == 'text') {
6598
			$retstring .= '<input placeholder="'.$langs->trans('MinuteShort').'" type="number" min="0" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputminute" value="'.(($minSelected != '') ? ((int) $minSelected) : '').'">';
6599
		}
6600
6601
		if ($typehour != 'text') {
6602
			$retstring .= ' '.$langs->trans('MinuteShort');
6603
		}
6604
6605
		//$retstring.="&nbsp;";
6606
6607
		if (!empty($nooutput)) {
6608
			return $retstring;
6609
		}
6610
6611
		print $retstring;
6612
		return;
6613
	}
6614
6615
	/**
6616
	 *  Return list of tickets in Ajax if Ajax activated or go to selectTicketsList
6617
	 *
6618
	 *  @param		int			$selected				Preselected tickets
6619
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
6620
	 *  @param  	string		$filtertype     		To add a filter
6621
	 *  @param		int			$limit					Limit on number of returned lines
6622
	 *  @param		int			$status					Ticket status
6623
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
6624
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
6625
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
6626
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
6627
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6628
	 * 	@param		int			$forcecombo				Force to use combo box
6629
	 *  @param      string      $morecss                Add more css on select
6630
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
6631
	 *  @param		string		$nooutput				No print, return the output into a string
6632
	 *  @return		void|string
6633
	 */
6634
	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)
6635
	{
6636
		global $langs, $conf;
6637
6638
		$out = '';
6639
6640
		// check parameters
6641
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
6642
6643
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6644
			$placeholder = '';
6645
6646
			if ($selected && empty($selected_input_value)) {
6647
				require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6648
				$tickettmpselect = new Ticket($this->db);
6649
				$tickettmpselect->fetch($selected);
6650
				$selected_input_value = $tickettmpselect->ref;
6651
				unset($tickettmpselect);
6652
			}
6653
6654
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $urloption seems to be never defined.
Loading history...
6655
6656
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
6657
			elseif ($hidelabel > 1) {
6658
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
6659
				if ($hidelabel == 2) {
6660
					$out .= img_picto($langs->trans("Search"), 'search');
6661
				}
6662
			}
6663
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
6664
			if ($hidelabel == 3) {
6665
				$out .= img_picto($langs->trans("Search"), 'search');
6666
			}
6667
		} else {
6668
			$out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
6669
		}
6670
6671
		if (empty($nooutput)) print $out;
6672
		else return $out;
6673
	}
6674
6675
6676
	/**
6677
	 *	Return list of tickets.
6678
	 *  Called by selectTickets.
6679
	 *
6680
	 *	@param      int		$selected           Preselected ticket
6681
	 *	@param      string	$htmlname           Name of select html
6682
	 *  @param		string	$filtertype         Filter on ticket type
6683
	 *	@param      int		$limit              Limit on number of returned lines
6684
	 * 	@param      string	$filterkey          Filter on ticket ref or subject
6685
	 *	@param		int		$status             Ticket status
6686
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
6687
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6688
	 * 	@param		int		$forcecombo		    Force to use combo box
6689
	 *  @param      string  $morecss            Add more css on select
6690
	 *  @return     array    				    Array of keys for json
6691
	 */
6692
	public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
6693
	{
6694
		global $langs, $conf, $user, $db;
6695
6696
		$out = '';
6697
		$outarray = array();
6698
6699
		$selectFields = " p.rowid, p.ref, p.message";
6700
6701
		$sql = "SELECT ";
6702
		$sql .= $selectFields;
6703
		$sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
6704
		$sql .= ' WHERE p.entity IN ('.getEntity('ticket').')';
6705
6706
		// Add criteria on ref/label
6707
		if ($filterkey != '') {
6708
			$sql .= ' AND (';
6709
			$prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
6710
			// For natural search
6711
			$scrit = explode(' ', $filterkey);
6712
			$i = 0;
6713
			if (count($scrit) > 1) $sql .= "(";
6714
			foreach ($scrit as $crit) {
6715
				if ($i > 0) $sql .= " AND ";
6716
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.subject LIKE '".$this->db->escape($prefix.$crit)."%'";
6717
				$sql .= ")";
6718
				$i++;
6719
			}
6720
			if (count($scrit) > 1) $sql .= ")";
6721
			$sql .= ')';
6722
		}
6723
6724
		$sql .= $this->db->plimit($limit, 0);
6725
6726
		// Build output string
6727
		dol_syslog(get_class($this)."::selectTicketsList search tickets", LOG_DEBUG);
6728
		$result = $this->db->query($sql);
6729
		if ($result) {
6730
			require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6731
			require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
6732
6733
			$num = $this->db->num_rows($result);
6734
6735
			$events = null;
6736
6737
			if (!$forcecombo) {
6738
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6739
				$out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT);
6740
			}
6741
6742
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
6743
6744
			$textifempty = '';
6745
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
6746
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
6747
			if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6748
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6749
				else $textifempty .= $langs->trans("All");
6750
			} else {
6751
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6752
			}
6753
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
6754
6755
			$i = 0;
6756
			while ($num && $i < $num) {
6757
				$opt = '';
6758
				$optJson = array();
6759
				$objp = $this->db->fetch_object($result);
6760
6761
				$this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
6762
				// Add new entry
6763
				// "key" value of json key array is used by jQuery automatically as selected value
6764
				// "label" value of json key array is used by jQuery automatically as text for combo box
6765
				$out .= $opt;
6766
				array_push($outarray, $optJson);
6767
6768
				$i++;
6769
			}
6770
6771
			$out .= '</select>';
6772
6773
			$this->db->free($result);
6774
6775
			if (empty($outputmode)) return $out;
6776
			return $outarray;
6777
		} else {
6778
			dol_print_error($db);
6779
		}
6780
	}
6781
6782
	/**
6783
	 * constructTicketListOption.
6784
	 * This define value for &$opt and &$optJson.
6785
	 *
6786
	 * @param 	resource	$objp			    Result set of fetch
6787
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
6788
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
6789
	 * @param 	string		$selected		    Preselected value
6790
	 * @param   string      $filterkey          Filter key to highlight
6791
	 * @return	void
6792
	 */
6793
	protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
6794
	{
6795
		global $langs, $conf, $user, $db;
6796
6797
		$outkey = '';
6798
		$outval = '';
6799
		$outref = '';
6800
		$outlabel = '';
6801
		$outtype = '';
6802
6803
		$label = $objp->label;
6804
6805
		$outkey = $objp->rowid;
6806
		$outref = $objp->ref;
6807
		$outlabel = $objp->label;
6808
		$outtype = $objp->fk_product_type;
6809
6810
		$opt = '<option value="'.$objp->rowid.'"';
6811
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
6812
		$opt .= '>';
6813
		$opt .= $objp->ref;
6814
		$objRef = $objp->ref;
6815
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
6816
		$outval .= $objRef;
6817
6818
		$opt .= "</option>\n";
6819
		$optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtypem);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $outtypem does not exist. Did you maybe mean $outtype?
Loading history...
6820
	}
6821
6822
6823
	/**
6824
	 * Generic method to select a component from a combo list.
6825
	 * Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.
6826
	 * This is the generic method that will replace all specific existing methods.
6827
	 *
6828
	 * @param 	string			$objectdesc			ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]
6829
	 * @param	string			$htmlname			Name of HTML select component
6830
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6831
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6832
	 * @param	string			$searchkey			Search criteria
6833
	 * @param	string			$placeholder		Place holder
6834
	 * @param	string			$morecss			More CSS
6835
	 * @param	string			$moreparams			More params provided to ajax call
6836
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6837
	 * @param	int				$disabled			1=Html component is disabled
6838
	 * @param	string	        $selected_input_value	Value of preselected input text (for use with ajax)
6839
	 * @return	string								Return HTML string
6840
	 * @see selectForFormsList() select_thirdparty_list()
6841
	 */
6842
	public function selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '')
6843
	{
6844
		global $conf, $user;
6845
6846
		$objecttmp = null;
6847
6848
		$InfoFieldList = explode(":", $objectdesc);
6849
		$classname = $InfoFieldList[0];
6850
		$classpath = $InfoFieldList[1];
6851
		$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
6852
		$filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
6853
		$sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
6854
6855
		if (!empty($classpath)) {
6856
			dol_include_once($classpath);
6857
6858
			if ($classname && class_exists($classname)) {
6859
				$objecttmp = new $classname($this->db);
6860
				// Make some replacement
6861
				$sharedentities = getEntity(strtolower($classname));
6862
				$objecttmp->filter = str_replace(
6863
					array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
6864
					array($conf->entity, $sharedentities, $user->id),
6865
					$filter
6866
				);
6867
			}
6868
		}
6869
		if (!is_object($objecttmp)) {
6870
			dol_syslog('Error bad setup of type for field '.$InfoFieldList, LOG_WARNING);
6871
			return 'Error bad setup of type for field '.join(',', $InfoFieldList);
6872
		}
6873
6874
		//var_dump($objecttmp->filter);
6875
		$prefixforautocompletemode = $objecttmp->element;
6876
		if ($prefixforautocompletemode == 'societe') {
6877
			$prefixforautocompletemode = 'company';
6878
		}
6879
		if ($prefixforautocompletemode == 'product') {
6880
			$prefixforautocompletemode = 'produit';
6881
		}
6882
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6883
6884
		dol_syslog(get_class($this)."::selectForForms object->filter=".$objecttmp->filter, LOG_DEBUG);
6885
		$out = '';
6886
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->$confkeyforautocompletemode) && !$forcecombo) {
6887
			// No immediate load of all database
6888
			$placeholder = '';
6889
			if ($preselectedvalue && empty($selected_input_value)) {
6890
				$objecttmp->fetch($preselectedvalue);
6891
				$selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
6892
				//unset($objecttmp);
6893
			}
6894
6895
			$objectdesc = $classname.':'.$classpath.':'.$addcreatebuttonornot.':'.$filter;
6896
			$urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php';
6897
6898
			// No immediate load of all database
6899
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&objectdesc='.urlencode($objectdesc).'&filter='.urlencode($objecttmp->filter).($sortfield ? '&sortfield='.urlencode($sortfield) : '');
6900
			// Activate the auto complete using ajax call.
6901
			$out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
6902
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
6903
			$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).'"' : '') .' />';
6904
		} else {
6905
			// Immediate load of table record. Note: filter is inside $objecttmp->filter
6906
			$out .= $this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield);
6907
		}
6908
6909
		return $out;
6910
	}
6911
6912
	/**
6913
	 * Function to forge a SQL criteria
6914
	 *
6915
	 * @param  array    $matches       Array of found string by regex search. Example: "t.ref:like:'SO-%'" or "t.date_creation:<:'20160101'" or "t.nature:is:NULL"
6916
	 * @return string                  Forged criteria. Example: "t.field like 'abc%'"
6917
	 */
6918
	protected static function forgeCriteriaCallback($matches)
6919
	{
6920
		global $db;
6921
6922
		//dol_syslog("Convert matches ".$matches[1]);
6923
		if (empty($matches[1])) {
6924
			return '';
6925
		}
6926
		$tmp = explode(':', $matches[1]);
6927
		if (count($tmp) < 3) {
6928
			return '';
6929
		}
6930
6931
		$tmpescaped = $tmp[2];
6932
		$regbis = array();
6933
		if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
6934
			$tmpescaped = "'".$db->escape($regbis[1])."'";
6935
		} else {
6936
			$tmpescaped = $db->escape($tmpescaped);
6937
		}
6938
		return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped;
6939
	}
6940
6941
	/**
6942
	 * Output html form to select an object.
6943
	 * Note, this function is called by selectForForms or by ajax selectobject.php
6944
	 *
6945
	 * @param 	Object			$objecttmp			Object to knwo the table to scan for combo.
6946
	 * @param	string			$htmlname			Name of HTML select component
6947
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6948
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6949
	 * @param	string			$searchkey			Search value
6950
	 * @param	string			$placeholder		Place holder
6951
	 * @param	string			$morecss			More CSS
6952
	 * @param	string			$moreparams			More params provided to ajax call
6953
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6954
	 * @param	int				$outputmode			0=HTML select string, 1=Array
6955
	 * @param	int				$disabled			1=Html component is disabled
6956
	 * @param	string			$sortfield			Sort field
6957
	 * @return	string|array						Return HTML string
6958
	 * @see selectForForms()
6959
	 */
6960
	public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '')
6961
	{
6962
		global $conf, $langs, $user, $hookmanager;
6963
6964
		//print "$objecttmp->filter, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled";
6965
6966
		$prefixforautocompletemode = $objecttmp->element;
6967
		if ($prefixforautocompletemode == 'societe') {
6968
			$prefixforautocompletemode = 'company';
6969
		}
6970
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6971
6972
		if (!empty($objecttmp->fields)) {	// For object that declare it, it is better to use declared fields (like societe, contact, ...)
6973
			$tmpfieldstoshow = '';
6974
			foreach ($objecttmp->fields as $key => $val) {
6975
				if (!dol_eval($val['enabled'], 1, 1)) {
6976
					continue;
6977
				}
6978
				if (!empty($val['showoncombobox'])) {
6979
					$tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '').'t.'.$key;
6980
				}
6981
			}
6982
			if ($tmpfieldstoshow) {
6983
				$fieldstoshow = $tmpfieldstoshow;
6984
			}
6985
		} else {
6986
			// For backward compatibility
6987
			$objecttmp->fields['ref'] = array('type'=>'varchar(30)', 'label'=>'Ref', 'showoncombobox'=>1);
6988
		}
6989
6990
		if (empty($fieldstoshow)) {
6991
			if (isset($objecttmp->fields['ref'])) {
6992
				$fieldstoshow = 't.ref';
6993
			} else {
6994
				$langs->load("errors");
6995
				$this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
6996
				return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
6997
			}
6998
		}
6999
7000
		$out = '';
7001
		$outarray = array();
7002
7003
		$num = 0;
7004
7005
		// Search data
7006
		$sql = "SELECT t.rowid, ".$fieldstoshow." FROM ".MAIN_DB_PREFIX.$objecttmp->table_element." as t";
7007
		if (isset($objecttmp->ismultientitymanaged)) {
7008
			if (!is_numeric($objecttmp->ismultientitymanaged)) {
7009
				$tmparray = explode('@', $objecttmp->ismultientitymanaged);
7010
				$sql .= " INNER JOIN ".MAIN_DB_PREFIX.$tmparray[1]." as parenttable ON parenttable.rowid = t.".$tmparray[0];
7011
			}
7012
			if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
7013
				if (!$user->rights->societe->client->voir && !$user->socid) {
7014
					$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
7015
				}
7016
			}
7017
		}
7018
7019
		// Add where from hooks
7020
		$parameters = array();
7021
		$reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
7022
		if (!empty($hookmanager->resPrint)) {
7023
			$sql .= $hookmanager->resPrint;
7024
		} else {
7025
			$sql .= " WHERE 1=1";
7026
			if (isset($objecttmp->ismultientitymanaged)) {
7027
				if ($objecttmp->ismultientitymanaged == 1) {
7028
					$sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
7029
				}
7030
				if (!is_numeric($objecttmp->ismultientitymanaged)) {
7031
					$sql .= " AND parenttable.entity = t.".$tmparray[0];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tmparray does not seem to be defined for all execution paths leading up to this point.
Loading history...
7032
				}
7033
				if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
7034
					if ($objecttmp->element == 'societe') {
7035
						$sql .= " AND t.rowid = ".((int) $user->socid);
7036
					} else {
7037
						$sql .= " AND t.fk_soc = ".((int) $user->socid);
7038
					}
7039
				}
7040
				if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
7041
					if (!$user->rights->societe->client->voir && !$user->socid) {
7042
						$sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
7043
					}
7044
				}
7045
			}
7046
			if ($searchkey != '') {
7047
				$sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
7048
			}
7049
			if ($objecttmp->filter) {	 // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
7050
				/*if (! DolibarrApi::_checkFilters($objecttmp->filter))
7051
				{
7052
					throw new RestException(503, 'Error when validating parameter sqlfilters '.$objecttmp->filter);
7053
				}*/
7054
				$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
7055
				$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'Form::forgeCriteriaCallback', $objecttmp->filter).")";
7056
			}
7057
		}
7058
		$sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, "ASC");
7059
		//$sql.=$this->db->plimit($limit, 0);
7060
		//print $sql;
7061
7062
		// Build output string
7063
		$resql = $this->db->query($sql);
7064
		if ($resql) {
7065
			// Construct $out and $outarray
7066
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').($moreparams ? ' '.$moreparams : '').' name="'.$htmlname.'">'."\n";
7067
7068
			// 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
7069
			$textifempty = '&nbsp;';
7070
7071
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7072
			if (!empty($conf->global->$confkeyforautocompletemode)) {
7073
				if ($showempty && !is_numeric($showempty)) {
7074
					$textifempty = $langs->trans($showempty);
7075
				} else {
7076
					$textifempty .= $langs->trans("All");
7077
				}
7078
			}
7079
			if ($showempty) {
7080
				$out .= '<option value="-1">'.$textifempty.'</option>'."\n";
7081
			}
7082
7083
			$num = $this->db->num_rows($resql);
7084
			$i = 0;
7085
			if ($num) {
7086
				while ($i < $num) {
7087
					$obj = $this->db->fetch_object($resql);
7088
					$label = '';
7089
					$tmparray = explode(',', $fieldstoshow);
7090
					$oldvalueforshowoncombobox = 0;
7091
					foreach ($tmparray as $key => $val) {
7092
						$val = preg_replace('/t\./', '', $val);
7093
						$label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
7094
						$label .= $obj->$val;
7095
						$oldvalueforshowoncombobox = $objecttmp->fields[$val]['showoncombobox'];
7096
					}
7097
					if (empty($outputmode)) {
7098
						if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
7099
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
7100
						} else {
7101
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
7102
						}
7103
					} else {
7104
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
7105
					}
7106
7107
					$i++;
7108
					if (($i % 10) == 0) {
7109
						$out .= "\n";
7110
					}
7111
				}
7112
			}
7113
7114
			$out .= '</select>'."\n";
7115
7116
			if (!$forcecombo) {
7117
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7118
				$out .= ajax_combobox($htmlname, null, (!empty($conf->global->$confkeyforautocompletemode) ? $conf->global->$confkeyforautocompletemode : 0));
7119
			}
7120
		} else {
7121
			dol_print_error($this->db);
7122
		}
7123
7124
		$this->result = array('nbofelement'=>$num);
7125
7126
		if ($outputmode) {
7127
			return $outarray;
7128
		}
7129
		return $out;
7130
	}
7131
7132
7133
	/**
7134
	 *	Return a HTML select string, built from an array of key+value.
7135
	 *  Note: Do not apply langs->trans function on returned content, content may be entity encoded twice.
7136
	 *
7137
	 *	@param	string			$htmlname			Name of html select area. Must start with "multi" if this is a multiselect
7138
	 *	@param	array			$array				Array like array(key => value) or array(key=>array('label'=>..., 'data-...'=>..., 'disabled'=>..., 'css'=>...))
7139
	 *	@param	string|string[]	$id					Preselected key or preselected keys for multiselect
7140
	 *	@param	int|string		$show_empty			0 no empty value allowed, 1 or string to add an empty value into list (If 1: key is -1 and value is '' or '&nbsp;', If placeholder string: key is -1 and value is the string), <0 to add an empty value with key that is this value.
7141
	 *	@param	int				$key_in_label		1 to show key into label with format "[key] value"
7142
	 *	@param	int				$value_as_key		1 to use value as key
7143
	 *	@param  string			$moreparam			Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
7144
	 *	@param  int				$translate			1=Translate and encode value
7145
	 * 	@param	int				$maxlen				Length maximum for labels
7146
	 * 	@param	int				$disabled			Html select box is disabled
7147
	 *  @param	string			$sort				'ASC' or 'DESC' = Sort on label, '' or 'NONE' or 'POS' = Do not sort, we keep original order
7148
	 *  @param	string			$morecss			Add more class to css styles
7149
	 *  @param	int				$addjscombo			Add js combo
7150
	 *  @param  string          $moreparamonempty	Add more param on the empty option line. Not used if show_empty not set
7151
	 *  @param  int             $disablebademail	1=Check if a not valid email, 2=Check string '---', and if found into value, disable and colorize entry
7152
	 *  @param  int             $nohtmlescape		No html escaping.
7153
	 * 	@return	string								HTML select string.
7154
	 *  @see multiselectarray(), selectArrayAjax(), selectArrayFilter()
7155
	 */
7156
	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 = '', $addjscombo = 1, $moreparamonempty = '', $disablebademail = 0, $nohtmlescape = 0)
7157
	{
7158
		global $conf, $langs;
7159
7160
		// Do we want a multiselect ?
7161
		//$jsbeautify = 0;
7162
		//if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
7163
		$jsbeautify = 1;
7164
7165
		if ($value_as_key) {
7166
			$array = array_combine($array, $array);
7167
		}
7168
7169
		$out = '';
7170
7171
		if ($addjscombo < 0) {
7172
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7173
				$addjscombo = 1;
7174
			} else {
7175
				$addjscombo = 0;
7176
			}
7177
		}
7178
7179
		// Add code for jquery to use multiselect
7180
		if ($addjscombo && $jsbeautify) {
7181
			// Enhance with select2
7182
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7183
			$out .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', $show_empty < 0 ? (string) $show_empty : '-1');
7184
		}
7185
7186
		$out .= '<select id="'.preg_replace('/^\./', '', $htmlname).'" '.($disabled ? 'disabled="disabled" ' : '').'class="flat '.(preg_replace('/^\./', '', $htmlname)).($morecss ? ' '.$morecss : '').'"';
7187
		$out .= ' name="'.preg_replace('/^\./', '', $htmlname).'" '.($moreparam ? $moreparam : '');
7188
		$out .= '>';
7189
7190
		if ($show_empty) {
7191
			$textforempty = ' ';
7192
			if (!empty($conf->use_javascript_ajax)) {
7193
				$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
7194
			}
7195
			if (!is_numeric($show_empty)) {
7196
				$textforempty = $show_empty;
7197
			}
7198
			$out .= '<option class="optiongrey" '.($moreparamonempty ? $moreparamonempty.' ' : '').'value="'.($show_empty < 0 ? $show_empty : -1).'"'.($id == $show_empty ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
7199
		}
7200
7201
		if (is_array($array)) {
7202
			// Translate
7203
			if ($translate) {
7204
				foreach ($array as $key => $value) {
7205
					if (!is_array($value)) {
7206
						$array[$key] = $langs->trans($value);
7207
					} else {
7208
						$array[$key]['label'] = $langs->trans($value['label']);
7209
					}
7210
				}
7211
			}
7212
7213
			// Sort
7214
			if ($sort == 'ASC') {
7215
				asort($array);
7216
			} elseif ($sort == 'DESC') {
7217
				arsort($array);
7218
			}
7219
7220
			foreach ($array as $key => $tmpvalue) {
7221
				if (is_array($tmpvalue)) {
7222
					$value = $tmpvalue['label'];
7223
					$disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
7224
					$style = empty($tmpvalue['css']) ? ' class="'.$tmpvalue['css'].'"' : '';
7225
				} else {
7226
					$value = $tmpvalue;
7227
					$disabled = '';
7228
					$style = '';
7229
				}
7230
				if (!empty($disablebademail)) {
7231
					if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
7232
						|| ($disablebademail == 2 && preg_match('/---/', $value))) {
7233
						$disabled = ' disabled';
7234
						$style = ' class="warning"';
7235
					}
7236
				}
7237
7238
				if ($key_in_label) {
7239
					if (empty($nohtmlescape)) {
7240
						$selectOptionValue = dol_escape_htmltag($key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value));
7241
					} else {
7242
						$selectOptionValue = $key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value);
7243
					}
7244
				} else {
7245
					if (empty($nohtmlescape)) {
7246
						$selectOptionValue = dol_escape_htmltag($maxlen ?dol_trunc($value, $maxlen) : $value);
7247
					} else {
7248
						$selectOptionValue = $maxlen ?dol_trunc($value, $maxlen) : $value;
7249
					}
7250
					if ($value == '' || $value == '-') {
7251
						$selectOptionValue = '&nbsp;';
7252
					}
7253
				}
7254
7255
				$out .= '<option value="'.$key.'"';
7256
				$out .= $style.$disabled;
7257
				if (is_array($id)) {
7258
					if (in_array($key, $id) && !$disabled) {
7259
						$out .= ' selected'; // To preselect a value
7260
					}
7261
				} else {
7262
					$id = (string) $id; // if $id = 0, then $id = '0'
7263
					if ($id != '' && $id == $key && !$disabled) {
7264
						$out .= ' selected'; // To preselect a value
7265
					}
7266
				}
7267
				if ($nohtmlescape) {
7268
					$out .= ' data-html="'.dol_escape_htmltag($selectOptionValue).'"';
7269
				}
7270
				if (is_array($tmpvalue)) {
7271
					foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
7272
						if (preg_match('/^data-/', $keyforvalue)) {
7273
							$out .= ' '.$keyforvalue.'="'.$valueforvalue.'"';
7274
						}
7275
					}
7276
				}
7277
				$out .= '>';
7278
				//var_dump($selectOptionValue);
7279
				$out .= $selectOptionValue;
7280
				$out .= "</option>\n";
7281
			}
7282
		}
7283
7284
		$out .= "</select>";
7285
		return $out;
7286
	}
7287
7288
7289
	/**
7290
	 *	Return a HTML select string, built from an array of key+value, but content returned into select come from an Ajax call of an URL.
7291
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7292
	 *
7293
	 *	@param	string	$htmlname       		Name of html select area
7294
	 *	@param	string	$url					Url. Must return a json_encode of array(key=>array('text'=>'A text', 'url'=>'An url'), ...)
7295
	 *	@param	string	$id             		Preselected key
7296
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7297
	 *	@param  string	$moreparamtourl 		Add more parameters onto the Ajax called URL
7298
	 * 	@param	int		$disabled				Html select box is disabled
7299
	 *  @param	int		$minimumInputLength		Minimum Input Length
7300
	 *  @param	string	$morecss				Add more class to css styles
7301
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7302
	 *  @param  string  $placeholder            String to use as placeholder
7303
	 *  @param  integer $acceptdelayedhtml      1 = caller is requesting to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
7304
	 * 	@return	string   						HTML select string
7305
	 *  @see selectArrayFilter(), ajax_combobox() in ajax.lib.php
7306
	 */
7307
	public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7308
	{
7309
		global $conf, $langs;
7310
		global $delayedhtmlcontent;	// Will be used later outside of this function
7311
7312
		// TODO Use an internal dolibarr component instead of select2
7313
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7314
			return '';
7315
		}
7316
7317
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"></select>';
7318
7319
		$outdelayed = '';
7320
		if (!empty($conf->use_javascript_ajax)) {
7321
			$tmpplugin = 'select2';
7322
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7323
		    	<script>
7324
		    	$(document).ready(function () {
7325
7326
	    	        '.($callurlonselect ? 'var saveRemoteData = [];' : '').'
7327
7328
	                $(".'.$htmlname.'").select2({
7329
				    	ajax: {
7330
					    	dir: "ltr",
7331
					    	url: "'.$url.'",
7332
					    	dataType: \'json\',
7333
					    	delay: 250,
7334
					    	data: function (params) {
7335
					    		return {
7336
							    	q: params.term, 	// search term
7337
					    			page: params.page
7338
					    		};
7339
				    		},
7340
				    		processResults: function (data) {
7341
				    			// parse the results into the format expected by Select2.
7342
				    			// since we are using custom formatting functions we do not need to alter the remote JSON data
7343
				    			//console.log(data);
7344
								saveRemoteData = data;
7345
					    	    /* format json result for select2 */
7346
					    	    result = []
7347
					    	    $.each( data, function( key, value ) {
7348
					    	       result.push({id: key, text: value.text});
7349
	                            });
7350
				    			//return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
7351
				    			//console.log(result);
7352
				    			return {results: result, more: false}
7353
				    		},
7354
				    		cache: true
7355
				    	},
7356
		 				language: select2arrayoflanguage,
7357
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7358
					    placeholder: "'.dol_escape_js($placeholder).'",
7359
				    	escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7360
				    	minimumInputLength: '.$minimumInputLength.',
7361
				        formatResult: function(result, container, query, escapeMarkup) {
7362
	                        return escapeMarkup(result.text);
7363
	                    },
7364
				    });
7365
7366
	                '.($callurlonselect ? '
7367
	                /* Code to execute a GET when we select a value */
7368
	                $(".'.$htmlname.'").change(function() {
7369
				    	var selected = $(".'.$htmlname.'").val();
7370
	                	console.log("We select in selectArrayAjax the entry "+selected)
7371
				        $(".'.$htmlname.'").val("");  /* reset visible combo value */
7372
	    			    $.each( saveRemoteData, function( key, value ) {
7373
	    				        if (key == selected)
7374
	    			            {
7375
	    			                 console.log("selectArrayAjax - Do a redirect to "+value.url)
7376
	    			                 location.assign(value.url);
7377
	    			            }
7378
	                    });
7379
	    			});' : '').'
7380
7381
	    	   });
7382
		       </script>';
7383
		}
7384
7385
		if ($acceptdelayedhtml) {
7386
			$delayedhtmlcontent .= $outdelayed;
7387
		} else {
7388
			$out .= $outdelayed;
7389
		}
7390
		return $out;
7391
	}
7392
7393
	/**
7394
	 *  Return a HTML select string, built from an array of key+value, but content returned into select is defined into $array parameter.
7395
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7396
	 *
7397
	 *  @param  string	$htmlname               Name of html select area
7398
	 *	@param	array	$array					Array (key=>array('text'=>'A text', 'url'=>'An url'), ...)
7399
	 *	@param	string	$id             		Preselected key
7400
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7401
	 *	@param	int		$disableFiltering		If set to 1, results are not filtered with searched string
7402
	 * 	@param	int		$disabled				Html select box is disabled
7403
	 *  @param	int		$minimumInputLength		Minimum Input Length
7404
	 *  @param	string	$morecss				Add more class to css styles
7405
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7406
	 *  @param  string  $placeholder            String to use as placeholder
7407
	 *  @param  integer $acceptdelayedhtml      1 = caller is requesting to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
7408
	 *  @return	string   						HTML select string
7409
	 *  @see selectArrayAjax(), ajax_combobox() in ajax.lib.php
7410
	 */
7411
	public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7412
	{
7413
		global $conf, $langs;
7414
		global $delayedhtmlcontent;	// Will be used later outside of this function
7415
7416
		// TODO Use an internal dolibarr component instead of select2
7417
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7418
			return '';
7419
		}
7420
7421
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"><option></option></select>';
7422
7423
		$formattedarrayresult = array();
7424
7425
		foreach ($array as $key => $value) {
7426
			$o = new stdClass();
7427
			$o->id = $key;
7428
			$o->text = $value['text'];
7429
			$o->url = $value['url'];
7430
			$formattedarrayresult[] = $o;
7431
		}
7432
7433
		$outdelayed = '';
7434
		if (!empty($conf->use_javascript_ajax)) {
7435
			$tmpplugin = 'select2';
7436
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7437
				<script>
7438
				$(document).ready(function () {
7439
					var data = '.json_encode($formattedarrayresult).';
7440
7441
					'.($callurlonselect ? 'var saveRemoteData = '.json_encode($array).';' : '').'
7442
7443
					$(".'.$htmlname.'").select2({
7444
						data: data,
7445
						language: select2arrayoflanguage,
7446
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7447
						placeholder: "'.dol_escape_js($placeholder).'",
7448
						escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7449
						minimumInputLength: '.$minimumInputLength.',
7450
						formatResult: function(result, container, query, escapeMarkup) {
7451
							return escapeMarkup(result.text);
7452
						},
7453
						matcher: function (params, data) {
7454
7455
							if(! data.id) return null;';
7456
7457
			if ($callurlonselect) {
7458
				$outdelayed .= '
7459
7460
							var urlBase = data.url;
7461
							var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
7462
							/* console.log("params.term="+params.term); */
7463
							/* console.log("params.term encoded="+encodeURIComponent(params.term)); */
7464
							saveRemoteData[data.id].url = urlBase + separ + "sall=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
7465
			}
7466
7467
			if (!$disableFiltering) {
7468
				$outdelayed .= '
7469
7470
							if(data.text.match(new RegExp(params.term))) {
7471
								return data;
7472
							}
7473
7474
							return null;';
7475
			} else {
7476
				$outdelayed .= '
7477
7478
							return data;';
7479
			}
7480
7481
			$outdelayed .= '
7482
						}
7483
					});
7484
7485
					'.($callurlonselect ? '
7486
					/* Code to execute a GET when we select a value */
7487
					$(".'.$htmlname.'").change(function() {
7488
						var selected = $(".'.$htmlname.'").val();
7489
						console.log("We select "+selected)
7490
7491
						$(".'.$htmlname.'").val("");  /* reset visible combo value */
7492
						$.each( saveRemoteData, function( key, value ) {
7493
							if (key == selected)
7494
							{
7495
								console.log("selectArrayFilter - Do a redirect to "+value.url)
7496
								location.assign(value.url);
7497
							}
7498
						});
7499
					});' : '').'
7500
7501
				});
7502
				</script>';
7503
		}
7504
7505
		if ($acceptdelayedhtml) {
7506
			$delayedhtmlcontent .= $outdelayed;
7507
		} else {
7508
			$out .= $outdelayed;
7509
		}
7510
		return $out;
7511
	}
7512
7513
	/**
7514
	 *	Show a multiselect form from an array. WARNING: Use this only for short lists.
7515
	 *
7516
	 *	@param	string	$htmlname		Name of select
7517
	 *	@param	array	$array			Array with key+value
7518
	 *	@param	array	$selected		Array with key+value preselected
7519
	 *	@param	int		$key_in_label   1 to show key like in "[key] value"
7520
	 *	@param	int		$value_as_key   1 to use value as key
7521
	 *	@param  string	$morecss        Add more css style
7522
	 *	@param  int		$translate		Translate and encode value
7523
	 *  @param	int		$width			Force width of select box. May be used only when using jquery couch. Example: 250, 95%
7524
	 *  @param	string	$moreattrib		Add more options on select component. Example: 'disabled'
7525
	 *  @param	string	$elemtype		Type of element we show ('category', ...). Will execute a formating function on it. To use in readonly mode if js component support HTML formatting.
7526
	 *  @param	string	$placeholder	String to use as placeholder
7527
	 *  @param	int		$addjscombo		Add js combo
7528
	 *	@return	string					HTML multiselect string
7529
	 *  @see selectarray(), selectArrayAjax(), selectArrayFilter()
7530
	 */
7531
	public static function multiselectarray($htmlname, $array, $selected = array(), $key_in_label = 0, $value_as_key = 0, $morecss = '', $translate = 0, $width = 0, $moreattrib = '', $elemtype = '', $placeholder = '', $addjscombo = -1)
7532
	{
7533
		global $conf, $langs;
7534
7535
		$out = '';
7536
7537
		if ($addjscombo < 0) {
7538
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7539
				$addjscombo = 1;
7540
			} else {
7541
				$addjscombo = 0;
7542
			}
7543
		}
7544
7545
		// Add code for jquery to use multiselect
7546
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
7547
			$out .= "\n".'<!-- JS CODE TO ENABLE select for id '.$htmlname.', addjscombo='.$addjscombo.' -->';
7548
			$out .= "\n".'<script>'."\n";
7549
			if ($addjscombo == 1) {
7550
				$tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
7551
				$out .= 'function formatResult(record) {'."\n";
7552
				if ($elemtype == 'category') {
7553
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
7554
				} else {
7555
					$out .= 'return record.text;';
7556
				}
7557
				$out .= '};'."\n";
7558
				$out .= 'function formatSelection(record) {'."\n";
7559
				if ($elemtype == 'category') {
7560
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
7561
				} else {
7562
					$out .= 'return record.text;';
7563
				}
7564
				$out .= '};'."\n";
7565
				$out .= '$(document).ready(function () {
7566
							$(\'#'.$htmlname.'\').'.$tmpplugin.'({
7567
								dir: \'ltr\',
7568
								// Specify format function for dropdown item
7569
								formatResult: formatResult,
7570
							 	templateResult: formatResult,		/* For 4.0 */
7571
								// Specify format function for selected item
7572
								formatSelection: formatSelection,
7573
							 	templateSelection: formatSelection		/* For 4.0 */
7574
							});
7575
7576
							/* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
7577
								 the size only if component is not hidden by default on load */
7578
							$(\'#'.$htmlname.' + .select2\').addClass(\''.$morecss.'\');
7579
						});'."\n";
7580
			} elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
7581
				// Add other js lib
7582
				// TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
7583
				// ...
7584
				$out .= 'console.log(\'addjscombo=2 for htmlname='.$htmlname.'\');';
7585
				$out .= '$(document).ready(function () {
7586
							$(\'#'.$htmlname.'\').multiSelect({
7587
								containerHTML: \'<div class="multi-select-container">\',
7588
								menuHTML: \'<div class="multi-select-menu">\',
7589
								buttonHTML: \'<span class="multi-select-button '.$morecss.'">\',
7590
								menuItemHTML: \'<label class="multi-select-menuitem">\',
7591
								activeClass: \'multi-select-container--open\',
7592
								noneText: \''.$placeholder.'\'
7593
							});
7594
						})';
7595
			}
7596
			$out .= '</script>';
7597
		}
7598
7599
		// Try also magic suggest
7600
		$out .= '<select id="'.$htmlname.'" class="multiselect'.($morecss ? ' '.$morecss : '').'" multiple name="'.$htmlname.'[]"'.($moreattrib ? ' '.$moreattrib : '').($width ? ' style="width: '.(preg_match('/%/', $width) ? $width : $width.'px').'"' : '').'>'."\n";
7601
		if (is_array($array) && !empty($array)) {
7602
			if ($value_as_key) {
7603
				$array = array_combine($array, $array);
7604
			}
7605
7606
			if (!empty($array)) {
7607
				foreach ($array as $key => $value) {
7608
					$newval = ($translate ? $langs->trans($value) : $value);
7609
					$newval = ($key_in_label ? $key.' - '.$newval : $newval);
7610
7611
					$out .= '<option value="'.$key.'"';
7612
					if (is_array($selected) && !empty($selected) && in_array((string) $key, $selected) && ((string) $key != '')) {
7613
						$out .= ' selected';
7614
					}
7615
					$out .= ' data-html="'.dol_escape_htmltag($newval).'"';
7616
					$out .= '>';
7617
					$out .= dol_htmlentitiesbr($newval);
7618
					$out .= '</option>'."\n";
7619
				}
7620
			}
7621
		}
7622
		$out .= '</select>'."\n";
7623
7624
		return $out;
7625
	}
7626
7627
7628
	/**
7629
	 *	Show a multiselect dropbox from an array. If a saved selection of fields exists for user (into $user->conf->MAIN_SELECTEDFIELDS_contextofpage), we use this one instead of default.
7630
	 *
7631
	 *	@param	string	$htmlname		Name of HTML field
7632
	 *	@param	array	$array			Array with array of fields we could show. This array may be modified according to setup of user.
7633
	 *  @param  string  $varpage        Id of context for page. Can be set by caller with $varpage=(empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage);
7634
	 *	@return	string					HTML multiselect string
7635
	 *  @see selectarray()
7636
	 */
7637
	public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage)
7638
	{
7639
		global $conf, $langs, $user, $extrafields;
7640
7641
		if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7642
			return '';
7643
		}
7644
7645
		$tmpvar = "MAIN_SELECTEDFIELDS_".$varpage; // To get list of saved selected fields to show
7646
7647
		if (!empty($user->conf->$tmpvar)) {		// A list of fields was already customized for user
7648
			$tmparray = explode(',', $user->conf->$tmpvar);
7649
			foreach ($array as $key => $val) {
7650
				//var_dump($key);
7651
				//var_dump($tmparray);
7652
				if (in_array($key, $tmparray)) {
7653
					$array[$key]['checked'] = 1;
7654
				} else {
7655
					$array[$key]['checked'] = 0;
7656
				}
7657
			}
7658
		} else {								// There is no list of fields already customized for user
7659
			foreach ($array as $key => $val) {
7660
				if ($array[$key]['checked'] < 0) {
7661
					$array[$key]['checked'] = 0;
7662
				}
7663
			}
7664
		}
7665
7666
		$listoffieldsforselection = '';
7667
		$listcheckedstring = '';
7668
7669
		foreach ($array as $key => $val) {
7670
			/* var_dump($val);
7671
			var_dump(array_key_exists('enabled', $val));
7672
			var_dump(!$val['enabled']);*/
7673
			if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
7674
				unset($array[$key]); // We don't want this field
7675
				continue;
7676
			}
7677
			if (!empty($val['type']) && $val['type'] == 'separate') {
7678
				// Field remains in array but we don't add it into $listoffieldsforselection
7679
				//$listoffieldsforselection .= '<li>-----</li>';
7680
				continue;
7681
			}
7682
			if ($val['label']) {
7683
				if (!empty($val['langfile']) && is_object($langs)) {
7684
					$langs->load($val['langfile']);
7685
				}
7686
7687
				// Note: $val['checked'] <> 0 means we must show the field into the combo list
7688
				$listoffieldsforselection .= '<li><input type="checkbox" id="checkbox'.$key.'" value="'.$key.'"'.((empty($val['checked']) && $val['checked'] != '-1') ? '' : ' checked="checked"').'/><label for="checkbox'.$key.'">'.dol_escape_htmltag($langs->trans($val['label'])).'</label></li>';
7689
				$listcheckedstring .= (empty($val['checked']) ? '' : $key.',');
7690
			}
7691
		}
7692
7693
		$out = '<!-- Component multiSelectArrayWithCheckbox '.$htmlname.' -->
7694
7695
        <dl class="dropdown">
7696
            <dt>
7697
            <a href="#'.$htmlname.'">
7698
              '.img_picto('', 'list').'
7699
            </a>
7700
            <input type="hidden" class="'.$htmlname.'" name="'.$htmlname.'" value="'.$listcheckedstring.'">
7701
            </dt>
7702
            <dd class="dropdowndd">
7703
                <div class="multiselectcheckbox'.$htmlname.'">
7704
                    <ul class="ul'.$htmlname.'">
7705
                    '.$listoffieldsforselection.'
7706
                    </ul>
7707
                </div>
7708
            </dd>
7709
        </dl>
7710
7711
        <script type="text/javascript">
7712
          jQuery(document).ready(function () {
7713
              $(\'.multiselectcheckbox'.$htmlname.' input[type="checkbox"]\').on(\'click\', function () {
7714
                  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
7715
7716
                  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\');	// Update field so we know we changed something on selected fields after POST
7717
7718
                  var title = $(this).val() + ",";
7719
                  if ($(this).is(\':checked\')) {
7720
                      $(\'.'.$htmlname.'\').val(title + $(\'.'.$htmlname.'\').val());
7721
                  }
7722
                  else {
7723
                      $(\'.'.$htmlname.'\').val( $(\'.'.$htmlname.'\').val().replace(title, \'\') )
7724
                  }
7725
                  // Now, we submit page
7726
                  //$(this).parents(\'form:first\').submit();
7727
              });
7728
7729
7730
           });
7731
        </script>
7732
7733
        ';
7734
		return $out;
7735
	}
7736
7737
	/**
7738
	 * 	Render list of categories linked to object with id $id and type $type
7739
	 *
7740
	 * 	@param		int		$id				Id of object
7741
	 * 	@param		string	$type			Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
7742
	 *  @param		int		$rendermode		0=Default, use multiselect. 1=Emulate multiselect (recommended)
7743
	 *  @param		int		$nolink			1=Do not add html links
7744
	 * 	@return		string					String with categories
7745
	 */
7746
	public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
7747
	{
7748
		global $db;
7749
7750
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7751
7752
		$cat = new Categorie($db);
7753
		$categories = $cat->containing($id, $type);
7754
7755
		if ($rendermode == 1) {
7756
			$toprint = array();
7757
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7758
				$ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text
7759
				foreach ($ways as $way) {
7760
					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.$way.'</li>';
7761
				}
7762
			}
7763
			return '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7764
		}
7765
7766
		if ($rendermode == 0) {
7767
			$arrayselected = array();
7768
			$cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 1);
7769
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7770
				$arrayselected[] = $c->id;
7771
			}
7772
7773
			return $this->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, '', 0, '100%', 'disabled', 'category');
7774
		}
7775
7776
		return 'ErrorBadValueForParameterRenderMode'; // Should not happened
7777
	}
7778
7779
	/**
7780
	 *  Show linked object block.
7781
	 *
7782
	 *  @param	CommonObject	$object		      Object we want to show links to
7783
	 *  @param  string          $morehtmlright    More html to show on right of title
7784
	 *  @param  array           $compatibleImportElementsList  Array of compatibles elements object for "import from" action
7785
	 *  @return	int							      <0 if KO, >=0 if OK
7786
	 */
7787
	public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = false)
7788
	{
7789
		global $conf, $langs, $hookmanager;
7790
		global $bc, $action;
7791
7792
		$object->fetchObjectLinked();
7793
7794
		// Bypass the default method
7795
		$hookmanager->initHooks(array('commonobject'));
7796
		$parameters = array(
7797
			'morehtmlright' => $morehtmlright,
7798
			'compatibleImportElementsList' => &$compatibleImportElementsList,
7799
		);
7800
		$reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
7801
7802
		if (empty($reshook)) {
7803
			$nbofdifferenttypes = count($object->linkedObjects);
7804
7805
			print '<!-- showLinkedObjectBlock -->';
7806
			print load_fiche_titre($langs->trans('RelatedObjects'), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
7807
7808
7809
			print '<div class="div-table-responsive-no-min">';
7810
			print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="'.$object->element.'"  data-elementid="'.$object->id.'"   >';
7811
7812
			print '<tr class="liste_titre">';
7813
			print '<td>'.$langs->trans("Type").'</td>';
7814
			print '<td>'.$langs->trans("Ref").'</td>';
7815
			print '<td class="center"></td>';
7816
			print '<td class="center">'.$langs->trans("Date").'</td>';
7817
			print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
7818
			print '<td class="right">'.$langs->trans("Status").'</td>';
7819
			print '<td></td>';
7820
			print '</tr>';
7821
7822
			$nboftypesoutput = 0;
7823
7824
			foreach ($object->linkedObjects as $objecttype => $objects) {
7825
				$tplpath = $element = $subelement = $objecttype;
7826
7827
				// to display inport button on tpl
7828
				$showImportButton = false;
7829
				if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
7830
					$showImportButton = true;
7831
				}
7832
7833
				$regs = array();
7834
				if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
7835
					$element = $regs[1];
7836
					$subelement = $regs[2];
7837
					$tplpath = $element.'/'.$subelement;
7838
				}
7839
				$tplname = 'linkedobjectblock';
7840
7841
				// To work with non standard path
7842
				if ($objecttype == 'facture') {
7843
					$tplpath = 'compta/'.$element;
7844
					if (empty($conf->facture->enabled)) {
7845
						continue; // Do not show if module disabled
7846
					}
7847
				} elseif ($objecttype == 'facturerec') {
7848
					$tplpath = 'compta/facture';
7849
					$tplname = 'linkedobjectblockForRec';
7850
					if (empty($conf->facture->enabled)) {
7851
						continue; // Do not show if module disabled
7852
					}
7853
				} elseif ($objecttype == 'propal') {
7854
					$tplpath = 'comm/'.$element;
7855
					if (empty($conf->propal->enabled)) {
7856
						continue; // Do not show if module disabled
7857
					}
7858
				} elseif ($objecttype == 'supplier_proposal') {
7859
					if (empty($conf->supplier_proposal->enabled)) {
7860
						continue; // Do not show if module disabled
7861
					}
7862
				} elseif ($objecttype == 'shipping' || $objecttype == 'shipment') {
7863
					$tplpath = 'expedition';
7864
					if (empty($conf->expedition->enabled)) {
7865
						continue; // Do not show if module disabled
7866
					}
7867
				} elseif ($objecttype == 'reception') {
7868
					$tplpath = 'reception';
7869
					if (empty($conf->reception->enabled)) {
7870
						continue; // Do not show if module disabled
7871
					}
7872
				} elseif ($objecttype == 'delivery') {
7873
					$tplpath = 'delivery';
7874
					if (empty($conf->expedition->enabled)) {
7875
						continue; // Do not show if module disabled
7876
					}
7877
				} elseif ($objecttype == 'ficheinter') {
7878
					$tplpath = 'fichinter';
7879
					if (empty($conf->ficheinter->enabled)) {
7880
						continue; // Do not show if module disabled
7881
					}
7882
				} elseif ($objecttype == 'invoice_supplier') {
7883
					$tplpath = 'fourn/facture';
7884
				} elseif ($objecttype == 'order_supplier') {
7885
					$tplpath = 'fourn/commande';
7886
				} elseif ($objecttype == 'expensereport') {
7887
					$tplpath = 'expensereport';
7888
				} elseif ($objecttype == 'subscription') {
7889
					$tplpath = 'adherents';
7890
				} elseif ($objecttype == 'conferenceorbooth') {
7891
					$tplpath = 'eventorganization';
7892
				} elseif ($objecttype == 'conferenceorboothattendee') {
7893
					$tplpath = 'eventorganization';
7894
				} elseif ($objecttype == 'mo') {
7895
					$tplpath = 'mrp';
7896
					if (empty($conf->mrp->enabled)) {
7897
						continue; // Do not show if module disabled
7898
					}
7899
				}
7900
7901
				global $linkedObjectBlock;
7902
				$linkedObjectBlock = $objects;
7903
7904
				// Output template part (modules that overwrite templates must declare this into descriptor)
7905
				$dirtpls = array_merge($conf->modules_parts['tpl'], array('/'.$tplpath.'/tpl'));
7906
				foreach ($dirtpls as $reldir) {
7907
					if ($nboftypesoutput == ($nbofdifferenttypes - 1)) {    // No more type to show after
7908
						global $noMoreLinkedObjectBlockAfter;
7909
						$noMoreLinkedObjectBlockAfter = 1;
7910
					}
7911
7912
					$res = @include dol_buildpath($reldir.'/'.$tplname.'.tpl.php');
7913
					if ($res) {
7914
						$nboftypesoutput++;
7915
						break;
7916
					}
7917
				}
7918
			}
7919
7920
			if (!$nboftypesoutput) {
7921
				print '<tr><td class="impair opacitymedium" colspan="7">'.$langs->trans("None").'</td></tr>';
7922
			}
7923
7924
			print '</table>';
7925
7926
			if (!empty($compatibleImportElementsList)) {
7927
				$res = @include dol_buildpath('core/tpl/ajax/objectlinked_lineimport.tpl.php');
7928
			}
7929
7930
7931
			print '</div>';
7932
7933
			return $nbofdifferenttypes;
7934
		}
7935
	}
7936
7937
	/**
7938
	 *  Show block with links to link to other objects.
7939
	 *
7940
	 *  @param	CommonObject	$object				Object we want to show links to
7941
	 *  @param	array			$restrictlinksto	Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
7942
	 *  @param	array			$excludelinksto		Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
7943
	 *  @return	string								<0 if KO, >0 if OK
7944
	 */
7945
	public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
7946
	{
7947
		global $conf, $langs, $hookmanager;
7948
		global $bc, $action;
7949
7950
		$linktoelem = '';
7951
		$linktoelemlist = '';
7952
		$listofidcompanytoscan = '';
7953
7954
		if (!is_object($object->thirdparty)) {
7955
			$object->fetch_thirdparty();
7956
		}
7957
7958
		$possiblelinks = array();
7959
		if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
7960
			$listofidcompanytoscan = $object->thirdparty->id;
7961
			if (($object->thirdparty->parent > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PARENT_IN_LINKTO)) {
7962
				$listofidcompanytoscan .= ','.$object->thirdparty->parent;
7963
			}
7964
			if (($object->fk_project > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO)) {
7965
				include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
7966
				$tmpproject = new Project($this->db);
7967
				$tmpproject->fetch($object->fk_project);
7968
				if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) {
7969
					$listofidcompanytoscan .= ','.$tmpproject->socid;
7970
				}
7971
				unset($tmpproject);
7972
			}
7973
7974
			$possiblelinks = array(
7975
				'propal'=>array('enabled'=>$conf->propal->enabled, 'perms'=>1, 'label'=>'LinkToProposal', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7976
				'order'=>array('enabled'=>$conf->commande->enabled, 'perms'=>1, 'label'=>'LinkToOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7977
				'invoice'=>array('enabled'=>$conf->facture->enabled, 'perms'=>1, 'label'=>'LinkToInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7978
				'invoice_template'=>array('enabled'=>$conf->facture->enabled, 'perms'=>1, 'label'=>'LinkToTemplateInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.titre as ref, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7979
				'contrat'=>array(
7980
					'enabled'=>$conf->contrat->enabled,
7981
					'perms'=>1,
7982
					'label'=>'LinkToContract',
7983
					'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 FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."contrat as t, ".MAIN_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'
7984
				),
7985
				'fichinter'=>array('enabled'=>$conf->ficheinter->enabled, 'perms'=>1, 'label'=>'LinkToIntervention', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7986
				'supplier_proposal'=>array('enabled'=>$conf->supplier_proposal->enabled, 'perms'=>1, 'label'=>'LinkToSupplierProposal', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, '' as ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7987
				'order_supplier'=>array('enabled'=>$conf->supplier_order->enabled, 'perms'=>1, 'label'=>'LinkToSupplierOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7988
				'invoice_supplier'=>array('enabled'=>$conf->supplier_invoice->enabled, 'perms'=>1, 'label'=>'LinkToSupplierInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7989
				'ticket'=>array('enabled'=>$conf->ticket->enabled, 'perms'=>1, 'label'=>'LinkToTicket', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.track_id, '0' as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_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').')'),
7990
				'mo'=>array('enabled'=>$conf->mrp->enabled, 'perms'=>1, 'label'=>'LinkToMO', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM ".MAIN_DB_PREFIX."societe as s INNER JOIN ".MAIN_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').')')
7991
			);
7992
		}
7993
7994
		// Can complete the possiblelink array
7995
		$hookmanager->initHooks(array('commonobject'));
7996
		$parameters = array('listofidcompanytoscan' => $listofidcompanytoscan);
7997
7998
		if (!empty($listofidcompanytoscan)) {  // If empty, we don't have criteria to scan the object we can link to
7999
			$reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
8000
		}
8001
8002
		if (empty($reshook)) {
8003
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
8004
				$possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
8005
			}
8006
		} elseif ($reshook > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $reshook does not seem to be defined for all execution paths leading up to this point.
Loading history...
8007
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
8008
				$possiblelinks = $hookmanager->resArray;
8009
			}
8010
		}
8011
8012
		foreach ($possiblelinks as $key => $possiblelink) {
8013
			$num = 0;
8014
8015
			if (empty($possiblelink['enabled'])) {
8016
				continue;
8017
			}
8018
8019
			if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
8020
				print '<div id="'.$key.'list"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display:none"').'>';
8021
8022
				if (!empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8023
					print '<br><form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
8024
					print '<input type="hidden" name="id" value="' . $object->id . '">';
8025
					print '<input type="hidden" name="action" value="addlinkbyref">';
8026
					print '<input type="hidden" name="addlink" value="' . $key . '">';
8027
					print '<table class="noborder">';
8028
					print '<tr>';
8029
					print '<td>' . $langs->trans("Ref") . '</td>';
8030
					print '<td><input type="text" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">&nbsp;<input type="submit" class="button valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;<input type="submit" class="button" name="cancel" value="' . $langs->trans('Cancel') . '"></td>';
8031
					print '</tr>';
8032
					print '</table>';
8033
					print '</form>';
8034
				}
8035
8036
				$sql = $possiblelink['sql'];
8037
8038
				$resqllist = $this->db->query($sql);
8039
				if ($resqllist) {
8040
					$num = $this->db->num_rows($resqllist);
8041
					$i = 0;
8042
8043
					print '<br>';
8044
					print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
8045
					print '<input type="hidden" name="action" value="addlink">';
8046
					print '<input type="hidden" name="token" value="'.newToken().'">';
8047
					print '<input type="hidden" name="id" value="'.$object->id.'">';
8048
					print '<input type="hidden" name="addlink" value="'.$key.'">';
8049
					print '<table class="noborder">';
8050
					print '<tr class="liste_titre">';
8051
					print '<td class="nowrap"></td>';
8052
					print '<td class="center">'.$langs->trans("Ref").'</td>';
8053
					print '<td class="left">'.$langs->trans("RefCustomer").'</td>';
8054
					print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
8055
					print '<td class="left">'.$langs->trans("Company").'</td>';
8056
					print '</tr>';
8057
					while ($i < $num) {
8058
						$objp = $this->db->fetch_object($resqllist);
8059
8060
						print '<tr class="oddeven">';
8061
						print '<td class="left">';
8062
						print '<input type="radio" name="idtolinkto" id="'.$key.'_'.$objp->rowid.'" value="'.$objp->rowid.'">';
8063
						print '</td>';
8064
						print '<td class="center"><label for="'.$key.'_'.$objp->rowid.'">'.$objp->ref.'</label></td>';
8065
						print '<td>'.(!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')).'</td>';
8066
						print '<td class="right">';
8067
						if ($possiblelink['label'] == 'LinkToContract') {
8068
							$form = new Form($this->db);
8069
							print $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")).' ';
8070
						}
8071
						print '<span class="amount">'.price($objp->total_ht).'</span>';
8072
						print '</td>';
8073
						print '<td>'.$objp->name.'</td>';
8074
						print '</tr>';
8075
						$i++;
8076
					}
8077
					print '</table>';
8078
					print '<div class="center">';
8079
					print '<input type="submit" class="button valignmiddle marginleftonly marginrightonly" value="'.$langs->trans('ToLink').'">';
8080
					if (empty($conf->use_javascript_ajax)) {
8081
						print '<input type="submit" class="button button-cancel marginleftonly marginrightonly" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
8082
					} else {
8083
						print '<input type="submit"; onclick="javascript:jQuery(\'#'.$key.'list\').toggle(); return false;" class="button button-cancel marginleftonly marginrightonly" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
8084
					}
8085
					print '</form>';
8086
					$this->db->free($resqllist);
8087
				} else {
8088
					dol_print_error($this->db);
8089
				}
8090
				print '</div>';
8091
8092
				//$linktoelem.=($linktoelem?' &nbsp; ':'');
8093
				if ($num > 0 || !empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8094
					$linktoelemlist .= '<li><a href="#linkto'.$key.'" class="linkto dropdowncloseonclick" rel="'.$key.'">'.$langs->trans($possiblelink['label']).' ('.$num.')</a></li>';
8095
					// } else $linktoelem.=$langs->trans($possiblelink['label']);
8096
				} else {
8097
					$linktoelemlist .= '<li><span class="linktodisabled">'.$langs->trans($possiblelink['label']).' (0)</span></li>';
8098
				}
8099
			}
8100
		}
8101
8102
		if ($linktoelemlist) {
8103
			$linktoelem = '
8104
    		<dl class="dropdown" id="linktoobjectname">
8105
    		';
8106
			if (!empty($conf->use_javascript_ajax)) {
8107
				$linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>'.$langs->trans("LinkTo").'...</a></dt>';
8108
			}
8109
			$linktoelem .= '<dd>
8110
    		<div class="multiselectlinkto">
8111
    		<ul class="ulselectedfields">'.$linktoelemlist.'
8112
    		</ul>
8113
    		</div>
8114
    		</dd>
8115
    		</dl>';
8116
		} else {
8117
			$linktoelem = '';
8118
		}
8119
8120
		if (!empty($conf->use_javascript_ajax)) {
8121
			print '<!-- Add js to show linkto box -->
8122
				<script>
8123
				jQuery(document).ready(function() {
8124
					jQuery(".linkto").click(function() {
8125
						console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
8126
					    jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
8127
					});
8128
				});
8129
				</script>
8130
		    ';
8131
		}
8132
8133
		return $linktoelem;
8134
	}
8135
8136
	/**
8137
	 *	Return an html string with a select combo box to choose yes or no
8138
	 *
8139
	 *	@param	string		$htmlname		Name of html select field
8140
	 *	@param	string		$value			Pre-selected value
8141
	 *	@param	int			$option			0 return yes/no, 1 return 1/0
8142
	 *	@param	bool		$disabled		true or false
8143
	 *  @param	int      	$useempty		1=Add empty line
8144
	 *  @param	int			$addjscombo		1=Add js beautifier on combo box
8145
	 *  @param	string		$morecss		More CSS
8146
	 *	@return	string						See option
8147
	 */
8148
	public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = '')
8149
	{
8150
		global $langs;
8151
8152
		$yes = "yes";
8153
		$no = "no";
8154
		if ($option) {
8155
			$yes = "1";
8156
			$no = "0";
8157
		}
8158
8159
		$disabled = ($disabled ? ' disabled' : '');
8160
8161
		$resultyesno = '<select class="flat width75'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'"'.$disabled.'>'."\n";
8162
		if ($useempty) {
8163
			$resultyesno .= '<option value="-1"'.(($value < 0) ? ' selected' : '').'>&nbsp;</option>'."\n";
8164
		}
8165
		if (("$value" == 'yes') || ($value == 1)) {
8166
			$resultyesno .= '<option value="'.$yes.'" selected>'.$langs->trans("Yes").'</option>'."\n";
8167
			$resultyesno .= '<option value="'.$no.'">'.$langs->trans("No").'</option>'."\n";
8168
		} else {
8169
			$selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
8170
			$resultyesno .= '<option value="'.$yes.'">'.$langs->trans("Yes").'</option>'."\n";
8171
			$resultyesno .= '<option value="'.$no.'"'.$selected.'>'.$langs->trans("No").'</option>'."\n";
8172
		}
8173
		$resultyesno .= '</select>'."\n";
8174
8175
		if ($addjscombo) {
8176
			$resultyesno .= ajax_combobox($htmlname);
8177
		}
8178
8179
		return $resultyesno;
8180
	}
8181
8182
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8183
	/**
8184
	 *  Return list of export templates
8185
	 *
8186
	 *  @param	string	$selected          Id modele pre-selectionne
8187
	 *  @param  string	$htmlname          Name of HTML select
8188
	 *  @param  string	$type              Type of searched templates
8189
	 *  @param  int		$useempty          Affiche valeur vide dans liste
8190
	 *  @return	void
8191
	 */
8192
	public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
8193
	{
8194
		// phpcs:enable
8195
		$sql = "SELECT rowid, label";
8196
		$sql .= " FROM ".MAIN_DB_PREFIX."export_model";
8197
		$sql .= " WHERE type = '".$this->db->escape($type)."'";
8198
		$sql .= " ORDER BY rowid";
8199
		$result = $this->db->query($sql);
8200
		if ($result) {
8201
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
8202
			if ($useempty) {
8203
				print '<option value="-1">&nbsp;</option>';
8204
			}
8205
8206
			$num = $this->db->num_rows($result);
8207
			$i = 0;
8208
			while ($i < $num) {
8209
				$obj = $this->db->fetch_object($result);
8210
				if ($selected == $obj->rowid) {
8211
					print '<option value="'.$obj->rowid.'" selected>';
8212
				} else {
8213
					print '<option value="'.$obj->rowid.'">';
8214
				}
8215
				print $obj->label;
8216
				print '</option>';
8217
				$i++;
8218
			}
8219
			print "</select>";
8220
		} else {
8221
			dol_print_error($this->db);
8222
		}
8223
	}
8224
8225
	/**
8226
	 *    Return a HTML area with the reference of object and a navigation bar for a business object
8227
	 *    Note: To complete search with a particular filter on select, you can set $object->next_prev_filter set to define SQL criterias.
8228
	 *
8229
	 *    @param	object	$object			Object to show.
8230
	 *    @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link.
8231
	 *    @param	string	$morehtml  		More html content to output just before the nav bar.
8232
	 *    @param	int		$shownav	  	Show Condition (navigation is shown if value is 1).
8233
	 *    @param	string	$fieldid   		Name of field id into database to use for select next and previous (we make the select max and min on this field compared to $object->ref). Use 'none' to disable next/prev.
8234
	 *    @param	string	$fieldref   	Name of field ref of object (object->ref) to show or 'none' to not show ref.
8235
	 *    @param	string	$morehtmlref  	More html to show after ref.
8236
	 *    @param	string	$moreparam  	More param to add in nav link url. Must start with '&...'.
8237
	 *	  @param	int		$nodbprefix		Do not include DB prefix to forge table name.
8238
	 *	  @param	string	$morehtmlleft	More html code to show before ref.
8239
	 *	  @param	string	$morehtmlstatus	More html code to show under navigation arrows (status place).
8240
	 *	  @param	string	$morehtmlright	More html code to show after ref.
8241
	 * 	  @return	string    				Portion HTML with ref + navigation buttons
8242
	 */
8243
	public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
8244
	{
8245
		global $langs, $conf, $hookmanager, $extralanguages;
8246
8247
		$ret = '';
8248
		if (empty($fieldid)) {
8249
			$fieldid = 'rowid';
8250
		}
8251
		if (empty($fieldref)) {
8252
			$fieldref = 'ref';
8253
		}
8254
8255
		// Preparing gender's display if there is one
8256
		$addgendertxt = '';
8257
		if (!empty($object->gender)) {
8258
			$addgendertxt = ' ';
8259
			switch ($object->gender) {
8260
				case 'man':
8261
					$addgendertxt .= '<i class="fas fa-mars"></i>';
8262
					break;
8263
				case 'woman':
8264
					$addgendertxt .= '<i class="fas fa-venus"></i>';
8265
					break;
8266
				case 'other':
8267
					$addgendertxt .= '<i class="fas fa-genderless"></i>';
8268
					break;
8269
			}
8270
		}
8271
8272
		// Add where from hooks
8273
		if (is_object($hookmanager)) {
8274
			$parameters = array();
8275
			$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
8276
			$object->next_prev_filter .= $hookmanager->resPrint;
8277
		}
8278
		$previous_ref = $next_ref = '';
8279
		if ($shownav) {
8280
			//print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
8281
			$object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
8282
8283
			$navurl = $_SERVER["PHP_SELF"];
8284
			// Special case for project/task page
8285
			if ($paramid == 'project_ref') {
8286
				if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) {     // TODO Remove this when nav with project_ref on task pages are ok
8287
					$navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
8288
					$paramid = 'ref';
8289
				}
8290
			}
8291
8292
			// accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
8293
			// accesskey is for Mac:               CTRL + key for all browsers
8294
			$stringforfirstkey = $langs->trans("KeyboardShortcut");
8295
			if ($conf->browser->name == 'chrome') {
8296
				$stringforfirstkey .= ' ALT +';
8297
			} elseif ($conf->browser->name == 'firefox') {
8298
				$stringforfirstkey .= ' ALT + SHIFT +';
8299
			} else {
8300
				$stringforfirstkey .= ' CTL +';
8301
			}
8302
8303
			$previous_ref = $object->ref_previous ? '<a accesskey="p" title="'.$stringforfirstkey.' p" class="classfortooltip" 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>';
8304
			$next_ref     = $object->ref_next ? '<a accesskey="n" title="'.$stringforfirstkey.' n" class="classfortooltip" 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>';
8305
		}
8306
8307
		//print "xx".$previous_ref."x".$next_ref;
8308
		$ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
8309
8310
		// Right part of banner
8311
		if ($morehtmlright) {
8312
			$ret .= '<div class="inline-block floatleft">'.$morehtmlright.'</div>';
8313
		}
8314
8315
		if ($previous_ref || $next_ref || $morehtml) {
8316
			$ret .= '<div class="pagination paginationref"><ul class="right">';
8317
		}
8318
		if ($morehtml) {
8319
			$ret .= '<li class="noborder litext'.(($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '').'">'.$morehtml.'</li>';
8320
		}
8321
		if ($shownav && ($previous_ref || $next_ref)) {
8322
			$ret .= '<li class="pagination">'.$previous_ref.'</li>';
8323
			$ret .= '<li class="pagination">'.$next_ref.'</li>';
8324
		}
8325
		if ($previous_ref || $next_ref || $morehtml) {
8326
			$ret .= '</ul></div>';
8327
		}
8328
8329
		$parameters = array();
8330
		$reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
8331
		if (empty($reshook)) {
8332
			$morehtmlstatus .= $hookmanager->resPrint;
8333
		} else {
8334
			$morehtmlstatus = $hookmanager->resPrint;
8335
		}
8336
		if ($morehtmlstatus) {
8337
			$ret .= '<div class="statusref">'.$morehtmlstatus.'</div>';
8338
		}
8339
8340
		$parameters = array();
8341
		$reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
8342
		if (empty($reshook)) {
8343
			$morehtmlref .= $hookmanager->resPrint;
8344
		} elseif ($reshook > 0) {
8345
			$morehtmlref = $hookmanager->resPrint;
8346
		}
8347
8348
		// Left part of banner
8349
		if ($morehtmlleft) {
8350
			if ($conf->browser->layout == 'phone') {
8351
				$ret .= '<!-- morehtmlleft --><div class="floatleft">'.$morehtmlleft.'</div>'; // class="center" to have photo in middle
8352
			} else {
8353
				$ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">'.$morehtmlleft.'</div>';
8354
			}
8355
		}
8356
8357
		//if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
8358
		$ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid'.(($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '').'">';
8359
8360
		// For thirdparty, contact, user, member, the ref is the id, so we show something else
8361
		if ($object->element == 'societe') {
8362
			$ret .= dol_htmlentities($object->name);
8363
8364
			// List of extra languages
8365
			$arrayoflangcode = array();
8366
			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
8367
				$arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
8368
			}
8369
8370
			if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
8371
				if (!is_object($extralanguages)) {
8372
					include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
8373
					$extralanguages = new ExtraLanguages($this->db);
8374
				}
8375
				$extralanguages->fetch_name_extralanguages('societe');
8376
8377
				if (!empty($extralanguages->attributes['societe']['name'])) {
8378
					$object->fetchValuesForExtraLanguages();
8379
8380
					$htmltext = '';
8381
					// If there is extra languages
8382
					foreach ($arrayoflangcode as $extralangcode) {
8383
						$htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
8384
						if ($object->array_languages['name'][$extralangcode]) {
8385
							$htmltext .= $object->array_languages['name'][$extralangcode];
8386
						} else {
8387
							$htmltext .= '<span class="opacitymedium">'.$langs->trans("SwitchInEditModeToAddTranslation").'</span>';
8388
						}
8389
					}
8390
					$ret .= '<!-- Show translations of name -->'."\n";
8391
					$ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
8392
				}
8393
			}
8394
		} elseif ($object->element == 'member') {
8395
			$ret .= $object->ref.'<br>';
8396
			$fullname = $object->getFullName($langs);
8397
			if ($object->morphy == 'mor' && $object->societe) {
8398
				$ret .= dol_htmlentities($object->societe).((!empty($fullname) && $object->societe != $fullname) ? ' ('.dol_htmlentities($fullname).$addgendertxt.')' : '');
8399
			} else {
8400
				$ret .= dol_htmlentities($fullname).$addgendertxt.((!empty($object->societe) && $object->societe != $fullname) ? ' ('.dol_htmlentities($object->societe).')' : '');
8401
			}
8402
		} elseif (in_array($object->element, array('contact', 'user', 'usergroup'))) {
8403
			$ret .= dol_htmlentities($object->getFullName($langs)).$addgendertxt;
8404
		} elseif (in_array($object->element, array('action', 'agenda'))) {
8405
			$ret .= $object->ref.'<br>'.$object->label;
8406
		} elseif (in_array($object->element, array('adherent_type'))) {
8407
			$ret .= $object->label;
8408
		} elseif ($object->element == 'ecm_directories') {
8409
			$ret .= '';
8410
		} elseif ($fieldref != 'none') {
8411
			$ret .= dol_htmlentities($object->$fieldref);
8412
		}
8413
8414
		if ($morehtmlref) {
8415
			// don't add a additional space, when "$morehtmlref" starts with a HTML div tag
8416
			if (substr($morehtmlref, 0, 4) != '<div') {
8417
				$ret .= ' ';
8418
			}
8419
8420
			$ret .= $morehtmlref;
8421
		}
8422
8423
		$ret .= '</div>';
8424
8425
		$ret .= '</div><!-- End banner content -->';
8426
8427
		return $ret;
8428
	}
8429
8430
8431
	/**
8432
	 *  Return HTML code to output a barcode
8433
	 *
8434
	 *  @param	Object	$object			Object containing data to retrieve file name
8435
	 * 	@param	int		$width			Width of photo
8436
	 * 	@param	string	$morecss		More CSS on img of barcode
8437
	 * 	@return string    				HTML code to output barcode
8438
	 */
8439
	public function showbarcode(&$object, $width = 100, $morecss = '')
8440
	{
8441
		global $conf;
8442
8443
		//Check if barcode is filled in the card
8444
		if (empty($object->barcode)) {
8445
			return '';
8446
		}
8447
8448
		// Complete object if not complete
8449
		if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
8450
			$result = $object->fetch_barcode();
8451
			//Check if fetch_barcode() failed
8452
			if ($result < 1) {
8453
				return '<!-- ErrorFetchBarcode -->';
8454
			}
8455
		}
8456
8457
		// Barcode image
8458
		$url = DOL_URL_ROOT.'/viewimage.php?modulepart=barcode&generator='.urlencode($object->barcode_type_coder).'&code='.urlencode($object->barcode).'&encoding='.urlencode($object->barcode_type_code);
8459
		$out = '<!-- url barcode = '.$url.' -->';
8460
		$out .= '<img src="'.$url.'"'.($morecss ? ' class="'.$morecss.'"' : '').'>';
8461
		return $out;
8462
	}
8463
8464
	/**
8465
	 *    	Return HTML code to output a photo
8466
	 *
8467
	 *    	@param	string		$modulepart			Key to define module concerned ('societe', 'userphoto', 'memberphoto')
8468
	 *     	@param  object		$object				Object containing data to retrieve file name
8469
	 * 		@param	int			$width				Width of photo
8470
	 * 		@param	int			$height				Height of photo (auto if 0)
8471
	 * 		@param	int			$caneditfield		Add edit fields
8472
	 * 		@param	string		$cssclass			CSS name to use on img for photo
8473
	 * 		@param	string		$imagesize		    'mini', 'small' or '' (original)
8474
	 *      @param  int         $addlinktofullsize  Add link to fullsize image
8475
	 *      @param  int         $cache              1=Accept to use image in cache
8476
	 *      @param	string		$forcecapture		'', 'user' or 'environment'. Force parameter capture on HTML input file element to ask a smartphone to allow to open camera to take photo. Auto if ''.
8477
	 *      @param	int			$noexternsourceoverwrite	No overwrite image with extern source (like 'gravatar' or other module)
8478
	 * 	  	@return string    						HTML code to output photo
8479
	 */
8480
	public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
8481
	{
8482
		global $conf, $langs;
8483
8484
		$entity = (!empty($object->entity) ? $object->entity : $conf->entity);
8485
		$id = (!empty($object->id) ? $object->id : $object->rowid);
8486
8487
		$ret = '';
8488
		$dir = '';
8489
		$file = '';
8490
		$originalfile = '';
8491
		$altfile = '';
8492
		$email = '';
8493
		$capture = '';
8494
		if ($modulepart == 'societe') {
8495
			$dir = $conf->societe->multidir_output[$entity];
8496
			if (!empty($object->logo)) {
8497
				if (dolIsAllowedForPreview($object->logo)) {
8498
					if ((string) $imagesize == 'mini') {
8499
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
8500
					} elseif ((string) $imagesize == 'small') {
8501
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_small');
8502
					} else {
8503
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
8504
					}
8505
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
8506
				}
8507
			}
8508
			$email = $object->email;
8509
		} elseif ($modulepart == 'contact')	{
8510
			$dir = $conf->societe->multidir_output[$entity].'/contact';
8511
			if (!empty($object->photo)) {
8512
				if (dolIsAllowedForPreview($object->photo)) {
8513
					if ((string) $imagesize == 'mini') {
8514
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_mini');
8515
					} elseif ((string) $imagesize == 'small') {
8516
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_small');
8517
					} else {
8518
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
8519
					}
8520
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
8521
				}
8522
			}
8523
			$email = $object->email;
8524
			$capture = 'user';
8525
		} elseif ($modulepart == 'userphoto') {
8526
			$dir = $conf->user->dir_output;
8527
			if (!empty($object->photo)) {
8528
				if (dolIsAllowedForPreview($object->photo)) {
8529
					if ((string) $imagesize == 'mini') {
8530
						$file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_mini');
8531
					} elseif ((string) $imagesize == 'small') {
8532
						$file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_small');
8533
					} else {
8534
						$file = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
8535
					}
8536
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
8537
				}
8538
			}
8539
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8540
				$altfile = $object->id.".jpg"; // For backward compatibility
8541
			}
8542
			$email = $object->email;
8543
			$capture = 'user';
8544
		} elseif ($modulepart == 'memberphoto')	{
8545
			$dir = $conf->adherent->dir_output;
8546
			if (!empty($object->photo)) {
8547
				if (dolIsAllowedForPreview($object->photo)) {
8548
					if ((string) $imagesize == 'mini') {
8549
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_mini');
8550
					} elseif ((string) $imagesize == 'small') {
8551
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_small');
8552
					} else {
8553
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
8554
					}
8555
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
8556
				}
8557
			}
8558
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8559
				$altfile = $object->id.".jpg"; // For backward compatibility
8560
			}
8561
			$email = $object->email;
8562
			$capture = 'user';
8563
		} else {
8564
			// Generic case to show photos
8565
			$dir = $conf->$modulepart->dir_output;
8566
			if (!empty($object->photo)) {
8567
				if (dolIsAllowedForPreview($object->photo)) {
8568
					if ((string) $imagesize == 'mini') {
8569
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_mini');
8570
					} elseif ((string) $imagesize == 'small') {
8571
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_small');
8572
					} else {
8573
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
8574
					}
8575
					$originalfile = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
8576
				}
8577
			}
8578
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8579
				$altfile = $object->id.".jpg"; // For backward compatibility
8580
			}
8581
			$email = $object->email;
8582
		}
8583
8584
		if ($forcecapture) {
8585
			$capture = $forcecapture;
8586
		}
8587
8588
		if ($dir) {
8589
			if ($file && file_exists($dir."/".$file)) {
8590
				if ($addlinktofullsize) {
8591
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
8592
					if ($urladvanced) {
8593
						$ret .= '<a href="'.$urladvanced.'">';
8594
					} else {
8595
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
8596
					}
8597
				}
8598
				$ret .= '<img alt="Photo" 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='.$modulepart.'&entity='.$entity.'&file='.urlencode($file).'&cache='.$cache.'">';
8599
				if ($addlinktofullsize) {
8600
					$ret .= '</a>';
8601
				}
8602
			} elseif ($altfile && file_exists($dir."/".$altfile)) {
8603
				if ($addlinktofullsize) {
8604
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
8605
					if ($urladvanced) {
8606
						$ret .= '<a href="'.$urladvanced.'">';
8607
					} else {
8608
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
8609
					}
8610
				}
8611
				$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='.$modulepart.'&entity='.$entity.'&file='.urlencode($altfile).'&cache='.$cache.'">';
8612
				if ($addlinktofullsize) {
8613
					$ret .= '</a>';
8614
				}
8615
			} else {
8616
				$nophoto = '/public/theme/common/nophoto.png';
8617
				$defaultimg = 'identicon';		// For gravatar
8618
				if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) {	// For modules that need a special image when photo not found
8619
					if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && strpos($object->morphy, 'mor')) !== false) {
8620
						$nophoto = 'company';
8621
					} else {
8622
						$nophoto = '/public/theme/common/user_anonymous.png';
8623
						if (!empty($object->gender) && $object->gender == 'man') {
8624
							$nophoto = '/public/theme/common/user_man.png';
8625
						}
8626
						if (!empty($object->gender) && $object->gender == 'woman') {
8627
							$nophoto = '/public/theme/common/user_woman.png';
8628
						}
8629
					}
8630
				}
8631
8632
				if (!empty($conf->gravatar->enabled) && $email && empty($noexternsourceoverwrite)) {
8633
					// see https://gravatar.com/site/implement/images/php/
8634
					$ret .= '<!-- Put link to gravatar -->';
8635
					$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="Gravatar avatar" title="'.$email.' Gravatar avatar" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="https://www.gravatar.com/avatar/'.md5(strtolower(trim($email))).'?s='.$width.'&d='.$defaultimg.'">'; // gravatar need md5 hash
8636
				} else {
8637
					if ($nophoto == 'company') {
8638
						$ret .= '<div class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').'">'.img_picto('', 'company').'</div>';
8639
					} else {
8640
						$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.$nophoto.'">';
8641
					}
8642
				}
8643
			}
8644
8645
			if ($caneditfield) {
8646
				if ($object->photo) {
8647
					$ret .= "<br>\n";
8648
				}
8649
				$ret .= '<table class="nobordernopadding centpercent">';
8650
				if ($object->photo) {
8651
					$ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> '.$langs->trans("Delete").'<br><br></td></tr>';
8652
				}
8653
				$ret .= '<tr><td class="tdoverflow"><input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"'.($capture ? ' capture="'.$capture.'"' : '').'></td></tr>';
8654
				$ret .= '</table>';
8655
			}
8656
		} else {
8657
			dol_print_error('', 'Call of showphoto with wrong parameters modulepart='.$modulepart);
8658
		}
8659
8660
		return $ret;
8661
	}
8662
8663
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8664
	/**
8665
	 *	Return select list of groups
8666
	 *
8667
	 *  @param	string	$selected       Id group preselected
8668
	 *  @param  string	$htmlname       Field name in form
8669
	 *  @param  int		$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
8670
	 *  @param  string	$exclude        Array list of groups id to exclude
8671
	 * 	@param	int		$disabled		If select list must be disabled
8672
	 *  @param  string	$include        Array list of groups id to include
8673
	 * 	@param	int		$enableonly		Array list of groups id to be enabled. All other must be disabled
8674
	 * 	@param	string	$force_entity	'0' or Ids of environment to force
8675
	 * 	@param	bool	$multiple		add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
8676
	 *  @param  string	$morecss		More css to add to html component
8677
	 *  @return	string
8678
	 *  @see select_dolusers()
8679
	 */
8680
	public function select_dolgroups($selected = '', $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $multiple = false, $morecss = '')
8681
	{
8682
		// phpcs:enable
8683
		global $conf, $user, $langs;
8684
8685
		// Permettre l'exclusion de groupes
8686
		if (is_array($exclude)) {
0 ignored issues
show
introduced by
The condition is_array($exclude) is always false.
Loading history...
8687
			$excludeGroups = implode(",", $exclude);
8688
		}
8689
		// Permettre l'inclusion de groupes
8690
		if (is_array($include)) {
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
8691
			$includeGroups = implode(",", $include);
8692
		}
8693
8694
		if (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
8695
			$selected = array($selected);
8696
		}
8697
8698
		$out = '';
8699
8700
		// On recherche les groupes
8701
		$sql = "SELECT ug.rowid, ug.nom as name";
8702
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
8703
			$sql .= ", e.label";
8704
		}
8705
		$sql .= " FROM ".MAIN_DB_PREFIX."usergroup as ug ";
8706
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
8707
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=ug.entity";
8708
			if ($force_entity) {
8709
				$sql .= " WHERE ug.entity IN (0, ".$force_entity.")";
8710
			} else {
8711
				$sql .= " WHERE ug.entity IS NOT NULL";
8712
			}
8713
		} else {
8714
			$sql .= " WHERE ug.entity IN (0, ".$conf->entity.")";
8715
		}
8716
		if (is_array($exclude) && $excludeGroups) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $excludeGroups does not seem to be defined for all execution paths leading up to this point.
Loading history...
introduced by
The condition is_array($exclude) is always false.
Loading history...
8717
			$sql .= " AND ug.rowid NOT IN (".$this->db->sanitize($excludeGroups).")";
8718
		}
8719
		if (is_array($include) && $includeGroups) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $includeGroups does not seem to be defined for all execution paths leading up to this point.
Loading history...
introduced by
The condition is_array($include) is always false.
Loading history...
8720
			$sql .= " AND ug.rowid IN (".$this->db->sanitize($includeGroups).")";
8721
		}
8722
		$sql .= " ORDER BY ug.nom ASC";
8723
8724
		dol_syslog(get_class($this)."::select_dolgroups", LOG_DEBUG);
8725
		$resql = $this->db->query($sql);
8726
		if ($resql) {
8727
			// Enhance with select2
8728
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
8729
			$out .= ajax_combobox($htmlname);
8730
8731
			$out .= '<select class="flat minwidth200'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
8732
8733
			$num = $this->db->num_rows($resql);
8734
			$i = 0;
8735
			if ($num) {
8736
				if ($show_empty && !$multiple) {
8737
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>&nbsp;</option>'."\n";
8738
				}
8739
8740
				while ($i < $num) {
8741
					$obj = $this->db->fetch_object($resql);
8742
					$disableline = 0;
8743
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
8744
						$disableline = 1;
8745
					}
8746
8747
					$out .= '<option value="'.$obj->rowid.'"';
8748
					if ($disableline) {
8749
						$out .= ' disabled';
8750
					}
8751
					if ((is_object($selected[0]) && $selected[0]->id == $obj->rowid) || (!is_object($selected[0]) && in_array($obj->rowid, $selected))) {
8752
						$out .= ' selected';
8753
					}
8754
					$out .= '>';
8755
8756
					$out .= $obj->name;
8757
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1) {
8758
						$out .= " (".$obj->label.")";
8759
					}
8760
8761
					$out .= '</option>';
8762
					$i++;
8763
				}
8764
			} else {
8765
				if ($show_empty) {
8766
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'></option>'."\n";
8767
				}
8768
				$out .= '<option value="" disabled>'.$langs->trans("NoUserGroupDefined").'</option>';
8769
			}
8770
			$out .= '</select>';
8771
		} else {
8772
			dol_print_error($this->db);
8773
		}
8774
8775
		return $out;
8776
	}
8777
8778
8779
	/**
8780
	 *	Return HTML to show the search and clear seach button
8781
	 *
8782
	 *  @return	string
8783
	 */
8784
	public function showFilterButtons()
8785
	{
8786
		$out = '<div class="nowraponall">';
8787
		$out .= '<button type="submit" class="liste_titre button_search" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
8788
		$out .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
8789
		$out .= '</div>';
8790
8791
		return $out;
8792
	}
8793
8794
	/**
8795
	 *	Return HTML to show the search and clear search button
8796
	 *
8797
	 *  @param  string  $cssclass                  CSS class
8798
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
8799
	 *  @param  string  $massactionname            Mass action button name that will launch an action on the selected items
8800
	 *  @return	string
8801
	 */
8802
	public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
8803
	{
8804
		global $conf, $langs;
8805
8806
		$out = '';
8807
8808
		if (!empty($conf->use_javascript_ajax)) {
8809
			$out .= '<div class="inline-block checkallactions"><input type="checkbox" id="'.$cssclass.'s" name="'.$cssclass.'s" class="checkallactions"></div>';
8810
		}
8811
		$out .= '<script>
8812
            $(document).ready(function() {
8813
                $("#' . $cssclass.'s").click(function() {
8814
                    if($(this).is(\':checked\')){
8815
                        console.log("We check all '.$cssclass.' and trigger the change method");
8816
                		$(".'.$cssclass.'").prop(\'checked\', true).trigger(\'change\');
8817
                    }
8818
                    else
8819
                    {
8820
                        console.log("We uncheck all");
8821
                		$(".'.$cssclass.'").prop(\'checked\', false).trigger(\'change\');
8822
                    }'."\n";
8823
		if ($calljsfunction) {
8824
			$out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "'.$massactionname.'", "'.$cssclass.'"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
8825
		}
8826
		$out .= '         });
8827
        	        $(".' . $cssclass.'").change(function() {
8828
					$(this).closest("tr").toggleClass("highlight", this.checked);
8829
				});
8830
		 	});
8831
    	</script>';
8832
8833
		return $out;
8834
	}
8835
8836
	/**
8837
	 *	Return HTML to show the search and clear seach button
8838
	 *
8839
	 *  @param	int  	$addcheckuncheckall        Add the check all/uncheck all checkbox (use javascript) and code to manage this
8840
	 *  @param  string  $cssclass                  CSS class
8841
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
8842
	 *  @param  string  $massactionname            Mass action name
8843
	 *  @return	string
8844
	 */
8845
	public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
8846
	{
8847
		$out = $this->showFilterButtons();
8848
		if ($addcheckuncheckall) {
8849
			$out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
8850
		}
8851
		return $out;
8852
	}
8853
8854
	/**
8855
	 * Return HTML to show the select of expense categories
8856
	 *
8857
	 * @param	string	$selected              preselected category
8858
	 * @param	string	$htmlname              name of HTML select list
8859
	 * @param	integer	$useempty              1=Add empty line
8860
	 * @param	array	$excludeid             id to exclude
8861
	 * @param	string	$target                htmlname of target select to bind event
8862
	 * @param	int		$default_selected      default category to select if fk_c_type_fees change = EX_KME
8863
	 * @param	array	$params                param to give
8864
	 * @param	int		$info_admin			   Show the tooltip help picto to setup list
8865
	 * @return	string
8866
	 */
8867
	public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
8868
	{
8869
		global $db, $langs, $user;
8870
8871
		$out = '';
8872
		$sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1';
8873
		$sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')';
8874
		if (!empty($excludeid)) {
8875
			$sql .= ' AND rowid NOT IN ('.$this->db->sanitize(implode(',', $excludeid)).')';
8876
		}
8877
		$sql .= ' ORDER BY label';
8878
8879
		$resql = $db->query($sql);
8880
		if ($resql) {
8881
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp maxwidth200">';
8882
			if ($useempty) {
8883
				$out .= '<option value="0">&nbsp;</option>';
8884
			}
8885
8886
			while ($obj = $db->fetch_object($resql)) {
8887
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.$langs->trans($obj->label).'</option>';
8888
			}
8889
			$out .= '</select>';
8890
			$out .= ajax_combobox('select_'.$htmlname);
8891
8892
			if (!empty($htmlname) && $user->admin && $info_admin) {
8893
				$out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
8894
			}
8895
8896
			if (!empty($target)) {
8897
				$sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
8898
				$resql = $db->query($sql);
8899
				if ($resql) {
8900
					if ($db->num_rows($resql) > 0) {
8901
						$obj = $db->fetch_object($resql);
8902
						$out .= '<script>
8903
							$(function() {
8904
								$("select[name='.$target.']").on("change", function() {
8905
									var current_val = $(this).val();
8906
									if (current_val == '.$obj->id.') {';
8907
						if (!empty($default_selected) || !empty($selected)) {
8908
							$out .= '$("select[name='.$htmlname.']").val("'.($default_selected > 0 ? $default_selected : $selected).'");';
8909
						}
8910
8911
						$out .= '
8912
										$("select[name='.$htmlname.']").change();
8913
									}
8914
								});
8915
8916
								$("select[name='.$htmlname.']").change(function() {
8917
8918
									if ($("select[name='.$target.']").val() == '.$obj->id.') {
8919
										// get price of kilometer to fill the unit price
8920
										$.ajax({
8921
											method: "POST",
8922
											dataType: "json",
8923
											data: { fk_c_exp_tax_cat: $(this).val(), token: \''.currentToken().'\' },
8924
											url: "'.(DOL_URL_ROOT.'/expensereport/ajax/ajaxik.php?'.$params).'",
8925
										}).done(function( data, textStatus, jqXHR ) {
8926
											console.log(data);
8927
											if (typeof data.up != "undefined") {
8928
												$("input[name=value_unit]").val(data.up);
8929
												$("select[name='.$htmlname.']").attr("title", data.title);
8930
											} else {
8931
												$("input[name=value_unit]").val("");
8932
												$("select[name='.$htmlname.']").attr("title", "");
8933
											}
8934
										});
8935
									}
8936
								});
8937
							});
8938
						</script>';
8939
					}
8940
				}
8941
			}
8942
		} else {
8943
			dol_print_error($db);
8944
		}
8945
8946
		return $out;
8947
	}
8948
8949
	/**
8950
	 * Return HTML to show the select ranges of expense range
8951
	 *
8952
	 * @param	string	$selected    preselected category
8953
	 * @param	string	$htmlname    name of HTML select list
8954
	 * @param	integer	$useempty    1=Add empty line
8955
	 * @return	string
8956
	 */
8957
	public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
8958
	{
8959
		global $db, $conf, $langs;
8960
8961
		$out = '';
8962
		$sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range';
8963
		$sql .= ' WHERE entity = '.$conf->entity.' AND active = 1';
8964
8965
		$resql = $db->query($sql);
8966
		if ($resql) {
8967
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
8968
			if ($useempty) {
8969
				$out .= '<option value="0"></option>';
8970
			}
8971
8972
			while ($obj = $db->fetch_object($resql)) {
8973
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.price($obj->range_ik, 0, $langs, 1, 0).'</option>';
8974
			}
8975
			$out .= '</select>';
8976
		} else {
8977
			dol_print_error($db);
8978
		}
8979
8980
		return $out;
8981
	}
8982
8983
	/**
8984
	 * Return HTML to show a select of expense
8985
	 *
8986
	 * @param	string	$selected    preselected category
8987
	 * @param	string	$htmlname    name of HTML select list
8988
	 * @param	integer	$useempty    1=Add empty choice
8989
	 * @param	integer	$allchoice   1=Add all choice
8990
	 * @param	integer	$useid       0=use 'code' as key, 1=use 'id' as key
8991
	 * @return	string
8992
	 */
8993
	public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
8994
	{
8995
		global $db, $langs;
8996
8997
		$out = '';
8998
		$sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees';
8999
		$sql .= ' WHERE active = 1';
9000
9001
		$resql = $db->query($sql);
9002
		if ($resql) {
9003
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
9004
			if ($useempty) {
9005
				$out .= '<option value="0"></option>';
9006
			}
9007
			if ($allchoice) {
9008
				$out .= '<option value="-1">'.$langs->trans('AllExpenseReport').'</option>';
9009
			}
9010
9011
			$field = 'code';
9012
			if ($useid) {
9013
				$field = 'id';
9014
			}
9015
9016
			while ($obj = $db->fetch_object($resql)) {
9017
				$key = $langs->trans($obj->code);
9018
				$out .= '<option '.($selected == $obj->{$field} ? 'selected="selected"' : '').' value="'.$obj->{$field}.'">'.($key != $obj->code ? $key : $obj->label).'</option>';
9019
			}
9020
			$out .= '</select>';
9021
		} else {
9022
			dol_print_error($db);
9023
		}
9024
9025
		return $out;
9026
	}
9027
9028
	/**
9029
	 *  Output a combo list with invoices qualified for a third party
9030
	 *
9031
	 *  @param	int		$socid      	Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id)
9032
	 *  @param  int		$selected   	Id invoice preselected
9033
	 *  @param  string	$htmlname   	Name of HTML select
9034
	 *	@param	int		$maxlength		Maximum length of label
9035
	 *	@param	int		$option_only	Return only html options lines without the select tag
9036
	 *	@param	string	$show_empty		Add an empty line ('1' or string to show for empty line)
9037
	 *  @param	int		$discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
9038
	 *  @param	int		$forcefocus		Force focus on field (works with javascript only)
9039
	 *  @param	int		$disabled		Disabled
9040
	 *  @param	string	$morecss        More css added to the select component
9041
	 *  @param	string	$projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
9042
	 *  @param	string	$showproject	'all' = Show project info, ''=Hide project info
9043
	 *  @param	User	$usertofilter	User object to use for filtering
9044
	 *	@return int         			Nbr of project if OK, <0 if KO
9045
	 */
9046
	public function selectInvoice($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)
9047
	{
9048
		global $user, $conf, $langs;
9049
9050
		require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
9051
9052
		if (is_null($usertofilter)) {
9053
			$usertofilter = $user;
9054
		}
9055
9056
		$out = '';
9057
9058
		$hideunselectables = false;
9059
		if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) {
9060
			$hideunselectables = true;
9061
		}
9062
9063
		if (empty($projectsListId)) {
9064
			if (empty($usertofilter->rights->projet->all->lire)) {
9065
				$projectstatic = new Project($this->db);
9066
				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
9067
			}
9068
		}
9069
9070
		// Search all projects
9071
		$sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref,
9072
            p.title, p.fk_soc, p.fk_statut, p.public,";
9073
		$sql .= ' s.nom as name';
9074
		$sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p';
9075
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,';
9076
		$sql .= ' '.MAIN_DB_PREFIX.'facture as f';
9077
		$sql .= " WHERE p.entity IN (".getEntity('project').")";
9078
		$sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
9079
		//if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
9080
		//if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
9081
		//if ($socid > 0)  $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
9082
		$sql .= " ORDER BY p.ref, f.ref ASC";
9083
9084
		$resql = $this->db->query($sql);
9085
		if ($resql) {
9086
			// Use select2 selector
9087
			if (!empty($conf->use_javascript_ajax)) {
9088
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
9089
				$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
9090
				$out .= $comboenhancement;
9091
				$morecss = 'minwidth200imp maxwidth500';
9092
			}
9093
9094
			if (empty($option_only)) {
9095
				$out .= '<select class="valignmiddle flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').' id="'.$htmlname.'" name="'.$htmlname.'">';
9096
			}
9097
			if (!empty($show_empty)) {
9098
				$out .= '<option value="0" class="optiongrey">';
9099
				if (!is_numeric($show_empty)) {
9100
					$out .= $show_empty;
9101
				} else {
9102
					$out .= '&nbsp;';
9103
				}
9104
				$out .= '</option>';
9105
			}
9106
			$num = $this->db->num_rows($resql);
9107
			$i = 0;
9108
			if ($num) {
9109
				while ($i < $num) {
9110
					$obj = $this->db->fetch_object($resql);
9111
					// 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.
9112
					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire)) {
9113
						// Do nothing
9114
					} else {
9115
						if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
9116
							$i++;
9117
							continue;
9118
						}
9119
9120
						$labeltoshow = '';
9121
9122
						if ($showproject == 'all') {
9123
							$labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
9124
							if ($obj->name) {
9125
								$labeltoshow .= ' - '.$obj->name; // Soc name
9126
							}
9127
9128
							$disabled = 0;
9129
							if ($obj->fk_statut == Project::STATUS_DRAFT) {
9130
								$disabled = 1;
9131
								$labeltoshow .= ' - '.$langs->trans("Draft");
9132
							} elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
9133
								if ($discard_closed == 2) {
9134
									$disabled = 1;
9135
								}
9136
								$labeltoshow .= ' - '.$langs->trans("Closed");
9137
							} elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
9138
								$disabled = 1;
9139
								$labeltoshow .= ' - '.$langs->trans("LinkedToAnotherCompany");
9140
							}
9141
						}
9142
9143
						if (!empty($selected) && $selected == $obj->rowid) {
9144
							$out .= '<option value="'.$obj->rowid.'" selected';
9145
							//if ($disabled) $out.=' disabled';						// with select2, field can't be preselected if disabled
9146
							$out .= '>'.$labeltoshow.'</option>';
9147
						} else {
9148
							if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
9149
								$resultat = '';
9150
							} else {
9151
								$resultat = '<option value="'.$obj->rowid.'"';
9152
								if ($disabled) {
9153
									$resultat .= ' disabled';
9154
								}
9155
								//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
9156
								//else $labeltoshow.=' ('.$langs->trans("Private").')';
9157
								$resultat .= '>';
9158
								$resultat .= $labeltoshow;
9159
								$resultat .= '</option>';
9160
							}
9161
							$out .= $resultat;
9162
						}
9163
					}
9164
					$i++;
9165
				}
9166
			}
9167
			if (empty($option_only)) {
9168
				$out .= '</select>';
9169
			}
9170
9171
			print $out;
9172
9173
			$this->db->free($resql);
9174
			return $num;
9175
		} else {
9176
			dol_print_error($this->db);
9177
			return -1;
9178
		}
9179
	}
9180
9181
	/**
9182
	 * Output the component to make advanced search criteries
9183
	 *
9184
	 * @param	array		$arrayofcriterias			          Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...)
9185
	 * @param	array		$search_component_params	          Array of selected search criterias
9186
	 * @param   array       $arrayofinputfieldsalreadyoutput      Array of input fields already inform. The component will not generate a hidden input field if it is in this list.
9187
	 * @return	string									          HTML component for advanced search
9188
	 */
9189
	public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array())
9190
	{
9191
		global $langs;
9192
9193
		$ret = '';
9194
9195
		$ret .= '<div class="nowrap centpercent">';
9196
		//$ret .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9197
		$ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor paddingright">';
9198
		$ret .= '<span class="fas fa-filter linkobject boxfilter" title="Filter" id="idsubimgproductdistribution"></span>';
9199
		$ret .= $langs->trans("Filters");
9200
		$ret .= '</a>';
9201
		//$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9202
		$ret .= '<div name="search_component_params" class="search_component_params inline-block minwidth500 maxwidth300onsmartphone valignmiddle">';
9203
		$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
9204
9205
		$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
9206
		$ret .= '</div>';
9207
		$ret .= '<input type="hidden" name="search_component_params_hidden" class="search_component_params_hidden" value="'.GETPOST("search_component_params_hidden").'">';
9208
		// For compatibility with forms that show themself the search criteria in addition of this component, we output the fields
9209
		foreach ($arrayofcriterias as $criterias) {
9210
			foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {
9211
				if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
9212
					continue;
9213
				}
9214
				if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
9215
					continue;
9216
				}
9217
				if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
9218
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_start">';
9219
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startyear">';
9220
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startmonth">';
9221
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startday">';
9222
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_end">';
9223
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endyear">';
9224
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endmonth">';
9225
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endday">';
9226
				} else {
9227
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'">';
9228
				}
9229
			}
9230
		}
9231
		$ret .= '</div>';
9232
9233
9234
		return $ret;
9235
	}
9236
9237
	/**
9238
	 * selectModelMail
9239
	 *
9240
	 * @param   string   $prefix     	Prefix
9241
	 * @param   string   $modelType  	Model type
9242
	 * @param	int		 $default	 	1=Show also Default mail template
9243
	 * @param	int		 $addjscombo	Add js combobox
9244
	 * @return  string               	HTML select string
9245
	 */
9246
	public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
9247
	{
9248
		global $langs, $db, $user;
9249
9250
		$retstring = '';
9251
9252
		$TModels = array();
9253
9254
		include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
9255
		$formmail = new FormMail($db);
9256
		$result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
9257
9258
		if ($default) {
9259
			$TModels[0] = $langs->trans('DefaultMailModel');
9260
		}
9261
		if ($result > 0) {
9262
			foreach ($formmail->lines_model as $model) {
9263
				$TModels[$model->id] = $model->label;
9264
			}
9265
		}
9266
9267
		$retstring .= '<select class="flat" id="select_'.$prefix.'model_mail" name="'.$prefix.'model_mail">';
9268
9269
		foreach ($TModels as $id_model => $label_model) {
9270
			$retstring .= '<option value="'.$id_model.'"';
9271
			$retstring .= ">".$label_model."</option>";
9272
		}
9273
9274
		$retstring .= "</select>";
9275
9276
		if ($addjscombo) {
9277
			$retstring .= ajax_combobox('select_'.$prefix.'model_mail');
9278
		}
9279
9280
		return $retstring;
9281
	}
9282
9283
	/**
9284
	 * Output the buttons to submit a creation/edit form
9285
	 *
9286
	 * @param   string  $save_label     Alternative label for save button
9287
	 * @param   string  $cancel_label   Alternative label for cancel button
9288
	 * @param   array   $morefields     Add additional buttons between save and cancel
9289
	 * @param   bool    $withoutdiv     Option to remove enclosing centered div
9290
	 * @return 	string					Html code with the buttons
9291
	 */
9292
	public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morefields = array(), $withoutdiv = 0)
9293
	{
9294
		global $langs;
9295
9296
		$buttons = array();
9297
9298
		$save = array(
9299
			'name' => 'save',
9300
			'label_key' => $save_label,
9301
		);
9302
9303
		if ($save_label == 'Create' || $save_label == 'Add' ) {
9304
			$save['name'] = 'add';
9305
		} elseif ($save_label == 'Modify') {
9306
			$save['name'] = 'edit';
9307
		}
9308
9309
		$cancel = array(
9310
				'name' => 'cancel',
9311
				'label_key' => 'Cancel',
9312
		);
9313
9314
		!empty($save_label) ? $buttons[] = $save : '';
9315
9316
		if (!empty($morefields)) {
9317
			$buttons[] = $morefields;
9318
		}
9319
9320
		!empty($cancel_label) ? $buttons[] = $cancel : '';
9321
9322
		$retstring = $withoutdiv ? '': '<div class="center">';
9323
9324
		foreach ($buttons as $button) {
9325
			$addclass = empty($button['addclass']) ? '' : $button['addclass'];
9326
			$retstring .= '<input type="submit" class="button button-'.$button['name'].' '.$addclass.'" name="'.$button['name'].'" value="'.dol_escape_htmltag($langs->trans($button['label_key'])).'">';
9327
		}
9328
		$retstring .= $withoutdiv ? '': '</div>';
9329
9330
		return $retstring;
9331
	}
9332
}
9333