Passed
Branch develop (5ec324)
by
unknown
37:40
created

Form::form_multicurrency_rate()   A

Complexity

Conditions 6

Size

Total Lines 24
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 19
nop 4
dl 0
loc 24
rs 9.0111
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-2019  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"');
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>';
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
			$out .= '<input id="location_incoterms" class="maxwidth100onsmartphone nomargintop nomarginbottom" name="location_incoterms" value="'.$location_incoterms.'">';
1060
1061
			if (!empty($page)) {
1062
				$out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1063
			}
1064
		} else {
1065
			dol_print_error($this->db);
1066
		}
1067
1068
		return $out;
1069
	}
1070
1071
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1072
	/**
1073
	 *	Return list of types of lines (product or service)
1074
	 * 	Example: 0=product, 1=service, 9=other (for external module)
1075
	 *
1076
	 *	@param  string	$selected       Preselected type
1077
	 *	@param  string	$htmlname       Name of field in html form
1078
	 * 	@param	int		$showempty		Add an empty field
1079
	 * 	@param	int		$hidetext		Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
1080
	 * 	@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')
1081
	 *  @return	void
1082
	 */
1083
	public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1084
	{
1085
		// phpcs:enable
1086
		global $db, $langs, $user, $conf;
1087
1088
		// If product & services are enabled or both disabled.
1089
		if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled))
1090
			|| (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) {
1091
			if (empty($hidetext)) {
1092
				print $langs->trans("Type").': ';
1093
			}
1094
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1095
			if ($showempty) {
1096
				print '<option value="-1"';
1097
				if ($selected == -1) {
1098
					print ' selected';
1099
				}
1100
				print '>&nbsp;</option>';
1101
			}
1102
1103
			print '<option value="0"';
1104
			if (0 == $selected) {
1105
				print ' selected';
1106
			}
1107
			print '>'.$langs->trans("Product");
1108
1109
			print '<option value="1"';
1110
			if (1 == $selected) {
1111
				print ' selected';
1112
			}
1113
			print '>'.$langs->trans("Service");
1114
1115
			print '</select>';
1116
			print ajax_combobox('select_'.$htmlname);
1117
			//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1118
		}
1119
		if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) {
1120
			print $langs->trans("Service");
1121
			print '<input type="hidden" name="'.$htmlname.'" value="1">';
1122
		}
1123
		if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) {
1124
			print $langs->trans("Product");
1125
			print '<input type="hidden" name="'.$htmlname.'" value="0">';
1126
		}
1127
		if ($forceall < 0) {	// This should happened only for contracts when both predefined product and service are disabled.
1128
			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
1129
		}
1130
	}
1131
1132
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1133
	/**
1134
	 *	Load into cache cache_types_fees, array of types of fees
1135
	 *
1136
	 *	@return     int             Nb of lines loaded, <0 if KO
1137
	 */
1138
	public function load_cache_types_fees()
1139
	{
1140
		// phpcs:enable
1141
		global $langs;
1142
1143
		$num = count($this->cache_types_fees);
1144
		if ($num > 0) {
1145
			return 0; // Cache already loaded
1146
		}
1147
1148
		dol_syslog(__METHOD__, LOG_DEBUG);
1149
1150
		$langs->load("trips");
1151
1152
		$sql = "SELECT c.code, c.label";
1153
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
1154
		$sql .= " WHERE active > 0";
1155
1156
		$resql = $this->db->query($sql);
1157
		if ($resql) {
1158
			$num = $this->db->num_rows($resql);
1159
			$i = 0;
1160
1161
			while ($i < $num) {
1162
				$obj = $this->db->fetch_object($resql);
1163
1164
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1165
				$label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1166
				$this->cache_types_fees[$obj->code] = $label;
1167
				$i++;
1168
			}
1169
1170
			asort($this->cache_types_fees);
1171
1172
			return $num;
1173
		} else {
1174
			dol_print_error($this->db);
1175
			return -1;
1176
		}
1177
	}
1178
1179
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1180
	/**
1181
	 *	Return list of types of notes
1182
	 *
1183
	 *	@param	string		$selected		Preselected type
1184
	 *	@param  string		$htmlname		Name of field in form
1185
	 * 	@param	int			$showempty		Add an empty field
1186
	 * 	@return	void
1187
	 */
1188
	public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1189
	{
1190
		// phpcs:enable
1191
		global $user, $langs;
1192
1193
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1194
1195
		$this->load_cache_types_fees();
1196
1197
		print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1198
		if ($showempty) {
1199
			print '<option value="-1"';
1200
			if ($selected == -1) {
1201
				print ' selected';
1202
			}
1203
			print '>&nbsp;</option>';
1204
		}
1205
1206
		foreach ($this->cache_types_fees as $key => $value) {
1207
			print '<option value="'.$key.'"';
1208
			if ($key == $selected) {
1209
				print ' selected';
1210
			}
1211
			print '>';
1212
			print $value;
1213
			print '</option>';
1214
		}
1215
1216
		print '</select>';
1217
		if ($user->admin) {
1218
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1219
		}
1220
	}
1221
1222
1223
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1224
	/**
1225
	 *  Output html form to select a third party
1226
	 *
1227
	 *	@param	string	$selected       		Preselected type
1228
	 *	@param  string	$htmlname       		Name of field in form
1229
	 *  @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)')
1230
	 *	@param	string	$showempty				Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
1231
	 * 	@param	int		$showtype				Show third party type in combolist (customer, prospect or supplier)
1232
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
1233
	 *  @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')))
1234
	 *	@param	int		$limit					Maximum number of elements
1235
	 *  @param	string	$morecss				Add more css styles to the SELECT component
1236
	 *	@param  string	$moreparam      		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1237
	 *	@param	string	$selected_input_value	Value of preselected input text (for use with ajax)
1238
	 *  @param	int		$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1239
	 *  @param	array	$ajaxoptions			Options for ajax_autocompleter
1240
	 * 	@param  bool	$multiple				add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
1241
	 *  @param	array	$excludeids				Exclude IDs from the select combo
1242
	 * 	@return	string							HTML string with select box for thirdparty.
1243
	 */
1244
	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())
1245
	{
1246
		// phpcs:enable
1247
		global $conf, $user, $langs;
1248
1249
		$out = '';
1250
1251
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1252
			if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
1253
				$ajaxoptions = array();
1254
			}
1255
1256
			require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1257
1258
			// No immediate load of all database
1259
			$placeholder = '';
1260
			if ($selected && empty($selected_input_value)) {
1261
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1262
				$societetmp = new Societe($this->db);
1263
				$societetmp->fetch($selected);
1264
				$selected_input_value = $societetmp->name;
1265
				unset($societetmp);
1266
			}
1267
			// mode 1
1268
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '');
1269
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1270
1271
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1272
			if (empty($hidelabel)) {
1273
				print $langs->trans("RefOrLabel").' : ';
1274
			} elseif ($hidelabel > 1) {
1275
				$placeholder = $langs->trans("RefOrLabel");
1276
				if ($hidelabel == 2) {
1277
					$out .= img_picto($langs->trans("Search"), 'search');
1278
				}
1279
			}
1280
			$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' : '').' />';
1281
			if ($hidelabel == 3) {
1282
				$out .= img_picto($langs->trans("Search"), 'search');
1283
			}
1284
		} else {
1285
			// Immediate load of all database
1286
			$out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids);
1287
		}
1288
1289
		return $out;
1290
	}
1291
1292
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1293
	/**
1294
	 *  Output html form to select a third party.
1295
	 *  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.
1296
	 *
1297
	 *	@param	string	$selected       Preselected type
1298
	 *	@param  string	$htmlname       Name of field in form
1299
	 *  @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.
1300
	 *	@param	string	$showempty		Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
1301
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
1302
	 * 	@param	int		$forcecombo		Force to use standard HTML select component without beautification
1303
	 *  @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')))
1304
	 *  @param	string	$filterkey		Filter on key value
1305
	 *  @param	int		$outputmode		0=HTML select string, 1=Array
1306
	 *  @param	int		$limit			Limit number of answers
1307
	 *  @param	string	$morecss		Add more css styles to the SELECT component
1308
	 *	@param  string	$moreparam      Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1309
	 *	@param  bool	$multiple       add [] in the name of element and add 'multiple' attribut
1310
	 *  @param	array	$excludeids		Exclude IDs from the select combo
1311
	 * 	@return	string					HTML string with
1312
	 */
1313
	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())
1314
	{
1315
		// phpcs:enable
1316
		global $conf, $user, $langs;
1317
1318
		$out = '';
1319
		$num = 0;
1320
		$outarray = array();
1321
1322
		if ($selected === '') {
1323
			$selected = array();
1324
		} elseif (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
1325
			$selected = array($selected);
1326
		}
1327
1328
		// Clean $filter that may contains sql conditions so sql code
1329
		if (function_exists('testSqlAndScriptInject')) {
1330
			if (testSqlAndScriptInject($filter, 3) > 0) {
1331
				$filter = '';
1332
			}
1333
		}
1334
1335
		// We search companies
1336
		$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1337
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1338
			$sql .= ", s.address, s.zip, s.town";
1339
			$sql .= ", dictp.code as country_code";
1340
		}
1341
		$sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
1342
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1343
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid = s.fk_pays";
1344
		}
1345
		if (!$user->rights->societe->client->voir && !$user->socid) {
1346
			$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1347
		}
1348
		$sql .= " WHERE s.entity IN (".getEntity('societe').")";
1349
		if (!empty($user->socid)) {
1350
			$sql .= " AND s.rowid = ".$user->socid;
1351
		}
1352
		if ($filter) {
1353
			$sql .= " AND (".$filter.")";
1354
		}
1355
		if (!$user->rights->societe->client->voir && !$user->socid) {
1356
			$sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id;
1357
		}
1358
		if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1359
			$sql .= " AND s.status <> 0";
1360
		}
1361
		if (!empty($excludeids)) {
1362
			$sql .= " AND rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1363
		}
1364
		// Add criteria
1365
		if ($filterkey && $filterkey != '') {
1366
			$sql .= " AND (";
1367
			$prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1368
			// For natural search
1369
			$scrit = explode(' ', $filterkey);
1370
			$i = 0;
1371
			if (count($scrit) > 1) {
1372
				$sql .= "(";
1373
			}
1374
			foreach ($scrit as $crit) {
1375
				if ($i > 0) {
1376
					$sql .= " AND ";
1377
				}
1378
				$sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1379
				$i++;
1380
			}
1381
			if (count($scrit) > 1) {
1382
				$sql .= ")";
1383
			}
1384
			if (!empty($conf->barcode->enabled)) {
1385
				$sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1386
			}
1387
			$sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1388
			$sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1389
			$sql .= ")";
1390
		}
1391
		$sql .= $this->db->order("nom", "ASC");
1392
		$sql .= $this->db->plimit($limit, 0);
1393
1394
		// Build output string
1395
		dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1396
		$resql = $this->db->query($sql);
1397
		if ($resql) {
1398
			if (!$forcecombo) {
1399
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1400
				$out .= ajax_combobox($htmlname, $events, $conf->global->COMPANY_USE_SEARCH_TO_SELECT);
1401
			}
1402
1403
			// Construct $out and $outarray
1404
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1405
1406
			$textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1407
			if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1408
				// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1409
				//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1410
				if ($showempty && !is_numeric($showempty)) {
1411
					$textifempty = $langs->trans($showempty);
1412
				} else {
1413
					$textifempty .= $langs->trans("All");
1414
				}
1415
			}
1416
			if ($showempty) {
1417
				$out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.$textifempty.'</span>').'">'.$textifempty.'</option>'."\n";
1418
			}
1419
1420
			$num = $this->db->num_rows($resql);
1421
			$i = 0;
1422
			if ($num) {
1423
				while ($i < $num) {
1424
					$obj = $this->db->fetch_object($resql);
1425
					$label = '';
1426
					if ($conf->global->SOCIETE_ADD_REF_IN_LIST) {
1427
						if (($obj->client) && (!empty($obj->code_client))) {
1428
							$label = $obj->code_client.' - ';
1429
						}
1430
						if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1431
							$label .= $obj->code_fournisseur.' - ';
1432
						}
1433
						$label .= ' '.$obj->name;
1434
					} else {
1435
						$label = $obj->name;
1436
					}
1437
1438
					if (!empty($obj->name_alias)) {
1439
						$label .= ' ('.$obj->name_alias.')';
1440
					}
1441
1442
					if ($showtype) {
1443
						if ($obj->client || $obj->fournisseur) {
1444
							$label .= ' (';
1445
						}
1446
						if ($obj->client == 1 || $obj->client == 3) {
1447
							$label .= $langs->trans("Customer");
1448
						}
1449
						if ($obj->client == 2 || $obj->client == 3) {
1450
							$label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1451
						}
1452
						if ($obj->fournisseur) {
1453
							$label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1454
						}
1455
						if ($obj->client || $obj->fournisseur) {
1456
							$label .= ')';
1457
						}
1458
					}
1459
1460
					if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1461
						$label .= ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1462
						if (!empty($obj->country_code)) {
1463
							$label .= ', '.$langs->trans('Country'.$obj->country_code);
1464
						}
1465
					}
1466
1467
					if (empty($outputmode)) {
1468
						if (in_array($obj->rowid, $selected)) {
1469
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
1470
						} else {
1471
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
1472
						}
1473
					} else {
1474
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
1475
					}
1476
1477
					$i++;
1478
					if (($i % 10) == 0) {
1479
						$out .= "\n";
1480
					}
1481
				}
1482
			}
1483
			$out .= '</select>'."\n";
1484
		} else {
1485
			dol_print_error($this->db);
1486
		}
1487
1488
		$this->result = array('nbofthirdparties'=>$num);
1489
1490
		if ($outputmode) {
1491
			return $outarray;
1492
		}
1493
		return $out;
1494
	}
1495
1496
1497
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1498
	/**
1499
	 *  Return HTML combo list of absolute discounts
1500
	 *
1501
	 *  @param	string	$selected       Id remise fixe pre-selectionnee
1502
	 *  @param  string	$htmlname       Nom champ formulaire
1503
	 *  @param  string	$filter         Criteres optionnels de filtre
1504
	 *  @param	int		$socid			Id of thirdparty
1505
	 *  @param	int		$maxvalue		Max value for lines that can be selected
1506
	 *  @return	int						Return number of qualifed lines in list
1507
	 */
1508
	public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1509
	{
1510
		// phpcs:enable
1511
		global $langs, $conf;
1512
1513
		// On recherche les remises
1514
		$sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1515
		$sql .= " re.description, re.fk_facture_source";
1516
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1517
		$sql .= " WHERE re.fk_soc = ".(int) $socid;
1518
		$sql .= " AND re.entity = ".$conf->entity;
1519
		if ($filter) {
1520
			$sql .= " AND ".$filter;
1521
		}
1522
		$sql .= " ORDER BY re.description ASC";
1523
1524
		dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1525
		$resql = $this->db->query($sql);
1526
		if ($resql) {
1527
			print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1528
			$num = $this->db->num_rows($resql);
1529
1530
			$qualifiedlines = $num;
1531
1532
			$i = 0;
1533
			if ($num) {
1534
				print '<option value="0">&nbsp;</option>';
1535
				while ($i < $num) {
1536
					$obj = $this->db->fetch_object($resql);
1537
					$desc = dol_trunc($obj->description, 40);
1538
					if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
1539
						$desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1540
					}
1541
					if (preg_match('/\(DEPOSIT\)/', $desc)) {
1542
						$desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1543
					}
1544
					if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
1545
						$desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1546
					}
1547
					if (preg_match('/\(EXCESS PAID\)/', $desc)) {
1548
						$desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1549
					}
1550
1551
					$selectstring = '';
1552
					if ($selected > 0 && $selected == $obj->rowid) {
1553
						$selectstring = ' selected';
1554
					}
1555
1556
					$disabled = '';
1557
					if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1558
						$qualifiedlines--;
1559
						$disabled = ' disabled';
1560
					}
1561
1562
					if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1563
						$tmpfac = new Facture($this->db);
1564
						if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1565
							$desc = $desc.' - '.$tmpfac->ref;
1566
						}
1567
					}
1568
1569
					print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1570
					$i++;
1571
				}
1572
			}
1573
			print '</select>';
1574
			return $qualifiedlines;
1575
		} else {
1576
			dol_print_error($this->db);
1577
			return -1;
1578
		}
1579
	}
1580
1581
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1582
	/**
1583
	 *  Return list of all contacts (for a third party or all)
1584
	 *
1585
	 *  @param	int		$socid      	Id ot third party or 0 for all
1586
	 *  @param  string	$selected   	Id contact pre-selectionne
1587
	 *  @param  string	$htmlname  	    Name of HTML field ('none' for a not editable field)
1588
	 *  @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
1589
	 *  @param  string	$exclude        List of contacts id to exclude
1590
	 *  @param	string	$limitto		Disable answers that are not id in this array list
1591
	 *  @param	integer	$showfunction   Add function into label
1592
	 *  @param	string	$moreclass		Add more class to class style
1593
	 *  @param	integer	$showsoc	    Add company into label
1594
	 *  @param	int		$forcecombo		Force to use combo box
1595
	 *  @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')))
1596
	 *  @param	bool	$options_only	Return options only (for ajax treatment)
1597
	 *  @param	string	$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1598
	 *  @param	string	$htmlid			Html id to use instead of htmlname
1599
	 *  @return	int						<0 if KO, Nb of contact in list if OK
1600
	 *  @deprecated						You can use selectcontacts directly (warning order of param was changed)
1601
	 */
1602
	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 = '')
1603
	{
1604
		// phpcs:enable
1605
		print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1606
		return $this->num;
1607
	}
1608
1609
	/**
1610
	 *	Return HTML code of the SELECT of list of all contacts (for a third party or all).
1611
	 *  This also set the number of contacts found into $this->num
1612
	 *
1613
	 * @since 9.0 Add afterSelectContactOptions hook
1614
	 *
1615
	 *	@param	int			$socid      	Id ot third party or 0 for all or -1 for empty list
1616
	 *	@param  array|int	$selected   	Array of ID of pre-selected contact id
1617
	 *	@param  string		$htmlname  	    Name of HTML field ('none' for a not editable field)
1618
	 *	@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
1619
	 *	@param  string		$exclude        List of contacts id to exclude
1620
	 *	@param	string		$limitto		Disable answers that are not id in this array list
1621
	 *	@param	integer		$showfunction   Add function into label
1622
	 *	@param	string		$moreclass		Add more class to class style
1623
	 *	@param	bool		$options_only	Return options only (for ajax treatment)
1624
	 *	@param	integer		$showsoc	    Add company into label
1625
	 * 	@param	int			$forcecombo		Force to use combo box (so no ajax beautify effect)
1626
	 *  @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')))
1627
	 *  @param	string		$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1628
	 *  @param	string		$htmlid			Html id to use instead of htmlname
1629
	 *  @param	bool		$multiple		add [] in the name of element and add 'multiple' attribut
1630
	 *  @param	integer		$disableifempty Set tag 'disabled' on select if there is no choice
1631
	 *	@return	 int|string					<0 if KO, HTML with select string if OK.
1632
	 */
1633
	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)
1634
	{
1635
		global $conf, $langs, $hookmanager, $action;
1636
1637
		$langs->load('companies');
1638
1639
		if (empty($htmlid)) {
1640
			$htmlid = $htmlname;
1641
		}
1642
		$num = 0;
1643
1644
		if ($selected === '') {
1645
			$selected = array();
1646
		} elseif (!is_array($selected)) {
1647
			$selected = array($selected);
1648
		}
1649
		$out = '';
1650
1651
		if (!is_object($hookmanager)) {
1652
			include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1653
			$hookmanager = new HookManager($this->db);
1654
		}
1655
1656
		// We search third parties
1657
		$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";
1658
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1659
			$sql .= ", s.nom as company, s.town AS company_town";
1660
		}
1661
		$sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp";
1662
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1663
			$sql .= " LEFT OUTER JOIN  ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc";
1664
		}
1665
		$sql .= " WHERE sp.entity IN (".getEntity('socpeople').")";
1666
		if ($socid > 0 || $socid == -1) {
1667
			$sql .= " AND sp.fk_soc=".$socid;
1668
		}
1669
		if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1670
			$sql .= " AND sp.statut <> 0";
1671
		}
1672
		$sql .= " ORDER BY sp.lastname ASC";
1673
1674
		dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1675
		$resql = $this->db->query($sql);
1676
		if ($resql) {
1677
			$num = $this->db->num_rows($resql);
1678
1679
			if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1680
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1681
				$out .= ajax_combobox($htmlid, $events, $conf->global->CONTACT_USE_SEARCH_TO_SELECT);
1682
			}
1683
1684
			if ($htmlname != 'none' && !$options_only) {
1685
				$out .= '<select class="flat'.($moreclass ? ' '.$moreclass : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1686
			}
1687
1688
			if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
1689
				$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1690
			}
1691
			if ($showempty == 2) {
1692
				$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1693
			}
1694
1695
			$i = 0;
1696
			if ($num) {
1697
				include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1698
				$contactstatic = new Contact($this->db);
1699
1700
				while ($i < $num) {
1701
					$obj = $this->db->fetch_object($resql);
1702
1703
					// Set email (or phones) and town extended infos
1704
					$extendedInfos = '';
1705
					if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1706
						$extendedInfos = array();
1707
						$email = trim($obj->email);
1708
						if (!empty($email)) {
1709
							$extendedInfos[] = $email;
1710
						} else {
1711
							$phone = trim($obj->phone);
1712
							$phone_perso = trim($obj->phone_perso);
1713
							$phone_mobile = trim($obj->phone_mobile);
1714
							if (!empty($phone)) {
1715
								$extendedInfos[] = $phone;
1716
							}
1717
							if (!empty($phone_perso)) {
1718
								$extendedInfos[] = $phone_perso;
1719
							}
1720
							if (!empty($phone_mobile)) {
1721
								$extendedInfos[] = $phone_mobile;
1722
							}
1723
						}
1724
						$contact_town = trim($obj->contact_town);
1725
						$company_town = trim($obj->company_town);
1726
						if (!empty($contact_town)) {
1727
							$extendedInfos[] = $contact_town;
1728
						} elseif (!empty($company_town)) {
1729
							$extendedInfos[] = $company_town;
1730
						}
1731
						$extendedInfos = implode(' - ', $extendedInfos);
1732
						if (!empty($extendedInfos)) {
1733
							$extendedInfos = ' - '.$extendedInfos;
1734
						}
1735
					}
1736
1737
					$contactstatic->id = $obj->rowid;
1738
					$contactstatic->lastname = $obj->lastname;
1739
					$contactstatic->firstname = $obj->firstname;
1740
					if ($obj->statut == 1) {
1741
						if ($htmlname != 'none') {
1742
							$disabled = 0;
1743
							if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1744
								$disabled = 1;
1745
							}
1746
							if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1747
								$disabled = 1;
1748
							}
1749
							if (!empty($selected) && in_array($obj->rowid, $selected)) {
1750
								$out .= '<option value="'.$obj->rowid.'"';
1751
								if ($disabled) {
1752
									$out .= ' disabled';
1753
								}
1754
								$out .= ' selected>';
1755
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1756
								if ($showfunction && $obj->poste) {
1757
									$out .= ' ('.$obj->poste.')';
1758
								}
1759
								if (($showsoc > 0) && $obj->company) {
1760
									$out .= ' - ('.$obj->company.')';
1761
								}
1762
								$out .= '</option>';
1763
							} else {
1764
								$out .= '<option value="'.$obj->rowid.'"';
1765
								if ($disabled) {
1766
									$out .= ' disabled';
1767
								}
1768
								$out .= '>';
1769
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1770
								if ($showfunction && $obj->poste) {
1771
									$out .= ' ('.$obj->poste.')';
1772
								}
1773
								if (($showsoc > 0) && $obj->company) {
1774
									$out .= ' - ('.$obj->company.')';
1775
								}
1776
								$out .= '</option>';
1777
							}
1778
						} else {
1779
							if (in_array($obj->rowid, $selected)) {
1780
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1781
								if ($showfunction && $obj->poste) {
1782
									$out .= ' ('.$obj->poste.')';
1783
								}
1784
								if (($showsoc > 0) && $obj->company) {
1785
									$out .= ' - ('.$obj->company.')';
1786
								}
1787
							}
1788
						}
1789
					}
1790
					$i++;
1791
				}
1792
			} else {
1793
				$labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1794
				$out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1795
				$out .= $labeltoshow;
1796
				$out .= '</option>';
1797
			}
1798
1799
			$parameters = array(
1800
				'socid'=>$socid,
1801
				'htmlname'=>$htmlname,
1802
				'resql'=>$resql,
1803
				'out'=>&$out,
1804
				'showfunction'=>$showfunction,
1805
				'showsoc'=>$showsoc,
1806
			);
1807
1808
			$reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1809
1810
			if ($htmlname != 'none' && !$options_only) {
1811
				$out .= '</select>';
1812
			}
1813
1814
			$this->num = $num;
1815
			return $out;
1816
		} else {
1817
			dol_print_error($this->db);
1818
			return -1;
1819
		}
1820
	}
1821
1822
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1823
	/**
1824
	 *	Return the HTML select list of users
1825
	 *
1826
	 *  @param	string			$selected       Id user preselected
1827
	 *  @param  string			$htmlname       Field name in form
1828
	 *  @param  int				$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
1829
	 *  @param  array			$exclude        Array list of users id to exclude
1830
	 * 	@param	int				$disabled		If select list must be disabled
1831
	 *  @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
1832
	 * 	@param	int				$enableonly		Array list of users id to be enabled. All other must be disabled
1833
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1834
	 * 	@return	void
1835
	 *  @deprecated		Use select_dolusers instead
1836
	 *  @see select_dolusers()
1837
	 */
1838
	public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1839
	{
1840
		// phpcs:enable
1841
		print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1842
	}
1843
1844
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1845
	/**
1846
	 *	Return select list of users
1847
	 *
1848
	 *  @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)
1849
	 *  @param  string			$htmlname       Field name in form
1850
	 *  @param  int|string		$show_empty     0=list with no empty value, 1=add also an empty value into list
1851
	 *  @param  array			$exclude        Array list of users id to exclude
1852
	 * 	@param	int				$disabled		If select list must be disabled
1853
	 *  @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
1854
	 * 	@param	array			$enableonly		Array list of users id to be enabled. If defined, it means that others will be disabled
1855
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1856
	 *  @param	int				$maxlength		Maximum length of string into list (0=no limit)
1857
	 *  @param	int				$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
1858
	 *  @param	string			$morefilter		Add more filters into sql request (Example: 'employee = 1'). This value must not come from user input.
1859
	 *  @param	integer			$show_every		0=default list, 1=add also a value "Everybody" at beginning of list
1860
	 *  @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.
1861
	 *  @param	string			$morecss		More css
1862
	 *  @param  int     		$noactive       Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
1863
	 *  @param  int				$outputmode     0=HTML select string, 1=Array
1864
	 *  @param  bool			$multiple       add [] in the name of element and add 'multiple' attribut
1865
	 * 	@return	string							HTML select string
1866
	 *  @see select_dolgroups()
1867
	 */
1868
	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)
1869
	{
1870
		// phpcs:enable
1871
		global $conf, $user, $langs, $hookmanager;
1872
1873
		// If no preselected user defined, we take current user
1874
		if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1875
			$selected = $user->id;
1876
		}
1877
1878
		if ($selected === '') {
1879
			$selected = array();
1880
		} elseif (!is_array($selected)) {
1881
			$selected = array($selected);
1882
		}
1883
1884
		$excludeUsers = null;
1885
		$includeUsers = null;
1886
1887
		// Permettre l'exclusion d'utilisateurs
1888
		if (is_array($exclude)) {
1889
			$excludeUsers = implode(",", $exclude);
1890
		}
1891
		// Permettre l'inclusion d'utilisateurs
1892
		if (is_array($include)) {
1893
			$includeUsers = implode(",", $include);
1894
		} elseif ($include == 'hierarchy') {
1895
			// Build list includeUsers to have only hierarchy
1896
			$includeUsers = implode(",", $user->getAllChildIds(0));
1897
		} elseif ($include == 'hierarchyme') {
1898
			// Build list includeUsers to have only hierarchy and current user
1899
			$includeUsers = implode(",", $user->getAllChildIds(1));
1900
		}
1901
1902
		$out = '';
1903
		$outarray = array();
1904
1905
		// Forge request to select users
1906
		$sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1907
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1908
			$sql .= ", e.label";
1909
		}
1910
		$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
1911
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1912
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid = u.entity";
1913
			if ($force_entity) {
1914
				$sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
1915
			} else {
1916
				$sql .= " WHERE u.entity IS NOT NULL";
1917
			}
1918
		} else {
1919
			if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1920
				$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug";
1921
				$sql .= " ON ug.fk_user = u.rowid";
1922
				$sql .= " WHERE ug.entity = ".$conf->entity;
1923
			} else {
1924
				$sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
1925
			}
1926
		}
1927
		if (!empty($user->socid)) {
1928
			$sql .= " AND u.fk_soc = ".((int) $user->socid);
1929
		}
1930
		if (is_array($exclude) && $excludeUsers) {
1931
			$sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
1932
		}
1933
		if ($includeUsers) {
1934
			$sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
1935
		}
1936
		if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) {
1937
			$sql .= " AND u.statut <> 0";
1938
		}
1939
		if (!empty($morefilter)) {
1940
			$sql .= " ".$morefilter;
1941
		}
1942
1943
		//Add hook to filter on user (for exemple on usergroup define in custom modules)
1944
		$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...
1945
		if (!empty($reshook)) {
1946
			$sql .= $hookmanager->resPrint;
1947
		}
1948
1949
		if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {	// MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
1950
			$sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
1951
		} else {
1952
			$sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
1953
		}
1954
1955
		dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
1956
1957
		$resql = $this->db->query($sql);
1958
		if ($resql) {
1959
			$num = $this->db->num_rows($resql);
1960
			$i = 0;
1961
			if ($num) {
1962
				// do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
1963
				$out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
1964
				if ($show_empty && !$multiple) {
1965
					$textforempty = ' ';
1966
					if (!empty($conf->use_javascript_ajax)) {
1967
						$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
1968
					}
1969
					if (!is_numeric($show_empty)) {
1970
						$textforempty = $show_empty;
1971
					}
1972
					$out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
1973
				}
1974
				if ($show_every) {
1975
					$out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
1976
				}
1977
1978
				$userstatic = new User($this->db);
1979
1980
				while ($i < $num) {
1981
					$obj = $this->db->fetch_object($resql);
1982
1983
					$userstatic->id = $obj->rowid;
1984
					$userstatic->lastname = $obj->lastname;
1985
					$userstatic->firstname = $obj->firstname;
1986
					$userstatic->photo = $obj->photo;
1987
					$userstatic->statut = $obj->status;
1988
					$userstatic->entity = $obj->entity;
1989
					$userstatic->admin = $obj->admin;
1990
1991
					$disableline = '';
1992
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
1993
						$disableline = ($enableonlytext ? $enableonlytext : '1');
1994
					}
1995
1996
					$labeltoshow = '';
1997
1998
					// $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
1999
					$fullNameMode = 0;
2000
					if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2001
						$fullNameMode = 1; //Firstname+lastname
2002
					}
2003
					$labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2004
					if (empty($obj->firstname) && empty($obj->lastname)) {
2005
						$labeltoshow .= $obj->login;
2006
					}
2007
2008
					// Complete name with more info
2009
					$moreinfo = '';
2010
					if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2011
						$moreinfo .= ($moreinfo ? ' - ' : ' (').$obj->login;
2012
					}
2013
					if ($showstatus >= 0) {
2014
						if ($obj->status == 1 && $showstatus == 1) {
2015
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2016
						}
2017
						if ($obj->status == 0 && $showstatus == 1) {
2018
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2019
						}
2020
					}
2021
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2022
						if (!$obj->entity) {
2023
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2024
						} else {
2025
							if ($obj->entity != $conf->entity) {
2026
								$moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2027
							}
2028
						}
2029
					}
2030
					$moreinfo .= ($moreinfo ? ')' : '');
2031
					if ($disableline && $disableline != '1') {
2032
						$moreinfo .= ' - '.$disableline; // This is text from $enableonlytext parameter
2033
					}
2034
					$labeltoshow .= $moreinfo;
2035
2036
					$out .= '<option value="'.$obj->rowid.'"';
2037
					if ($disableline) {
2038
						$out .= ' disabled';
2039
					}
2040
					if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2041
						$out .= ' selected';
2042
					}
2043
					$out .= ' data-html="';
2044
					$outhtml = '';
2045
					// if (!empty($obj->photo)) {
2046
					$outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2047
					// }
2048
					if ($showstatus >= 0 && $obj->status == 0) {
2049
						$outhtml .= '<strike class="opacitymediumxxx">';
2050
					}
2051
					$outhtml .= $labeltoshow;
2052
					if ($showstatus >= 0 && $obj->status == 0) {
2053
						$outhtml .= '</strike>';
2054
					}
2055
					$out .= dol_escape_htmltag($outhtml);
2056
					$out .= '">';
2057
					$out .= $labeltoshow;
2058
					$out .= '</option>';
2059
2060
					$outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2061
2062
					$i++;
2063
				}
2064
			} else {
2065
				$out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2066
				$out .= '<option value="">'.$langs->trans("None").'</option>';
2067
			}
2068
			$out .= '</select>';
2069
2070
			if ($num) {
2071
				// Enhance with select2
2072
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2073
				$out .= ajax_combobox($htmlname);
2074
			}
2075
		} else {
2076
			dol_print_error($this->db);
2077
		}
2078
2079
		if ($outputmode) {
2080
			return $outarray;
2081
		}
2082
		return $out;
2083
	}
2084
2085
2086
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2087
	/**
2088
	 *	Return select list of users. Selected users are stored into session.
2089
	 *  List of users are provided into $_SESSION['assignedtouser'].
2090
	 *
2091
	 *  @param  string	$action         Value for $action
2092
	 *  @param  string	$htmlname       Field name in form
2093
	 *  @param  int		$show_empty     0=list without the empty value, 1=add empty value
2094
	 *  @param  array	$exclude        Array list of users id to exclude
2095
	 * 	@param	int		$disabled		If select list must be disabled
2096
	 *  @param  array	$include        Array list of users id to include or 'hierarchy' to have only supervised users
2097
	 * 	@param	array	$enableonly		Array list of users id to be enabled. All other must be disabled
2098
	 *  @param	int		$force_entity	'0' or Ids of environment to force
2099
	 *  @param	int		$maxlength		Maximum length of string into list (0=no limit)
2100
	 *  @param	int		$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2101
	 *  @param	string	$morefilter		Add more filters into sql request
2102
	 *  @param	int		$showproperties		Show properties of each attendees
2103
	 *  @param	array	$listofuserid		Array with properties of each user
2104
	 *  @param	array	$listofcontactid	Array with properties of each contact
2105
	 *  @param	array	$listofotherid		Array with properties of each other contact
2106
	 * 	@return	string					HTML select string
2107
	 *  @see select_dolgroups()
2108
	 */
2109
	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())
2110
	{
2111
		// phpcs:enable
2112
		global $conf, $user, $langs;
2113
2114
		$userstatic = new User($this->db);
2115
		$out = '';
2116
2117
2118
		$assignedtouser = array();
2119
		if (!empty($_SESSION['assignedtouser'])) {
2120
			$assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2121
		}
2122
		$nbassignetouser = count($assignedtouser);
2123
2124
		//if ($nbassignetouser && $action != 'view') $out .= '<br>';
2125
		if ($nbassignetouser) {
2126
			$out .= '<ul class="attendees">';
2127
		}
2128
		$i = 0;
2129
		$ownerid = 0;
2130
		foreach ($assignedtouser as $key => $value) {
2131
			if ($value['id'] == $ownerid) {
2132
				continue;
2133
			}
2134
2135
			$out .= '<li>';
2136
			$userstatic->fetch($value['id']);
2137
			$out .= $userstatic->getNomUrl(-1);
2138
			if ($i == 0) {
2139
				$ownerid = $value['id'];
2140
				$out .= ' ('.$langs->trans("Owner").')';
2141
			}
2142
			if ($nbassignetouser > 1 && $action != 'view') {
2143
				$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.'">';
2144
			}
2145
			// Show my availability
2146
			if ($showproperties) {
2147
				if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2148
					$out .= '<div class="myavailability inline-block">';
2149
					$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>';
2150
					$out .= '</div>';
2151
				}
2152
			}
2153
			//$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2154
			//$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2155
2156
			$out .= '</li>';
2157
			$i++;
2158
		}
2159
		if ($nbassignetouser) {
2160
			$out .= '</ul>';
2161
		}
2162
2163
		// Method with no ajax
2164
		if ($action != 'view') {
2165
			$out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2166
			$out .= '<script type="text/javascript" language="javascript">jQuery(document).ready(function () {';
2167
			$out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2168
			$out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2169
			$out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2170
			$out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2171
			$out .= '});';
2172
			$out .= '})</script>';
2173
			$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2174
			$out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2175
			$out .= '<br>';
2176
		}
2177
2178
		return $out;
2179
	}
2180
2181
2182
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2183
	/**
2184
	 *  Return list of products for customer in Ajax if Ajax activated or go to select_produits_list
2185
	 *
2186
	 *  @param		int			$selected				Preselected products
2187
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
2188
	 *  @param		int|string	$filtertype				Filter on product type (''=nofilter, 0=product, 1=service)
2189
	 *  @param		int			$limit					Limit on number of returned lines
2190
	 *  @param		int			$price_level			Level of price to show
2191
	 *  @param		int			$status					Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell
2192
	 *  @param		int			$finished				2=all, 1=finished, 0=raw material
2193
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
2194
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
2195
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
2196
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
2197
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2198
	 * 	@param		int			$forcecombo				Force to use combo box
2199
	 *  @param      string      $morecss                Add more css on select
2200
	 *  @param      int         $hidepriceinlabel       1=Hide prices in label
2201
	 *  @param      string      $warehouseStatus        Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used
2202
	 *										            'warehouseopen' = count products from open warehouses,
2203
	 *										            'warehouseclosed' = count products from closed warehouses,
2204
	 *										            'warehouseinternal' = count products from warehouses for internal correct/transfer only
2205
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
2206
	 *  @param		string		$nooutput				No print, return the output into a string
2207
	 *  @return		void|string
2208
	 */
2209
	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)
2210
	{
2211
		// phpcs:enable
2212
		global $langs, $conf;
2213
2214
		$out = '';
2215
2216
		// check parameters
2217
		$price_level = (!empty($price_level) ? $price_level : 0);
2218
		if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
2219
			$ajaxoptions = array();
2220
		}
2221
2222
		if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) {
2223
			if (!empty($conf->product->enabled) && empty($conf->service->enabled)) {
2224
				$filtertype = '0';
2225
			} elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) {
2226
				$filtertype = '1';
2227
			}
2228
		}
2229
2230
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2231
			$placeholder = '';
2232
2233
			if ($selected && empty($selected_input_value)) {
2234
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2235
				$producttmpselect = new Product($this->db);
2236
				$producttmpselect->fetch($selected);
2237
				$selected_input_value = $producttmpselect->ref;
2238
				unset($producttmpselect);
2239
			}
2240
			// handle case where product or service module is disabled + no filter specified
2241
			if ($filtertype == '') {
2242
				if (empty($conf->product->enabled)) { // when product module is disabled, show services only
2243
					$filtertype = 1;
2244
				} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2245
					$filtertype = 0;
2246
				}
2247
			}
2248
			// mode=1 means customers products
2249
			$urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
2250
			//Price by customer
2251
			if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2252
				$urloption .= '&socid='.$socid;
2253
			}
2254
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2255
2256
			if (!empty($conf->variants->enabled) && is_array($selected_combinations)) {
2257
				// Code to automatically insert with javascript the select of attributes under the select of product
2258
				// when a parent of variant has been selected.
2259
				$out .= '
2260
				<!-- script to auto show attributes select tags if a variant was selected -->
2261
				<script>
2262
					// auto show attributes fields
2263
					selected = '.json_encode($selected_combinations).';
2264
					combvalues = {};
2265
2266
					jQuery(document).ready(function () {
2267
2268
						jQuery("input[name=\'prod_entry_mode\']").change(function () {
2269
							if (jQuery(this).val() == \'free\') {
2270
								jQuery(\'div#attributes_box\').empty();
2271
							}
2272
						});
2273
2274
						jQuery("input#'.$htmlname.'").change(function () {
2275
2276
							if (!jQuery(this).val()) {
2277
								jQuery(\'div#attributes_box\').empty();
2278
								return;
2279
							}
2280
2281
							console.log("A change has started. We get variants fields to inject html select");
2282
2283
							jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2284
								id: jQuery(this).val()
2285
							}, function (data) {
2286
								jQuery(\'div#attributes_box\').empty();
2287
2288
								jQuery.each(data, function (key, val) {
2289
2290
									combvalues[val.id] = val.values;
2291
2292
									var span = jQuery(document.createElement(\'div\')).css({
2293
										\'display\': \'table-row\'
2294
									});
2295
2296
									span.append(
2297
										jQuery(document.createElement(\'div\')).text(val.label).css({
2298
											\'font-weight\': \'bold\',
2299
											\'display\': \'table-cell\'
2300
										})
2301
									);
2302
2303
									var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2304
										\'margin-left\': \'15px\',
2305
										\'white-space\': \'pre\'
2306
									}).append(
2307
										jQuery(document.createElement(\'option\')).val(\'\')
2308
									);
2309
2310
									jQuery.each(combvalues[val.id], function (key, val) {
2311
										var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2312
2313
										if (selected[val.fk_product_attribute] == val.id) {
2314
											tag.attr(\'selected\', \'selected\');
2315
										}
2316
2317
										html.append(tag);
2318
									});
2319
2320
									span.append(html);
2321
									jQuery(\'div#attributes_box\').append(span);
2322
								});
2323
							})
2324
						});
2325
2326
						'.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2327
					});
2328
				</script>
2329
                ';
2330
			}
2331
2332
			if (empty($hidelabel)) {
2333
				$out .= $langs->trans("RefOrLabel").' : ';
2334
			} elseif ($hidelabel > 1) {
2335
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2336
				if ($hidelabel == 2) {
2337
					$out .= img_picto($langs->trans("Search"), 'search');
2338
				}
2339
			}
2340
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
2341
			if ($hidelabel == 3) {
2342
				$out .= img_picto($langs->trans("Search"), 'search');
2343
			}
2344
		} else {
2345
			$out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus);
2346
		}
2347
2348
		if (empty($nooutput)) {
2349
			print $out;
2350
		} else {
2351
			return $out;
2352
		}
2353
	}
2354
2355
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2356
	/**
2357
	 *	Return list of products for a customer.
2358
	 *  Called by select_produits.
2359
	 *
2360
	 *	@param      int		$selected           Preselected product
2361
	 *	@param      string	$htmlname           Name of select html
2362
	 *  @param		string	$filtertype         Filter on product type (''=nofilter, 0=product, 1=service)
2363
	 *	@param      int		$limit              Limit on number of returned lines
2364
	 *	@param      int		$price_level        Level of price to show
2365
	 * 	@param      string	$filterkey          Filter on product
2366
	 *	@param		int		$status             -1=Return all products, 0=Products not on sell, 1=Products on sell
2367
	 *  @param      int		$finished           Filter on finished field: 2=No filter
2368
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
2369
	 *  @param      int		$socid     		    Thirdparty Id (to get also price dedicated to this customer)
2370
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2371
	 * 	@param		int		$forcecombo		    Force to use combo box
2372
	 *  @param      string  $morecss            Add more css on select
2373
	 *  @param      int     $hidepriceinlabel   1=Hide prices in label
2374
	 *  @param      string  $warehouseStatus    Warehouse status filter to group/count stock. Following comma separated filter options can be used.
2375
	 *										    'warehouseopen' = count products from open warehouses,
2376
	 *										    'warehouseclosed' = count products from closed warehouses,
2377
	 *										    'warehouseinternal' = count products from warehouses for internal correct/transfer only
2378
	 *  @return     array    				    Array of keys for json
2379
	 */
2380
	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 = '')
2381
	{
2382
		// phpcs:enable
2383
		global $langs, $conf, $user, $db;
2384
2385
		$out = '';
2386
		$outarray = array();
2387
2388
		// Units
2389
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2390
			$langs->load('other');
2391
		}
2392
2393
		$warehouseStatusArray = array();
2394
		if (!empty($warehouseStatus)) {
2395
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2396
			if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2397
				$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2398
			}
2399
			if (preg_match('/warehouseopen/', $warehouseStatus)) {
2400
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2401
			}
2402
			if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2403
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2404
			}
2405
		}
2406
2407
		$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";
2408
		if (count($warehouseStatusArray)) {
2409
			$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
2410
		} else {
2411
			$selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2412
		}
2413
2414
		$sql = "SELECT ";
2415
		$sql .= $selectFields.$selectFieldsGrouped;
2416
2417
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2418
			//Product category
2419
			$sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie
2420
						FROM ".MAIN_DB_PREFIX."categorie_product
2421
						WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid
2422
						LIMIT 1
2423
				) AS categorie_product_id ";
2424
		}
2425
2426
		//Price by customer
2427
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2428
			$sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2429
			$sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.ref_customer as custref';
2430
			$selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custref";
2431
		}
2432
		// Units
2433
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2434
			$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";
2435
			$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';
2436
		}
2437
2438
		// Multilang : we add translation
2439
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2440
			$sql .= ", pl.label as label_translated";
2441
			$sql .= ", pl.description as description_translated";
2442
			$selectFields .= ", label_translated";
2443
			$selectFields .= ", description_translated";
2444
		}
2445
		// Price by quantity
2446
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2447
			$sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid";
2448
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2449
				$sql .= " AND price_level=".$price_level;
2450
			}
2451
			$sql .= " ORDER BY date_price";
2452
			$sql .= " DESC LIMIT 1) as price_rowid";
2453
			$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
2454
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2455
				$sql .= " AND price_level=".$price_level;
2456
			}
2457
			$sql .= " ORDER BY date_price";
2458
			$sql .= " DESC LIMIT 1) as price_by_qty";
2459
			$selectFields .= ", price_rowid, price_by_qty";
2460
		}
2461
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
2462
		if (count($warehouseStatusArray)) {
2463
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid";
2464
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2465
			$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.
2466
		}
2467
2468
		// include search in supplier ref
2469
		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2470
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2471
		}
2472
2473
		//Price by customer
2474
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2475
			$sql .= " LEFT JOIN  ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid";
2476
		}
2477
		// Units
2478
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2479
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
2480
		}
2481
		// Multilang : we add translation
2482
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2483
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid ";
2484
			if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2485
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2486
				$soc = new Societe($db);
2487
				$result = $soc->fetch($socid);
2488
				if ($result > 0 && !empty($soc->default_lang)) {
2489
					$sql .= " AND pl.lang='" . $this->db->escape($soc->default_lang) . "'";
2490
				} else {
2491
					$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2492
				}
2493
			} else {
2494
				$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2495
			}
2496
		}
2497
2498
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2499
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2500
		}
2501
2502
		$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2503
2504
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2505
			$sql .= " AND pac.rowid IS NULL";
2506
		}
2507
2508
		if ($finished == 0) {
2509
			$sql .= " AND p.finished = ".((int) $finished);
2510
		} elseif ($finished == 1) {
2511
			$sql .= " AND p.finished = ".((int) $finished);
2512
			if ($status >= 0) {
2513
				$sql .= " AND p.tosell = ".((int) $status);
2514
			}
2515
		} elseif ($status >= 0) {
2516
			$sql .= " AND p.tosell = ".((int) $status);
2517
		}
2518
		// Filter by product type
2519
		if (strval($filtertype) != '') {
2520
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
2521
		} elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only
2522
			$sql .= " AND p.fk_product_type = 1";
2523
		} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2524
			$sql .= " AND p.fk_product_type = 0";
2525
		}
2526
		// Add criteria on ref/label
2527
		if ($filterkey != '') {
2528
			$sql .= ' AND (';
2529
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2530
			// For natural search
2531
			$scrit = explode(' ', $filterkey);
2532
			$i = 0;
2533
			if (count($scrit) > 1) {
2534
				$sql .= "(";
2535
			}
2536
			foreach ($scrit as $crit) {
2537
				if ($i > 0) {
2538
					$sql .= " AND ";
2539
				}
2540
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2541
				if (!empty($conf->global->MAIN_MULTILANGS)) {
2542
					$sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2543
				}
2544
				if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) {
2545
					$sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2546
				}
2547
				if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2548
					$sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2549
					if (!empty($conf->global->MAIN_MULTILANGS)) {
2550
						$sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2551
					}
2552
				}
2553
				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2554
					$sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2555
				}
2556
				$sql .= ")";
2557
				$i++;
2558
			}
2559
			if (count($scrit) > 1) {
2560
				$sql .= ")";
2561
			}
2562
			if (!empty($conf->barcode->enabled)) {
2563
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2564
			}
2565
			$sql .= ')';
2566
		}
2567
		if (count($warehouseStatusArray)) {
2568
			$sql .= ' GROUP BY'.$selectFields;
2569
		}
2570
2571
		//Sort by category
2572
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2573
			$sql .= " ORDER BY categorie_product_id ";
2574
			//ASC OR DESC order
2575
			($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2576
		} else {
2577
			$sql .= $this->db->order("p.ref");
2578
		}
2579
2580
		$sql .= $this->db->plimit($limit, 0);
2581
2582
		// Build output string
2583
		dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2584
		$result = $this->db->query($sql);
2585
		if ($result) {
2586
			require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2587
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2588
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2589
2590
			$num = $this->db->num_rows($result);
2591
2592
			$events = null;
2593
2594
			if (!$forcecombo) {
2595
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2596
				$out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT);
2597
			}
2598
2599
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2600
2601
			$textifempty = '';
2602
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2603
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2604
			if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2605
				if ($showempty && !is_numeric($showempty)) {
2606
					$textifempty = $langs->trans($showempty);
2607
				} else {
2608
					$textifempty .= $langs->trans("All");
2609
				}
2610
			} else {
2611
				if ($showempty && !is_numeric($showempty)) {
2612
					$textifempty = $langs->trans($showempty);
2613
				}
2614
			}
2615
			if ($showempty) {
2616
				$out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2617
			}
2618
2619
			$i = 0;
2620
			while ($num && $i < $num) {
2621
				$opt = '';
2622
				$optJson = array();
2623
				$objp = $this->db->fetch_object($result);
2624
2625
				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
2626
					$sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2627
					$sql .= " FROM ".MAIN_DB_PREFIX."product_price_by_qty";
2628
					$sql .= " WHERE fk_product_price=".$objp->price_rowid;
2629
					$sql .= " ORDER BY quantity ASC";
2630
2631
					dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2632
					$result2 = $this->db->query($sql);
2633
					if ($result2) {
2634
						$nb_prices = $this->db->num_rows($result2);
2635
						$j = 0;
2636
						while ($nb_prices && $j < $nb_prices) {
2637
							$objp2 = $this->db->fetch_object($result2);
2638
2639
							$objp->price_by_qty_rowid = $objp2->rowid;
2640
							$objp->price_by_qty_price_base_type = $objp2->price_base_type;
2641
							$objp->price_by_qty_quantity = $objp2->quantity;
2642
							$objp->price_by_qty_unitprice = $objp2->unitprice;
2643
							$objp->price_by_qty_remise_percent = $objp2->remise_percent;
2644
							// For backward compatibility
2645
							$objp->quantity = $objp2->quantity;
2646
							$objp->price = $objp2->price;
2647
							$objp->unitprice = $objp2->unitprice;
2648
							$objp->remise_percent = $objp2->remise_percent;
2649
							$objp->remise = $objp2->remise;
2650
2651
							$this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2652
2653
							$j++;
2654
2655
							// Add new entry
2656
							// "key" value of json key array is used by jQuery automatically as selected value
2657
							// "label" value of json key array is used by jQuery automatically as text for combo box
2658
							$out .= $opt;
2659
							array_push($outarray, $optJson);
2660
						}
2661
					}
2662
				} else {
2663
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
2664
						$price_product = new Product($this->db);
2665
						$price_product->fetch($objp->rowid, '', '', 1);
2666
						$priceparser = new PriceParser($this->db);
2667
						$price_result = $priceparser->parseProduct($price_product);
2668
						if ($price_result >= 0) {
2669
							$objp->price = $price_result;
2670
							$objp->unitprice = $price_result;
2671
							//Calculate the VAT
2672
							$objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2673
							$objp->price_ttc = price2num($objp->price_ttc, 'MU');
2674
						}
2675
					}
2676
2677
					$this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2678
					// Add new entry
2679
					// "key" value of json key array is used by jQuery automatically as selected value
2680
					// "label" value of json key array is used by jQuery automatically as text for combo box
2681
					$out .= $opt;
2682
					array_push($outarray, $optJson);
2683
				}
2684
2685
				$i++;
2686
			}
2687
2688
			$out .= '</select>';
2689
2690
			$this->db->free($result);
2691
2692
			if (empty($outputmode)) {
2693
				return $out;
2694
			}
2695
			return $outarray;
2696
		} else {
2697
			dol_print_error($db);
2698
		}
2699
	}
2700
2701
	/**
2702
	 * constructProductListOption.
2703
	 * This define value for &$opt and &$optJson.
2704
	 *
2705
	 * @param 	resource	$objp			    Resultset of fetch
2706
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
2707
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
2708
	 * @param 	int			$price_level	    Price level
2709
	 * @param 	string		$selected		    Preselected value
2710
	 * @param   int         $hidepriceinlabel   Hide price in label
2711
	 * @param   string      $filterkey          Filter key to highlight
2712
	 * @param	int			$novirtualstock 	Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on.
2713
	 * @return	void
2714
	 */
2715
	protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2716
	{
2717
		global $langs, $conf, $user, $db;
2718
2719
		$outkey = '';
2720
		$outval = '';
2721
		$outref = '';
2722
		$outlabel = '';
2723
		$outlabel_translated = '';
2724
		$outdesc = '';
2725
		$outdesc_translated = '';
2726
		$outbarcode = '';
2727
		$outorigin = '';
2728
		$outtype = '';
2729
		$outprice_ht = '';
2730
		$outprice_ttc = '';
2731
		$outpricebasetype = '';
2732
		$outtva_tx = '';
2733
		$outqty = 1;
2734
		$outdiscount = 0;
2735
2736
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2737
2738
		$label = $objp->label;
2739
		if (!empty($objp->label_translated)) {
2740
			$label = $objp->label_translated;
2741
		}
2742
		if (!empty($filterkey) && $filterkey != '') {
2743
			$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2744
		}
2745
2746
		$outkey = $objp->rowid;
2747
		$outref = $objp->ref;
2748
		$outrefcust = empty($objp->custref) ? '' : $objp->custref;
2749
		$outlabel = $objp->label;
2750
		$outdesc = $objp->description;
2751
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2752
			$outlabel_translated = $objp->label_translated;
2753
			$outdesc_translated = $objp->description_translated;
2754
		}
2755
		$outbarcode = $objp->barcode;
2756
		$outorigin = $objp->fk_country;
2757
		$outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2758
2759
		$outtype = $objp->fk_product_type;
2760
		$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2761
		$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2762
2763
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2764
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2765
		}
2766
2767
		// Units
2768
		$outvalUnits = '';
2769
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2770
			if (!empty($objp->unit_short)) {
2771
				$outvalUnits .= ' - '.$objp->unit_short;
2772
			}
2773
		}
2774
		if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2775
			if (!empty($objp->weight) && $objp->weight_units !== null) {
2776
				$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2777
				$outvalUnits .= ' - '.$unitToShow;
2778
			}
2779
			if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2780
				$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2781
				$outvalUnits .= ' - '.$unitToShow;
2782
			}
2783
			if (!empty($objp->surface) && $objp->surface_units !== null) {
2784
				$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2785
				$outvalUnits .= ' - '.$unitToShow;
2786
			}
2787
			if (!empty($objp->volume) && $objp->volume_units !== null) {
2788
				$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2789
				$outvalUnits .= ' - '.$unitToShow;
2790
			}
2791
		}
2792
		if ($outdurationvalue && $outdurationunit) {
2793
			$da = array(
2794
				'h' => $langs->trans('Hour'),
2795
				'd' => $langs->trans('Day'),
2796
				'w' => $langs->trans('Week'),
2797
				'm' => $langs->trans('Month'),
2798
				'y' => $langs->trans('Year')
2799
			);
2800
			if (isset($da[$outdurationunit])) {
2801
				$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2802
			}
2803
		}
2804
2805
		$opt = '<option value="'.$objp->rowid.'"';
2806
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
2807
		if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2808
			$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.'"';
2809
		}
2810
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2811
			if (!empty($user->rights->stock->lire)) {
2812
				if ($objp->stock > 0) {
2813
					$opt .= ' class="product_line_stock_ok"';
2814
				} elseif ($objp->stock <= 0) {
2815
					$opt .= ' class="product_line_stock_too_low"';
2816
				}
2817
			}
2818
		}
2819
		if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2820
			$opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2821
			$opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2822
		}
2823
		$opt .= '>';
2824
		$opt .= $objp->ref;
2825
		if (! empty($objp->custref)) {
2826
			$opt.= ' (' . $objp->custref . ')';
2827
		}
2828
		if ($outbarcode) {
2829
			$opt .= ' ('.$outbarcode.')';
2830
		}
2831
		$opt .= ' - '.dol_trunc($label, $maxlengtharticle);
2832
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2833
			$opt .= ' ('.getCountry($outorigin, 1).')';
2834
		}
2835
2836
		$objRef = $objp->ref;
2837
		if (! empty($objp->custref)) {
2838
			$objRef .= ' (' . $objp->custref . ')';
2839
		}
2840
		if (!empty($filterkey) && $filterkey != '') {
2841
			$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2842
		}
2843
		$outval .= $objRef;
2844
		if ($outbarcode) {
2845
			$outval .= ' ('.$outbarcode.')';
2846
		}
2847
		$outval .= ' - '.dol_trunc($label, $maxlengtharticle);
2848
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2849
			$outval .= ' ('.getCountry($outorigin, 1).')';
2850
		}
2851
2852
		// Units
2853
		$opt .= $outvalUnits;
2854
		$outval .= $outvalUnits;
2855
2856
		$found = 0;
2857
2858
		// Multiprice
2859
		// If we need a particular price level (from 1 to 6)
2860
		if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
2861
			$sql = "SELECT price, price_ttc, price_base_type, tva_tx";
2862
			$sql .= " FROM ".MAIN_DB_PREFIX."product_price";
2863
			$sql .= " WHERE fk_product = ".((int) $objp->rowid);
2864
			$sql .= " AND entity IN (".getEntity('productprice').")";
2865
			$sql .= " AND price_level = ".((int) $price_level);
2866
			$sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
2867
			$sql .= " LIMIT 1";
2868
2869
			dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
2870
			$result2 = $this->db->query($sql);
2871
			if ($result2) {
2872
				$objp2 = $this->db->fetch_object($result2);
2873
				if ($objp2) {
2874
					$found = 1;
2875
					if ($objp2->price_base_type == 'HT') {
2876
						$opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2877
						$outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2878
					} else {
2879
						$opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2880
						$outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2881
					}
2882
					$outprice_ht = price($objp2->price);
2883
					$outprice_ttc = price($objp2->price_ttc);
2884
					$outpricebasetype = $objp2->price_base_type;
2885
					$outtva_tx = $objp2->tva_tx;
2886
				}
2887
			} else {
2888
				dol_print_error($this->db);
2889
			}
2890
		}
2891
2892
		// Price by quantity
2893
		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))) {
2894
			$found = 1;
2895
			$outqty = $objp->quantity;
2896
			$outdiscount = $objp->remise_percent;
2897
			if ($objp->quantity == 1) {
2898
				$opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
2899
				$outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
2900
				$opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
2901
				$outval .= $langs->transnoentities("Unit");
2902
			} else {
2903
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2904
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2905
				$opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
2906
				$outval .= $langs->transnoentities("Units");
2907
			}
2908
2909
			$outprice_ht = price($objp->unitprice);
2910
			$outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
2911
			$outpricebasetype = $objp->price_base_type;
2912
			$outtva_tx = $objp->tva_tx;
2913
		}
2914
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
2915
			$opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2916
			$outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2917
		}
2918
		if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
2919
			$opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
2920
			$outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
2921
		}
2922
2923
		// Price by customer
2924
		if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
2925
			if (!empty($objp->idprodcustprice)) {
2926
				$found = 1;
2927
2928
				if ($objp->custprice_base_type == 'HT') {
2929
					$opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2930
					$outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2931
				} else {
2932
					$opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2933
					$outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2934
				}
2935
2936
				$outprice_ht = price($objp->custprice);
2937
				$outprice_ttc = price($objp->custprice_ttc);
2938
				$outpricebasetype = $objp->custprice_base_type;
2939
				$outtva_tx = $objp->custtva_tx;
2940
			}
2941
		}
2942
2943
		// If level no defined or multiprice not found, we used the default price
2944
		if (empty($hidepriceinlabel) && !$found) {
2945
			if ($objp->price_base_type == 'HT') {
2946
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2947
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2948
			} else {
2949
				$opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2950
				$outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2951
			}
2952
			$outprice_ht = price($objp->price);
2953
			$outprice_ttc = price($objp->price_ttc);
2954
			$outpricebasetype = $objp->price_base_type;
2955
			$outtva_tx = $objp->tva_tx;
2956
		}
2957
2958
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2959
			if (!empty($user->rights->stock->lire)) {
2960
				$opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
2961
2962
				if ($objp->stock > 0) {
2963
					$outval .= ' - <span class="product_line_stock_ok">';
2964
				} elseif ($objp->stock <= 0) {
2965
					$outval .= ' - <span class="product_line_stock_too_low">';
2966
				}
2967
				$outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
2968
				$outval .= '</span>';
2969
				if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
2970
					$langs->load("stocks");
2971
2972
					$tmpproduct = new Product($this->db);
2973
					$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
2974
					$tmpproduct->load_virtual_stock();
2975
					$virtualstock = $tmpproduct->stock_theorique;
2976
2977
					$opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
2978
2979
					$outval .= ' - '.$langs->transnoentities("VirtualStock").':';
2980
					if ($virtualstock > 0) {
2981
						$outval .= '<span class="product_line_stock_ok">';
2982
					} elseif ($virtualstock <= 0) {
2983
						$outval .= '<span class="product_line_stock_too_low">';
2984
					}
2985
					$outval .= $virtualstock;
2986
					$outval .= '</span>';
2987
2988
					unset($tmpproduct);
2989
				}
2990
			}
2991
		}
2992
2993
		$opt .= "</option>\n";
2994
		$optJson = array(
2995
			'key'=>$outkey,
2996
			'value'=>$outref,
2997
			'label'=>$outval,
2998
			'label2'=>$outlabel,
2999
			'desc'=>$outdesc,
3000
			'type'=>$outtype,
3001
			'price_ht'=>price2num($outprice_ht),
3002
			'price_ttc'=>price2num($outprice_ttc),
3003
			'pricebasetype'=>$outpricebasetype,
3004
			'tva_tx'=>$outtva_tx, 'qty'=>$outqty,
3005
			'discount'=>$outdiscount,
3006
			'duration_value'=>$outdurationvalue,
3007
			'duration_unit'=>$outdurationunit,
3008
			'pbq'=>$outpbq,
3009
			'labeltrans'=>$outlabel_translated,
3010
			'desctrans'=>$outdesc_translated,
3011
			'ref_customer'=>$outrefcust
3012
		);
3013
	}
3014
3015
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3016
	/**
3017
	 *	Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list)
3018
	 *
3019
	 *	@param	int		$socid			Id third party
3020
	 *	@param  string	$selected       Preselected product
3021
	 *	@param  string	$htmlname       Name of HTML Select
3022
	 *  @param	string	$filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
3023
	 *	@param  string	$filtre			For a SQL filter
3024
	 *	@param	array	$ajaxoptions	Options for ajax_autocompleter
3025
	 *  @param	int		$hidelabel		Hide label (0=no, 1=yes)
3026
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3027
	 *  @param	string	$morecss		More CSS
3028
	 *  @param	string	$placeholder	Placeholder
3029
	 *	@return	void
3030
	 */
3031
	public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3032
	{
3033
		// phpcs:enable
3034
		global $langs, $conf;
3035
		global $price_level, $status, $finished;
3036
3037
		if (!isset($status)) {
3038
			$status = 1;
3039
		}
3040
3041
		$selected_input_value = '';
3042
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3043
			if ($selected > 0) {
3044
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3045
				$producttmpselect = new Product($this->db);
3046
				$producttmpselect->fetch($selected);
3047
				$selected_input_value = $producttmpselect->ref;
3048
				unset($producttmpselect);
3049
			}
3050
3051
			// mode=2 means suppliers products
3052
			$urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3053
			print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3054
			print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3055
		} else {
3056
			print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3057
		}
3058
	}
3059
3060
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3061
	/**
3062
	 *	Return list of suppliers products
3063
	 *
3064
	 *	@param	int		$socid   			Id of supplier thirdparty (0 = no filter)
3065
	 *	@param  int		$selected       	Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD')
3066
	 *	@param  string	$htmlname       	Name of HTML select
3067
	 *  @param	string	$filtertype     	Filter on product type (''=nofilter, 0=product, 1=service)
3068
	 *	@param  string	$filtre         	Generic filter. Data must not come from user input.
3069
	 *	@param  string	$filterkey      	Filter of produdts
3070
	 *  @param  int		$statut         	-1=Return all products, 0=Products not on buy, 1=Products on buy
3071
	 *  @param  int		$outputmode     	0=HTML select string, 1=Array
3072
	 *  @param  int     $limit          	Limit of line number
3073
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3074
	 *  @param	string	$morecss			Add more CSS
3075
	 *  @param	int		$showstockinlist	Show stock information (slower).
3076
	 *  @param	string	$placeholder		Placeholder
3077
	 *  @return array           			Array of keys for json
3078
	 */
3079
	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 = '')
3080
	{
3081
		// phpcs:enable
3082
		global $langs, $conf, $db, $user;
3083
3084
		$out = '';
3085
		$outarray = array();
3086
3087
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3088
3089
		$langs->load('stocks');
3090
		// Units
3091
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3092
			$langs->load('other');
3093
		}
3094
3095
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock,";
3096
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3097
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name,";
3098
		$sql .= " pfp.supplier_reputation";
3099
		// if we use supplier description of the products
3100
		if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3101
			$sql .= " ,pfp.desc_fourn as description";
3102
		} else {
3103
			$sql .= " ,p.description";
3104
		}
3105
		// Units
3106
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3107
			$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";
3108
		}
3109
		if (!empty($conf->barcode->enabled)) {
3110
			$sql .= ", pfp.barcode";
3111
		}
3112
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3113
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3114
		if ($socid > 0) {
3115
			$sql .= " AND pfp.fk_soc = ".((int) $socid);
3116
		}
3117
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3118
		// Units
3119
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3120
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
3121
		}
3122
		$sql .= " WHERE p.entity IN (".getEntity('product').")";
3123
		if ($statut != -1) {
3124
			$sql .= " AND p.tobuy = ".((int) $statut);
3125
		}
3126
		if (strval($filtertype) != '') {
3127
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
3128
		}
3129
		if (!empty($filtre)) {
3130
			$sql .= " ".$filtre;
3131
		}
3132
		// Add criteria on ref/label
3133
		if ($filterkey != '') {
3134
			$sql .= ' AND (';
3135
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3136
			// For natural search
3137
			$scrit = explode(' ', $filterkey);
3138
			$i = 0;
3139
			if (count($scrit) > 1) {
3140
				$sql .= "(";
3141
			}
3142
			foreach ($scrit as $crit) {
3143
				if ($i > 0) {
3144
					$sql .= " AND ";
3145
				}
3146
				$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)."%'";
3147
				if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3148
					$sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3149
				}
3150
				$sql .= ")";
3151
				$i++;
3152
			}
3153
			if (count($scrit) > 1) {
3154
				$sql .= ")";
3155
			}
3156
			if (!empty($conf->barcode->enabled)) {
3157
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3158
				$sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3159
			}
3160
			$sql .= ')';
3161
		}
3162
		$sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3163
		$sql .= $this->db->plimit($limit, 0);
3164
3165
		// Build output string
3166
3167
		dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3168
		$result = $this->db->query($sql);
3169
		if ($result) {
3170
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3171
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3172
3173
			$num = $this->db->num_rows($result);
3174
3175
			//$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';	// remove select to have id same with combo and ajax
3176
			$out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3177
			if (!$selected) {
3178
				$out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3179
			} else {
3180
				$out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3181
			}
3182
3183
			$i = 0;
3184
			while ($i < $num) {
3185
				$objp = $this->db->fetch_object($result);
3186
3187
				$outkey = $objp->idprodfournprice; // id in table of price
3188
				if (!$outkey && $alsoproductwithnosupplierprice) {
3189
					$outkey = 'idprod_'.$objp->rowid; // id of product
3190
				}
3191
3192
				$outref = $objp->ref;
3193
				$outval = '';
3194
				$outbarcode = $objp->barcode;
3195
				$outqty = 1;
3196
				$outdiscount = 0;
3197
				$outtype = $objp->fk_product_type;
3198
				$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3199
				$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3200
3201
				// Units
3202
				$outvalUnits = '';
3203
				if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3204
					if (!empty($objp->unit_short)) {
3205
						$outvalUnits .= ' - '.$objp->unit_short;
3206
					}
3207
					if (!empty($objp->weight) && $objp->weight_units !== null) {
3208
						$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3209
						$outvalUnits .= ' - '.$unitToShow;
3210
					}
3211
					if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3212
						$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3213
						$outvalUnits .= ' - '.$unitToShow;
3214
					}
3215
					if (!empty($objp->surface) && $objp->surface_units !== null) {
3216
						$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3217
						$outvalUnits .= ' - '.$unitToShow;
3218
					}
3219
					if (!empty($objp->volume) && $objp->volume_units !== null) {
3220
						$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3221
						$outvalUnits .= ' - '.$unitToShow;
3222
					}
3223
					if ($outdurationvalue && $outdurationunit) {
3224
						$da = array(
3225
							'h' => $langs->trans('Hour'),
3226
							'd' => $langs->trans('Day'),
3227
							'w' => $langs->trans('Week'),
3228
							'm' => $langs->trans('Month'),
3229
							'y' => $langs->trans('Year')
3230
						);
3231
						if (isset($da[$outdurationunit])) {
3232
							$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3233
						}
3234
					}
3235
				}
3236
3237
				$objRef = $objp->ref;
3238
				if ($filterkey && $filterkey != '') {
3239
					$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3240
				}
3241
				$objRefFourn = $objp->ref_fourn;
3242
				if ($filterkey && $filterkey != '') {
3243
					$objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3244
				}
3245
				$label = $objp->label;
3246
				if ($filterkey && $filterkey != '') {
3247
					$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3248
				}
3249
3250
				$optlabel = $objp->ref;
3251
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3252
					$optlabel .= ' <span class=\'opacitymedium\'>('.$objp->ref_fourn.')</span>';
3253
				}
3254
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3255
					$optlabel .= ' ('.$outbarcode.')';
3256
				}
3257
				$optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3258
3259
				$outvallabel = $objRef;
3260
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3261
					$outvallabel .= ' ('.$objRefFourn.')';
3262
				}
3263
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3264
					$outvallabel .= ' ('.$outbarcode.')';
3265
				}
3266
				$outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3267
3268
				// Units
3269
				$optlabel .= $outvalUnits;
3270
				$outvallabel .= $outvalUnits;
3271
3272
				if (!empty($objp->idprodfournprice)) {
3273
					$outqty = $objp->quantity;
3274
					$outdiscount = $objp->remise_percent;
3275
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3276
						$prod_supplier = new ProductFournisseur($this->db);
3277
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3278
						$prod_supplier->id = $objp->fk_product;
3279
						$prod_supplier->fourn_qty = $objp->quantity;
3280
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3281
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3282
						$priceparser = new PriceParser($this->db);
3283
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3284
						if ($price_result >= 0) {
3285
							$objp->fprice = $price_result;
3286
							if ($objp->quantity >= 1) {
3287
								$objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3288
							}
3289
						}
3290
					}
3291
					if ($objp->quantity == 1) {
3292
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3293
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3294
						$optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3295
						$outvallabel .= $langs->transnoentities("Unit");
3296
					} else {
3297
						$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;
3298
						$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;
3299
						$optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3300
						$outvallabel .= ' '.$langs->transnoentities("Units");
3301
					}
3302
3303
					if ($objp->quantity > 1) {
3304
						$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
3305
						$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
3306
					}
3307
					if ($objp->remise_percent >= 1) {
3308
						$optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3309
						$outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3310
					}
3311
					if ($objp->duration) {
3312
						$optlabel .= " - ".$objp->duration;
3313
						$outvallabel .= " - ".$objp->duration;
3314
					}
3315
					if (!$socid) {
3316
						$optlabel .= " - ".dol_trunc($objp->name, 8);
3317
						$outvallabel .= " - ".dol_trunc($objp->name, 8);
3318
					}
3319
					if ($objp->supplier_reputation) {
3320
						//TODO dictionary
3321
						$reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3322
3323
						$optlabel .= " - ".$reputations[$objp->supplier_reputation];
3324
						$outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3325
					}
3326
				} else {
3327
					if (empty($alsoproductwithnosupplierprice)) {     // No supplier price defined for couple product/supplier
3328
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3329
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3330
					} else // No supplier price defined for product, even on other suppliers
3331
					{
3332
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3333
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3334
					}
3335
				}
3336
3337
				if (!empty($conf->stock->enabled) && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3338
					$novirtualstock = ($showstockinlist == 2);
3339
3340
					if (!empty($user->rights->stock->lire)) {
3341
						$outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3342
3343
						if ($objp->stock > 0) {
3344
							$optlabel .= ' - <span class="product_line_stock_ok">';
3345
						} elseif ($objp->stock <= 0) {
3346
							$optlabel .= ' - <span class="product_line_stock_too_low">';
3347
						}
3348
						$optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3349
						$optlabel .= '</span>';
3350
						if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
3351
							$langs->load("stocks");
3352
3353
							$tmpproduct = new Product($this->db);
3354
							$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3355
							$tmpproduct->load_virtual_stock();
3356
							$virtualstock = $tmpproduct->stock_theorique;
3357
3358
							$outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3359
3360
							$optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3361
							if ($virtualstock > 0) {
3362
								$optlabel .= '<span class="product_line_stock_ok">';
3363
							} elseif ($virtualstock <= 0) {
3364
								$optlabel .= '<span class="product_line_stock_too_low">';
3365
							}
3366
							$optlabel .= $virtualstock;
3367
							$optlabel .= '</span>';
3368
3369
							unset($tmpproduct);
3370
						}
3371
					}
3372
				}
3373
3374
				$opt = '<option value="'.$outkey.'"';
3375
				if ($selected && $selected == $objp->idprodfournprice) {
3376
					$opt .= ' selected';
3377
				}
3378
				if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3379
					$opt .= ' disabled';
3380
				}
3381
				if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3382
					$opt .= ' data-qty="'.$objp->quantity.'" data-up="'.$objp->unitprice.'" data-discount="'.$outdiscount.'"';
3383
				}
3384
				$opt .= ' data-description="'.dol_escape_htmltag($objp->description).'"';
3385
				$opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"';
3386
				$opt .= '>';
3387
3388
				$opt .= $optlabel;
3389
				$outval .= $outvallabel;
3390
3391
				$opt .= "</option>\n";
3392
3393
3394
				// Add new entry
3395
				// "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
3396
				// "label" value of json key array is used by jQuery automatically as text for combo box
3397
				$out .= $opt;
3398
				array_push(
3399
					$outarray,
3400
					array('key'=>$outkey,
3401
						'value'=>$outref,
3402
						'label'=>$outval,
3403
						'qty'=>$outqty,
3404
						'price_ht'=>price2num($objp->unitprice, 'MT'),
3405
						'discount'=>$outdiscount,
3406
						'type'=>$outtype,
3407
						'duration_value'=>$outdurationvalue,
3408
						'duration_unit'=>$outdurationunit,
3409
						'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3410
						'description'=>$objp->description
3411
					)
3412
				);
3413
				// Exemple of var_dump $outarray
3414
				// array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3415
				//           ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3416
				//      	 ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3417
				//}
3418
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3419
				//$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3420
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3421
3422
				$i++;
3423
			}
3424
			$out .= '</select>';
3425
3426
			$this->db->free($result);
3427
3428
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3429
			$out .= ajax_combobox($htmlname);
3430
3431
			if (empty($outputmode)) {
3432
				return $out;
3433
			}
3434
			return $outarray;
3435
		} else {
3436
			dol_print_error($this->db);
3437
		}
3438
	}
3439
3440
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3441
	/**
3442
	 *	Return list of suppliers prices for a product
3443
	 *
3444
	 *  @param	    int		$productid       	Id of product
3445
	 *  @param      string	$htmlname        	Name of HTML field
3446
	 *  @param      int		$selected_supplier  Pre-selected supplier if more than 1 result
3447
	 *  @return	    string
3448
	 */
3449
	public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3450
	{
3451
		// phpcs:enable
3452
		global $langs, $conf;
3453
3454
		$langs->load('stocks');
3455
3456
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3457
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3458
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3459
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3460
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3461
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3462
		$sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3463
		$sql .= " AND p.tobuy = 1";
3464
		$sql .= " AND s.fournisseur = 1";
3465
		$sql .= " AND p.rowid = ".$productid;
3466
		$sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3467
3468
		dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3469
		$result = $this->db->query($sql);
3470
3471
		if ($result) {
3472
			$num = $this->db->num_rows($result);
3473
3474
			$form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3475
3476
			if (!$num) {
3477
				$form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3478
			} else {
3479
				require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3480
				$form .= '<option value="0">&nbsp;</option>';
3481
3482
				$i = 0;
3483
				while ($i < $num) {
3484
					$objp = $this->db->fetch_object($result);
3485
3486
					$opt = '<option value="'.$objp->idprodfournprice.'"';
3487
					//if there is only one supplier, preselect it
3488
					if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier)) {
3489
						$opt .= ' selected';
3490
					}
3491
					$opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3492
3493
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3494
						$prod_supplier = new ProductFournisseur($this->db);
3495
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3496
						$prod_supplier->id = $productid;
3497
						$prod_supplier->fourn_qty = $objp->quantity;
3498
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3499
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3500
						$priceparser = new PriceParser($this->db);
3501
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3502
						if ($price_result >= 0) {
3503
							$objp->fprice = $price_result;
3504
							if ($objp->quantity >= 1) {
3505
								$objp->unitprice = $objp->fprice / $objp->quantity;
3506
							}
3507
						}
3508
					}
3509
					if ($objp->quantity == 1) {
3510
						$opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3511
					}
3512
3513
					$opt .= $objp->quantity.' ';
3514
3515
					if ($objp->quantity == 1) {
3516
						$opt .= $langs->trans("Unit");
3517
					} else {
3518
						$opt .= $langs->trans("Units");
3519
					}
3520
					if ($objp->quantity > 1) {
3521
						$opt .= " - ";
3522
						$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");
3523
					}
3524
					if ($objp->duration) {
3525
						$opt .= " - ".$objp->duration;
3526
					}
3527
					$opt .= "</option>\n";
3528
3529
					$form .= $opt;
3530
					$i++;
3531
				}
3532
			}
3533
3534
			$form .= '</select>';
3535
			$this->db->free($result);
3536
			return $form;
3537
		} else {
3538
			dol_print_error($this->db);
3539
		}
3540
	}
3541
3542
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3543
	/**
3544
	 *    Return list of delivery address
3545
	 *
3546
	 *    @param    string	$selected          	Id contact pre-selectionn
3547
	 *    @param    int		$socid				Id of company
3548
	 *    @param    string	$htmlname          	Name of HTML field
3549
	 *    @param    int		$showempty         	Add an empty field
3550
	 *    @return	integer|null
3551
	 */
3552
	public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3553
	{
3554
		// phpcs:enable
3555
		// looking for users
3556
		$sql = "SELECT a.rowid, a.label";
3557
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a";
3558
		$sql .= " WHERE a.fk_soc = ".((int) $socid);
3559
		$sql .= " ORDER BY a.label ASC";
3560
3561
		dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3562
		$resql = $this->db->query($sql);
3563
		if ($resql) {
3564
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3565
			if ($showempty) {
3566
				print '<option value="0">&nbsp;</option>';
3567
			}
3568
			$num = $this->db->num_rows($resql);
3569
			$i = 0;
3570
			if ($num) {
3571
				while ($i < $num) {
3572
					$obj = $this->db->fetch_object($resql);
3573
3574
					if ($selected && $selected == $obj->rowid) {
3575
						print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3576
					} else {
3577
						print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3578
					}
3579
					$i++;
3580
				}
3581
			}
3582
			print '</select>';
3583
			return $num;
3584
		} else {
3585
			dol_print_error($this->db);
3586
		}
3587
	}
3588
3589
3590
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3591
	/**
3592
	 *      Load into cache list of payment terms
3593
	 *
3594
	 *      @return     int             Nb of lines loaded, <0 if KO
3595
	 */
3596
	public function load_cache_conditions_paiements()
3597
	{
3598
		// phpcs:enable
3599
		global $langs;
3600
3601
		$num = count($this->cache_conditions_paiements);
3602
		if ($num > 0) {
3603
			return 0; // Cache already loaded
3604
		}
3605
3606
		dol_syslog(__METHOD__, LOG_DEBUG);
3607
3608
		$sql = "SELECT rowid, code, libelle as label";
3609
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term';
3610
		$sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3611
		$sql .= " AND active > 0";
3612
		$sql .= " ORDER BY sortorder";
3613
3614
		$resql = $this->db->query($sql);
3615
		if ($resql) {
3616
			$num = $this->db->num_rows($resql);
3617
			$i = 0;
3618
			while ($i < $num) {
3619
				$obj = $this->db->fetch_object($resql);
3620
3621
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3622
				$label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3623
				$this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3624
				$this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3625
				$i++;
3626
			}
3627
3628
			//$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1);		// We use the field sortorder of table
3629
3630
			return $num;
3631
		} else {
3632
			dol_print_error($this->db);
3633
			return -1;
3634
		}
3635
	}
3636
3637
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3638
	/**
3639
	 *      Charge dans cache la liste des délais de livraison possibles
3640
	 *
3641
	 *      @return     int             Nb of lines loaded, <0 if KO
3642
	 */
3643
	public function load_cache_availability()
3644
	{
3645
		// phpcs:enable
3646
		global $langs;
3647
3648
		$num = count($this->cache_availability);
3649
		if ($num > 0) {
3650
			return 0; // Cache already loaded
3651
		}
3652
3653
		dol_syslog(__METHOD__, LOG_DEBUG);
3654
3655
		$langs->load('propal');
3656
3657
		$sql = "SELECT rowid, code, label, position";
3658
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_availability';
3659
		$sql .= " WHERE active > 0";
3660
3661
		$resql = $this->db->query($sql);
3662
		if ($resql) {
3663
			$num = $this->db->num_rows($resql);
3664
			$i = 0;
3665
			while ($i < $num) {
3666
				$obj = $this->db->fetch_object($resql);
3667
3668
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3669
				$label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3670
				$this->cache_availability[$obj->rowid]['code'] = $obj->code;
3671
				$this->cache_availability[$obj->rowid]['label'] = $label;
3672
				$this->cache_availability[$obj->rowid]['position'] = $obj->position;
3673
				$i++;
3674
			}
3675
3676
			$this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3677
3678
			return $num;
3679
		} else {
3680
			dol_print_error($this->db);
3681
			return -1;
3682
		}
3683
	}
3684
3685
	/**
3686
	 *      Retourne la liste des types de delais de livraison possibles
3687
	 *
3688
	 *      @param	int		$selected       Id du type de delais pre-selectionne
3689
	 *      @param  string	$htmlname       Nom de la zone select
3690
	 *      @param  string	$filtertype     To add a filter
3691
	 *		@param	int		$addempty		Add empty entry
3692
	 * 		@param	string	$morecss		More CSS
3693
	 *		@return	void
3694
	 */
3695
	public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3696
	{
3697
		global $langs, $user;
3698
3699
		$this->load_cache_availability();
3700
3701
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3702
3703
		print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3704
		if ($addempty) {
3705
			print '<option value="0">&nbsp;</option>';
3706
		}
3707
		foreach ($this->cache_availability as $id => $arrayavailability) {
3708
			if ($selected == $id) {
3709
				print '<option value="'.$id.'" selected>';
3710
			} else {
3711
				print '<option value="'.$id.'">';
3712
			}
3713
			print dol_escape_htmltag($arrayavailability['label']);
3714
			print '</option>';
3715
		}
3716
		print '</select>';
3717
		if ($user->admin) {
3718
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3719
		}
3720
		print ajax_combobox($htmlname);
3721
	}
3722
3723
	/**
3724
	 *      Load into cache cache_demand_reason, array of input reasons
3725
	 *
3726
	 *      @return     int             Nb of lines loaded, <0 if KO
3727
	 */
3728
	public function loadCacheInputReason()
3729
	{
3730
		global $langs;
3731
3732
		$num = count($this->cache_demand_reason);
3733
		if ($num > 0) {
3734
			return 0; // Cache already loaded
3735
		}
3736
3737
		$sql = "SELECT rowid, code, label";
3738
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason';
3739
		$sql .= " WHERE active > 0";
3740
3741
		$resql = $this->db->query($sql);
3742
		if ($resql) {
3743
			$num = $this->db->num_rows($resql);
3744
			$i = 0;
3745
			$tmparray = array();
3746
			while ($i < $num) {
3747
				$obj = $this->db->fetch_object($resql);
3748
3749
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3750
				$label = ($obj->label != '-' ? $obj->label : '');
3751
				if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
3752
					$label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3753
				}
3754
				if ($langs->trans($obj->code) != $obj->code) {
3755
					$label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3756
				}
3757
3758
				$tmparray[$obj->rowid]['id']   = $obj->rowid;
3759
				$tmparray[$obj->rowid]['code'] = $obj->code;
3760
				$tmparray[$obj->rowid]['label'] = $label;
3761
				$i++;
3762
			}
3763
3764
			$this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
3765
3766
			unset($tmparray);
3767
			return $num;
3768
		} else {
3769
			dol_print_error($this->db);
3770
			return -1;
3771
		}
3772
	}
3773
3774
	/**
3775
	 *	Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
3776
	 *  List found into table c_input_reason loaded by loadCacheInputReason
3777
	 *
3778
	 *  @param	int		$selected        Id or code of type origin to select by default
3779
	 *  @param  string	$htmlname        Nom de la zone select
3780
	 *  @param  string	$exclude         To exclude a code value (Example: SRC_PROP)
3781
	 *	@param	int		$addempty		 Add an empty entry
3782
	 *  @param  string	$morecss		 Add more css to the HTML select component
3783
	 *  @param	int		$notooltip		 Do not show the tooltip for admin
3784
	 *	@return	void
3785
	 */
3786
	public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
3787
	{
3788
		global $langs, $user;
3789
3790
		$this->loadCacheInputReason();
3791
3792
		print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3793
		if ($addempty) {
3794
			print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
3795
		}
3796
		foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
3797
			if ($arraydemandreason['code'] == $exclude) {
3798
				continue;
3799
			}
3800
3801
			if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
3802
				print '<option value="'.$arraydemandreason['id'].'" selected>';
3803
			} else {
3804
				print '<option value="'.$arraydemandreason['id'].'">';
3805
			}
3806
			$label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
3807
			print $langs->trans($label);
3808
			print '</option>';
3809
		}
3810
		print '</select>';
3811
		if ($user->admin && empty($notooltip)) {
3812
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3813
		}
3814
		print ajax_combobox('select_'.$htmlname);
3815
	}
3816
3817
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3818
	/**
3819
	 *      Charge dans cache la liste des types de paiements possibles
3820
	 *
3821
	 *      @return     int                 Nb of lines loaded, <0 if KO
3822
	 */
3823
	public function load_cache_types_paiements()
3824
	{
3825
		// phpcs:enable
3826
		global $langs;
3827
3828
		$num = count($this->cache_types_paiements);
3829
		if ($num > 0) {
3830
			return $num; // Cache already loaded
3831
		}
3832
3833
		dol_syslog(__METHOD__, LOG_DEBUG);
3834
3835
		$this->cache_types_paiements = array();
3836
3837
		$sql = "SELECT id, code, libelle as label, type, active";
3838
		$sql .= " FROM ".MAIN_DB_PREFIX."c_paiement";
3839
		$sql .= " WHERE entity IN (".getEntity('c_paiement').")";
3840
3841
		$resql = $this->db->query($sql);
3842
		if ($resql) {
3843
			$num = $this->db->num_rows($resql);
3844
			$i = 0;
3845
			while ($i < $num) {
3846
				$obj = $this->db->fetch_object($resql);
3847
3848
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3849
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3850
				$this->cache_types_paiements[$obj->id]['id'] = $obj->id;
3851
				$this->cache_types_paiements[$obj->id]['code'] = $obj->code;
3852
				$this->cache_types_paiements[$obj->id]['label'] = $label;
3853
				$this->cache_types_paiements[$obj->id]['type'] = $obj->type;
3854
				$this->cache_types_paiements[$obj->id]['active'] = $obj->active;
3855
				$i++;
3856
			}
3857
3858
			$this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
3859
3860
			return $num;
3861
		} else {
3862
			dol_print_error($this->db);
3863
			return -1;
3864
		}
3865
	}
3866
3867
3868
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3869
	/**
3870
	 *      Return list of payment modes.
3871
	 *      Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want.
3872
	 *      See instead to force the default value by the caller.
3873
	 *
3874
	 *      @param	int		$selected		Id of payment term to preselect by default
3875
	 *      @param	string	$htmlname		Nom de la zone select
3876
	 *      @param	int		$filtertype		Not used
3877
	 *		@param	int		$addempty		Add an empty entry
3878
	 * 		@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
3879
	 * 		@param	string	$morecss			Add more CSS on select tag
3880
	 *		@return	void
3881
	 */
3882
	public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '')
3883
	{
3884
		// phpcs:enable
3885
		global $langs, $user, $conf;
3886
3887
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3888
3889
		$this->load_cache_conditions_paiements();
3890
3891
		// Set default value if not already set by caller
3892
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
3893
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
3894
		}
3895
3896
		print '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3897
		if ($addempty) {
3898
			print '<option value="0">&nbsp;</option>';
3899
		}
3900
		foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
3901
			if ($selected == $id) {
3902
				print '<option value="'.$id.'" selected>';
3903
			} else {
3904
				print '<option value="'.$id.'">';
3905
			}
3906
			print $arrayconditions['label'];
3907
			print '</option>';
3908
		}
3909
		print '</select>';
3910
		if ($user->admin && empty($noinfoadmin)) {
3911
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3912
		}
3913
		print ajax_combobox($htmlname);
3914
	}
3915
3916
3917
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3918
	/**
3919
	 *      Return list of payment methods
3920
	 *      Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want.
3921
	 *
3922
	 *      @param	string	$selected       Id or code or preselected payment mode
3923
	 *      @param  string	$htmlname       Name of select field
3924
	 *      @param  string	$filtertype     To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
3925
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
3926
	 *      @param  int		$empty			1=can be empty, 0 otherwise
3927
	 * 		@param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
3928
	 *      @param  int		$maxlength      Max length of label
3929
	 *      @param  int     $active         Active or not, -1 = all
3930
	 *      @param  string  $morecss        Add more CSS on select tag
3931
	 * 		@return	void
3932
	 */
3933
	public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
3934
	{
3935
		// phpcs:enable
3936
		global $langs, $user, $conf;
3937
3938
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
3939
3940
		$filterarray = array();
3941
		if ($filtertype == 'CRDT') {
3942
			$filterarray = array(0, 2, 3);
3943
		} elseif ($filtertype == 'DBIT') {
3944
			$filterarray = array(1, 2, 3);
3945
		} elseif ($filtertype != '' && $filtertype != '-1') {
3946
			$filterarray = explode(',', $filtertype);
3947
		}
3948
3949
		$this->load_cache_types_paiements();
3950
3951
		// Set default value if not already set by caller
3952
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
3953
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
3954
		}
3955
3956
		print '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3957
		if ($empty) {
3958
			print '<option value="">&nbsp;</option>';
3959
		}
3960
		foreach ($this->cache_types_paiements as $id => $arraytypes) {
3961
			// If not good status
3962
			if ($active >= 0 && $arraytypes['active'] != $active) {
3963
				continue;
3964
			}
3965
3966
			// On passe si on a demande de filtrer sur des modes de paiments particuliers
3967
			if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
3968
				continue;
3969
			}
3970
3971
			// We discard empty line if showempty is on because an empty line has already been output.
3972
			if ($empty && empty($arraytypes['code'])) {
3973
				continue;
3974
			}
3975
3976
			if ($format == 0) {
3977
				print '<option value="'.$id.'"';
3978
			} elseif ($format == 1) {
3979
				print '<option value="'.$arraytypes['code'].'"';
3980
			} elseif ($format == 2) {
3981
				print '<option value="'.$arraytypes['code'].'"';
3982
			} elseif ($format == 3) {
3983
				print '<option value="'.$id.'"';
3984
			}
3985
			// Print attribute selected or not
3986
			if ($format == 1 || $format == 2) {
3987
				if ($selected == $arraytypes['code']) {
3988
					print ' selected';
3989
				}
3990
			} else {
3991
				if ($selected == $id) {
3992
					print ' selected';
3993
				}
3994
			}
3995
			print '>';
3996
			if ($format == 0) {
3997
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
3998
			} elseif ($format == 1) {
3999
				$value = $arraytypes['code'];
4000
			} elseif ($format == 2) {
4001
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4002
			} elseif ($format == 3) {
4003
				$value = $arraytypes['code'];
4004
			}
4005
			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...
4006
			print '</option>';
4007
		}
4008
		print '</select>';
4009
		if ($user->admin && !$noadmininfo) {
4010
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4011
		}
4012
		print ajax_combobox('select'.$htmlname);
4013
	}
4014
4015
4016
	/**
4017
	 *  Selection HT or TTC
4018
	 *
4019
	 *  @param	string	$selected       Id pre-selectionne
4020
	 *  @param  string	$htmlname       Nom de la zone select
4021
	 *  @param	string	$addjscombo		Add js combo
4022
	 * 	@return	string					Code of HTML select to chose tax or not
4023
	 */
4024
	public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4025
	{
4026
		global $langs;
4027
4028
		$return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4029
		$options = array(
4030
			'HT'=>$langs->trans("HT"),
4031
			'TTC'=>$langs->trans("TTC")
4032
		);
4033
		foreach ($options as $id => $value) {
4034
			if ($selected == $id) {
4035
				$return .= '<option value="'.$id.'" selected>'.$value;
4036
			} else {
4037
				$return .= '<option value="'.$id.'">'.$value;
4038
			}
4039
			$return .= '</option>';
4040
		}
4041
		$return .= '</select>';
4042
		if ($addjscombo) {
4043
			$return .= ajax_combobox('select_'.$htmlname);
4044
		}
4045
4046
		return $return;
4047
	}
4048
4049
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4050
	/**
4051
	 *      Load in cache list of transport mode
4052
	 *
4053
	 *      @return     int                 Nb of lines loaded, <0 if KO
4054
	 */
4055
	public function load_cache_transport_mode()
4056
	{
4057
		// phpcs:enable
4058
		global $langs;
4059
4060
		$num = count($this->cache_transport_mode);
4061
		if ($num > 0) {
4062
			return $num; // Cache already loaded
4063
		}
4064
4065
		dol_syslog(__METHOD__, LOG_DEBUG);
4066
4067
		$this->cache_transport_mode = array();
4068
4069
		$sql = "SELECT rowid, code, label, active";
4070
		$sql .= " FROM ".MAIN_DB_PREFIX."c_transport_mode";
4071
		$sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4072
4073
		$resql = $this->db->query($sql);
4074
		if ($resql) {
4075
			$num = $this->db->num_rows($resql);
4076
			$i = 0;
4077
			while ($i < $num) {
4078
				$obj = $this->db->fetch_object($resql);
4079
4080
				// If traduction exist, we use it else we take the default label
4081
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4082
				$this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4083
				$this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4084
				$this->cache_transport_mode[$obj->rowid]['label'] = $label;
4085
				$this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4086
				$i++;
4087
			}
4088
4089
			$this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4090
4091
			return $num;
4092
		} else {
4093
			dol_print_error($this->db);
4094
			return -1;
4095
		}
4096
	}
4097
4098
	/**
4099
	 *      Return list of transport mode for intracomm report
4100
	 *
4101
	 *      @param	string	$selected       Id of the transport mode pre-selected
4102
	 *      @param  string	$htmlname       Name of the select field
4103
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
4104
	 *      @param  int		$empty			1=can be empty, 0 else
4105
	 *      @param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
4106
	 *      @param  int		$maxlength      Max length of label
4107
	 *      @param  int     $active         Active or not, -1 = all
4108
	 *      @param  string  $morecss        Add more CSS on select tag
4109
	 * 		@return	void
4110
	 */
4111
	public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4112
	{
4113
		global $langs, $user;
4114
4115
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4116
4117
		$this->load_cache_transport_mode();
4118
4119
		print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4120
		if ($empty) {
4121
			print '<option value="">&nbsp;</option>';
4122
		}
4123
		foreach ($this->cache_transport_mode as $id => $arraytypes) {
4124
			// If not good status
4125
			if ($active >= 0 && $arraytypes['active'] != $active) {
4126
				continue;
4127
			}
4128
4129
			// We discard empty line if showempty is on because an empty line has already been output.
4130
			if ($empty && empty($arraytypes['code'])) {
4131
				continue;
4132
			}
4133
4134
			if ($format == 0) {
4135
				print '<option value="'.$id.'"';
4136
			} elseif ($format == 1) {
4137
				print '<option value="'.$arraytypes['code'].'"';
4138
			} elseif ($format == 2) {
4139
				print '<option value="'.$arraytypes['code'].'"';
4140
			} elseif ($format == 3) {
4141
				print '<option value="'.$id.'"';
4142
			}
4143
			// If text is selected, we compare with code, else with id
4144
			if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4145
				print ' selected';
4146
			} elseif ($selected == $id) {
4147
				print ' selected';
4148
			}
4149
			print '>';
4150
			if ($format == 0) {
4151
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4152
			} elseif ($format == 1) {
4153
				$value = $arraytypes['code'];
4154
			} elseif ($format == 2) {
4155
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4156
			} elseif ($format == 3) {
4157
				$value = $arraytypes['code'];
4158
			}
4159
			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...
4160
			print '</option>';
4161
		}
4162
		print '</select>';
4163
		if ($user->admin && !$noadmininfo) {
4164
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4165
		}
4166
	}
4167
4168
	/**
4169
	 *  Return a HTML select list of shipping mode
4170
	 *
4171
	 *  @param	string	$selected           Id shipping mode pre-selected
4172
	 *  @param  string	$htmlname           Name of select zone
4173
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4174
	 *  @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.
4175
	 *  @param  string	$moreattrib         To add more attribute on select
4176
	 *	@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
4177
	 *  @param	string	$morecss			More CSS
4178
	 * 	@return	void
4179
	 */
4180
	public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4181
	{
4182
		global $langs, $conf, $user;
4183
4184
		$langs->load("admin");
4185
		$langs->load("deliveries");
4186
4187
		$sql = "SELECT rowid, code, libelle as label";
4188
		$sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode";
4189
		$sql .= " WHERE active > 0";
4190
		if ($filtre) {
4191
			$sql .= " AND ".$filtre;
4192
		}
4193
		$sql .= " ORDER BY libelle ASC";
4194
4195
		dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4196
		$result = $this->db->query($sql);
4197
		if ($result) {
4198
			$num = $this->db->num_rows($result);
4199
			$i = 0;
4200
			if ($num) {
4201
				print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4202
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4203
					print '<option value="-1">&nbsp;</option>';
4204
				}
4205
				while ($i < $num) {
4206
					$obj = $this->db->fetch_object($result);
4207
					if ($selected == $obj->rowid) {
4208
						print '<option value="'.$obj->rowid.'" selected>';
4209
					} else {
4210
						print '<option value="'.$obj->rowid.'">';
4211
					}
4212
					print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4213
					print '</option>';
4214
					$i++;
4215
				}
4216
				print "</select>";
4217
				if ($user->admin  && empty($noinfoadmin)) {
4218
					print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4219
				}
4220
4221
				print ajax_combobox('select'.$htmlname);
4222
			} else {
4223
				print $langs->trans("NoShippingMethodDefined");
4224
			}
4225
		} else {
4226
			dol_print_error($this->db);
4227
		}
4228
	}
4229
4230
	/**
4231
	 *    Display form to select shipping mode
4232
	 *
4233
	 *    @param	string	$page        Page
4234
	 *    @param    int		$selected    Id of shipping mode
4235
	 *    @param    string	$htmlname    Name of select html field
4236
	 *    @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.
4237
	 *    @return	void
4238
	 */
4239
	public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4240
	{
4241
		global $langs, $db;
4242
4243
		$langs->load("deliveries");
4244
4245
		if ($htmlname != "none") {
4246
			print '<form method="POST" action="'.$page.'">';
4247
			print '<input type="hidden" name="action" value="setshippingmethod">';
4248
			print '<input type="hidden" name="token" value="'.newToken().'">';
4249
			$this->selectShippingMethod($selected, $htmlname, '', $addempty);
4250
			print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4251
			print '</form>';
4252
		} else {
4253
			if ($selected) {
4254
				$code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code');
4255
				print $langs->trans("SendingMethod".strtoupper($code));
4256
			} else {
4257
				print "&nbsp;";
4258
			}
4259
		}
4260
	}
4261
4262
	/**
4263
	 * Creates HTML last in cycle situation invoices selector
4264
	 *
4265
	 * @param     string  $selected   		Preselected ID
4266
	 * @param     int     $socid      		Company ID
4267
	 *
4268
	 * @return    string                     HTML select
4269
	 */
4270
	public function selectSituationInvoices($selected = '', $socid = 0)
4271
	{
4272
		global $langs;
4273
4274
		$langs->load('bills');
4275
4276
		$opt = '<option value ="" selected></option>';
4277
		$sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc';
4278
		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture';
4279
		$sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4280
		$sql .= ' AND situation_counter >= 1';
4281
		$sql .= ' AND fk_soc = '.(int) $socid;
4282
		$sql .= ' AND type <> 2';
4283
		$sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4284
		$resql = $this->db->query($sql);
4285
4286
		if ($resql && $this->db->num_rows($resql) > 0) {
4287
			// Last seen cycle
4288
			$ref = 0;
4289
			while ($obj = $this->db->fetch_object($resql)) {
4290
				//Same cycle ?
4291
				if ($obj->situation_cycle_ref != $ref) {
4292
					// Just seen this cycle
4293
					$ref = $obj->situation_cycle_ref;
4294
					//not final ?
4295
					if ($obj->situation_final != 1) {
4296
						//Not prov?
4297
						if (substr($obj->ref, 1, 4) != 'PROV') {
4298
							if ($selected == $obj->rowid) {
4299
								$opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4300
							} else {
4301
								$opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4302
							}
4303
						}
4304
					}
4305
				}
4306
			}
4307
		} else {
4308
				dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4309
		}
4310
		if ($opt == '<option value ="" selected></option>') {
4311
			$opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4312
		}
4313
		return $opt;
4314
	}
4315
4316
	/**
4317
	 *      Creates HTML units selector (code => label)
4318
	 *
4319
	 *      @param	string	$selected       Preselected Unit ID
4320
	 *      @param  string	$htmlname       Select name
4321
	 *      @param	int		$showempty		Add a nempty line
4322
	 *      @param  string  $unit_type      Restrict to one given unit type
4323
	 * 		@return	string                  HTML select
4324
	 */
4325
	public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4326
	{
4327
		global $langs;
4328
4329
		$langs->load('products');
4330
4331
		$return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4332
4333
		$sql = 'SELECT rowid, label, code from '.MAIN_DB_PREFIX.'c_units';
4334
		$sql .= ' WHERE active > 0';
4335
		if (!empty($unit_type)) {
4336
			$sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4337
		}
4338
4339
		$resql = $this->db->query($sql);
4340
		if ($resql && $this->db->num_rows($resql) > 0) {
4341
			if ($showempty) {
4342
				$return .= '<option value="none"></option>';
4343
			}
4344
4345
			while ($res = $this->db->fetch_object($resql)) {
4346
				$unitLabel = $res->label;
4347
				if (!empty($langs->tab_translate['unit'.$res->code])) {	// check if Translation is available before
4348
					$unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4349
				}
4350
4351
				if ($selected == $res->rowid) {
4352
					$return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4353
				} else {
4354
					$return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4355
				}
4356
			}
4357
			$return .= '</select>';
4358
		}
4359
		return $return;
4360
	}
4361
4362
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4363
	/**
4364
	 *  Return a HTML select list of bank accounts
4365
	 *
4366
	 *  @param	string	$selected           Id account pre-selected
4367
	 *  @param  string	$htmlname           Name of select zone
4368
	 *  @param  int		$status             Status of searched accounts (0=open, 1=closed, 2=both)
4369
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4370
	 *  @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.
4371
	 *  @param  string	$moreattrib         To add more attribute on select
4372
	 *  @param	int		$showcurrency		Show currency in label
4373
	 *  @param	string	$morecss			More CSS
4374
	 *  @param	int		$nooutput			1=Return string, do not send to output
4375
	 * 	@return	int							<0 if error, Num of bank account found if OK (0, 1, 2, ...)
4376
	 */
4377
	public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4378
	{
4379
		// phpcs:enable
4380
		global $langs, $conf;
4381
4382
		$out = '';
4383
4384
		$langs->load("admin");
4385
		$num = 0;
4386
4387
		$sql = "SELECT rowid, label, bank, clos as status, currency_code";
4388
		$sql .= " FROM ".MAIN_DB_PREFIX."bank_account";
4389
		$sql .= " WHERE entity IN (".getEntity('bank_account').")";
4390
		if ($status != 2) {
4391
			$sql .= " AND clos = ".(int) $status;
4392
		}
4393
		if ($filtre) {
4394
			$sql .= " AND ".$filtre;
4395
		}
4396
		$sql .= " ORDER BY label";
4397
4398
		dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4399
		$result = $this->db->query($sql);
4400
		if ($result) {
4401
			$num = $this->db->num_rows($result);
4402
			$i = 0;
4403
			if ($num) {
4404
				$out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4405
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4406
					$out .= '<option value="-1">&nbsp;</option>';
4407
				}
4408
4409
				while ($i < $num) {
4410
					$obj = $this->db->fetch_object($result);
4411
					if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4412
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4413
					} else {
4414
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4415
					}
4416
					$out .= trim($obj->label);
4417
					if ($showcurrency) {
4418
						$out .= ' ('.$obj->currency_code.')';
4419
					}
4420
					if ($status == 2 && $obj->status == 1) {
4421
						$out .= ' ('.$langs->trans("Closed").')';
4422
					}
4423
					$out .= '</option>';
4424
					$i++;
4425
				}
4426
				$out .= "</select>";
4427
				$out .= ajax_combobox('select'.$htmlname);
4428
			} else {
4429
				if ($status == 0) {
4430
					$out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4431
				} else {
4432
					$out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4433
				}
4434
			}
4435
		} else {
4436
			dol_print_error($this->db);
4437
		}
4438
4439
		// Output or return
4440
		if (empty($nooutput)) {
4441
			print $out;
4442
		} else {
4443
			return $out;
4444
		}
4445
4446
		return $num;
4447
	}
4448
4449
	/**
4450
	 *  Return a HTML select list of establishment
4451
	 *
4452
	 *  @param	string	$selected           Id establishment pre-selected
4453
	 *  @param  string	$htmlname           Name of select zone
4454
	 *  @param  int		$status             Status of searched establishment (0=open, 1=closed, 2=both)
4455
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4456
	 *  @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.
4457
	 *  @param  string	$moreattrib         To add more attribute on select
4458
	 * 	@return	int							<0 if error, Num of establishment found if OK (0, 1, 2, ...)
4459
	 */
4460
	public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4461
	{
4462
		global $langs, $conf;
4463
4464
		$langs->load("admin");
4465
		$num = 0;
4466
4467
		$sql = "SELECT rowid, name, fk_country, status, entity";
4468
		$sql .= " FROM ".MAIN_DB_PREFIX."establishment";
4469
		$sql .= " WHERE 1=1";
4470
		if ($status != 2) {
4471
			$sql .= " AND status = ".(int) $status;
4472
		}
4473
		if ($filtre) {
4474
			$sql .= " AND ".$filtre;
4475
		}
4476
		$sql .= " ORDER BY name";
4477
4478
		dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4479
		$result = $this->db->query($sql);
4480
		if ($result) {
4481
			$num = $this->db->num_rows($result);
4482
			$i = 0;
4483
			if ($num) {
4484
				print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4485
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4486
					print '<option value="-1">&nbsp;</option>';
4487
				}
4488
4489
				while ($i < $num) {
4490
					$obj = $this->db->fetch_object($result);
4491
					if ($selected == $obj->rowid) {
4492
						print '<option value="'.$obj->rowid.'" selected>';
4493
					} else {
4494
						print '<option value="'.$obj->rowid.'">';
4495
					}
4496
					print trim($obj->name);
4497
					if ($status == 2 && $obj->status == 1) {
4498
						print ' ('.$langs->trans("Closed").')';
4499
					}
4500
					print '</option>';
4501
					$i++;
4502
				}
4503
				print "</select>";
4504
			} else {
4505
				if ($status == 0) {
4506
					print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4507
				} else {
4508
					print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4509
				}
4510
			}
4511
		} else {
4512
			dol_print_error($this->db);
4513
		}
4514
	}
4515
4516
	/**
4517
	 *    Display form to select bank account
4518
	 *
4519
	 *    @param	string	$page        Page
4520
	 *    @param    int		$selected    Id of bank account
4521
	 *    @param    string	$htmlname    Name of select html field
4522
	 *    @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.
4523
	 *    @return	void
4524
	 */
4525
	public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4526
	{
4527
		global $langs;
4528
		if ($htmlname != "none") {
4529
			print '<form method="POST" action="'.$page.'">';
4530
			print '<input type="hidden" name="action" value="setbankaccount">';
4531
			print '<input type="hidden" name="token" value="'.newToken().'">';
4532
			print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4533
			$nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4534
			if ($nbaccountfound > 0) {
4535
				print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4536
			}
4537
			print '</form>';
4538
		} else {
4539
			$langs->load('banks');
4540
4541
			if ($selected) {
4542
				require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4543
				$bankstatic = new Account($this->db);
4544
				$result = $bankstatic->fetch($selected);
4545
				if ($result) {
4546
					print $bankstatic->getNomUrl(1);
4547
				}
4548
			} else {
4549
				print "&nbsp;";
4550
			}
4551
		}
4552
	}
4553
4554
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4555
	/**
4556
	 *    Return list of categories having choosed type
4557
	 *
4558
	 *    @param	string|int	            $type				Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
4559
	 *    @param    string		            $selected    		Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1.
4560
	 *    @param    string		            $htmlname			HTML field name
4561
	 *    @param    int			            $maxlength      	Maximum length for labels
4562
	 *    @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.
4563
	 *                                                          $markafterid can be an :
4564
	 *                                                          - int (id of category)
4565
	 *                                                          - string (categories ids seprated by comma)
4566
	 *                                                          - array (list of categories ids)
4567
	 *    @param	int			            $outputmode			0=HTML select string, 1=Array
4568
	 *    @param	int			            $include			[=0] Removed or 1=Keep only
4569
	 *    @param	string					$morecss			More CSS
4570
	 *    @return	string
4571
	 *    @see select_categories()
4572
	 */
4573
	public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4574
	{
4575
		// phpcs:enable
4576
		global $conf, $langs;
4577
		$langs->load("categories");
4578
4579
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4580
4581
		// For backward compatibility
4582
		if (is_numeric($type)) {
4583
			dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4584
		}
4585
4586
		if ($type === Categorie::TYPE_BANK_LINE) {
4587
			// TODO Move this into common category feature
4588
			$cate_arbo = array();
4589
			$sql = "SELECT c.label, c.rowid";
4590
			$sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c";
4591
			$sql .= " WHERE entity = ".$conf->entity;
4592
			$sql .= " ORDER BY c.label";
4593
			$result = $this->db->query($sql);
4594
			if ($result) {
4595
				$num = $this->db->num_rows($result);
4596
				$i = 0;
4597
				while ($i < $num) {
4598
					$objp = $this->db->fetch_object($result);
4599
					if ($objp) {
4600
						$cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label);
4601
					}
4602
					$i++;
4603
				}
4604
				$this->db->free($result);
4605
			} else {
4606
				dol_print_error($this->db);
4607
			}
4608
		} else {
4609
			$cat = new Categorie($this->db);
4610
			$cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4611
		}
4612
4613
		$output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4614
		$outarray = array();
4615
		if (is_array($cate_arbo)) {
4616
			if (!count($cate_arbo)) {
4617
				$output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4618
			} else {
4619
				$output .= '<option value="-1">&nbsp;</option>';
4620
				foreach ($cate_arbo as $key => $value) {
4621
					if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4622
						$add = 'selected ';
4623
					} else {
4624
						$add = '';
4625
					}
4626
					$output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'">'.dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle').'</option>';
4627
4628
					$outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4629
				}
4630
			}
4631
		}
4632
		$output .= '</select>';
4633
		$output .= "\n";
4634
4635
		if ($outputmode) {
4636
			return $outarray;
4637
		}
4638
		return $output;
4639
	}
4640
4641
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4642
	/**
4643
	 *     Show a confirmation HTML form or AJAX popup
4644
	 *
4645
	 *     @param	string		$page        	   	Url of page to call if confirmation is OK
4646
	 *     @param	string		$title       	   	Title
4647
	 *     @param	string		$question    	   	Question
4648
	 *     @param 	string		$action      	   	Action
4649
	 *	   @param	array		$formquestion	   	An array with forms complementary inputs
4650
	 * 	   @param	string		$selectedchoice		"" or "no" or "yes"
4651
	 * 	   @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
4652
	 *     @param	int			$height          	Force height of box
4653
	 *     @param	int			$width				Force width of box
4654
	 *     @return 	void
4655
	 *     @deprecated
4656
	 *     @see formconfirm()
4657
	 */
4658
	public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4659
	{
4660
		// phpcs:enable
4661
		dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4662
		print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4663
	}
4664
4665
	/**
4666
	 *     Show a confirmation HTML form or AJAX popup.
4667
	 *     Easiest way to use this is with useajax=1.
4668
	 *     If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters)
4669
	 *     just after calling this method. For example:
4670
	 *       print '<script type="text/javascript">'."\n";
4671
	 *       print 'jQuery(document).ready(function() {'."\n";
4672
	 *       print 'jQuery(".xxxlink").click(function(e) { jQuery("#aparamid").val(jQuery(this).attr("rel")); jQuery("#dialog-confirm-xxx").dialog("open"); return false; });'."\n";
4673
	 *       print '});'."\n";
4674
	 *       print '</script>'."\n";
4675
	 *
4676
	 *     @param  	string			$page        	   	Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated)
4677
	 *     @param	string			$title       	   	Title
4678
	 *     @param	string			$question    	   	Question
4679
	 *     @param 	string			$action      	   	Action
4680
	 *	   @param  	array|string	$formquestion	   	An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>))
4681
	 *													type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', 'other' or 'onecolumn'...
4682
	 * 	   @param  	string			$selectedchoice  	'' or 'no', or 'yes' or '1' or '0'
4683
	 * 	   @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
4684
	 *     @param  	int|string		$height          	Force height of box (0 = auto)
4685
	 *     @param	int				$width				Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones.
4686
	 *     @param	int				$disableformtag		1=Disable form tag. Can be used if we are already inside a <form> section.
4687
	 *     @return 	string      		    			HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form
4688
	 */
4689
	public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0)
4690
	{
4691
		global $langs, $conf;
4692
4693
		$more = '<!-- formconfirm before calling page='.dol_escape_htmltag($page).' -->';
4694
		$formconfirm = '';
4695
		$inputok = array();
4696
		$inputko = array();
4697
4698
		// Clean parameters
4699
		$newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
4700
		if ($conf->browser->layout == 'phone') {
4701
			$width = '95%';
4702
		}
4703
4704
		// Set height automatically if not defined
4705
		if (empty($height)) {
4706
			$height = 220;
4707
			if (is_array($formquestion) && count($formquestion) > 2) {
4708
				$height += ((count($formquestion) - 2) * 24);
4709
			}
4710
		}
4711
4712
		if (is_array($formquestion) && !empty($formquestion)) {
4713
			// First add hidden fields and value
4714
			foreach ($formquestion as $key => $input) {
4715
				if (is_array($input) && !empty($input)) {
4716
					if ($input['type'] == 'hidden') {
4717
						$more .= '<input type="hidden" id="'.$input['name'].'" name="'.$input['name'].'" value="'.dol_escape_htmltag($input['value']).'">'."\n";
4718
					}
4719
				}
4720
			}
4721
4722
			// Now add questions
4723
			$moreonecolumn = '';
4724
			$more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
4725
			foreach ($formquestion as $key => $input) {
4726
				if (is_array($input) && !empty($input)) {
4727
					$size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : '');
4728
					$moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4729
					$morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4730
4731
					if ($input['type'] == 'text') {
4732
						$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="'.$input['name'].'" name="'.$input['name'].'"'.$size.' value="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4733
					} elseif ($input['type'] == 'password')	{
4734
						$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="'.$input['name'].'" name="'.$input['name'].'"'.$size.' value="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4735
					} elseif ($input['type'] == 'select') {
4736
						if (empty($morecss)) {
4737
							$morecss = 'minwidth100';
4738
						}
4739
4740
						$show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
4741
						$key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
4742
						$value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
4743
						$translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
4744
						$maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
4745
						$disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
4746
						$sort = isset($input['select_sort']) ? $input['select_sort'] : '';
4747
4748
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4749
						if (!empty($input['label'])) {
4750
							$more .= $input['label'].'</div><div class="tagtd left">';
4751
						}
4752
						$more .= $this->selectarray($input['name'], $input['values'], $input['default'], $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
4753
						$more .= '</div></div>'."\n";
4754
					} elseif ($input['type'] == 'checkbox') {
4755
						$more .= '<div class="tagtr">';
4756
						$more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
4757
						$more .= '<input type="checkbox" class="flat'.$morecss.'" id="'.$input['name'].'" name="'.$input['name'].'"'.$moreattr;
4758
						if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0') {
4759
							$more .= ' checked';
4760
						}
4761
						if (is_bool($input['value']) && $input['value']) {
4762
							$more .= ' checked';
4763
						}
4764
						if (isset($input['disabled'])) {
4765
							$more .= ' disabled';
4766
						}
4767
						$more .= ' /></div>';
4768
						$more .= '</div>'."\n";
4769
					} elseif ($input['type'] == 'radio') {
4770
						$i = 0;
4771
						foreach ($input['values'] as $selkey => $selval) {
4772
							$more .= '<div class="tagtr">';
4773
							if ($i == 0) {
4774
								$more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
4775
							} else {
4776
								$more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
4777
							}
4778
							$more .= '<div class="tagtd'.($i == 0 ? ' tdtop' : '').'"><input type="radio" class="flat'.$morecss.'" id="'.$input['name'].$selkey.'" name="'.$input['name'].'" value="'.$selkey.'"'.$moreattr;
4779
							if ($input['disabled']) {
4780
								$more .= ' disabled';
4781
							}
4782
							if (isset($input['default']) && $input['default'] === $selkey) {
4783
								$more .= ' checked="checked"';
4784
							}
4785
							$more .= ' /> ';
4786
							$more .= '<label for="'.$input['name'].$selkey.'">'.$selval.'</label>';
4787
							$more .= '</div></div>'."\n";
4788
							$i++;
4789
						}
4790
					} elseif ($input['type'] == 'date') {
4791
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
4792
						$more .= '<div class="tagtd">';
4793
						$more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, 0);
4794
						$more .= '</div></div>'."\n";
4795
						$formquestion[] = array('name'=>$input['name'].'day');
4796
						$formquestion[] = array('name'=>$input['name'].'month');
4797
						$formquestion[] = array('name'=>$input['name'].'year');
4798
						$formquestion[] = array('name'=>$input['name'].'hour');
4799
						$formquestion[] = array('name'=>$input['name'].'min');
4800
					} elseif ($input['type'] == 'other') {
4801
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4802
						if (!empty($input['label'])) {
4803
							$more .= $input['label'].'</div><div class="tagtd">';
4804
						}
4805
						$more .= $input['value'];
4806
						$more .= '</div></div>'."\n";
4807
					} elseif ($input['type'] == 'onecolumn') {
4808
						$moreonecolumn .= '<div class="margintoponly">';
4809
						$moreonecolumn .= $input['value'];
4810
						$moreonecolumn .= '</div>'."\n";
4811
					} elseif ($input['type'] == 'hidden') {
4812
						// Do nothing more, already added by a previous loop
4813
					} else {
4814
						$more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
4815
					}
4816
				}
4817
			}
4818
			$more .= '</div>'."\n";
4819
			$more .= $moreonecolumn;
4820
		}
4821
4822
		// JQUI method dialog is broken with jmobile, we use standard HTML.
4823
		// 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
4824
		// See page product/card.php for example
4825
		if (!empty($conf->dol_use_jmobile)) {
4826
			$useajax = 0;
4827
		}
4828
		if (empty($conf->use_javascript_ajax)) {
4829
			$useajax = 0;
4830
		}
4831
4832
		if ($useajax) {
4833
			$autoOpen = true;
4834
			$dialogconfirm = 'dialog-confirm';
4835
			$button = '';
4836
			if (!is_numeric($useajax)) {
4837
				$button = $useajax;
4838
				$useajax = 1;
4839
				$autoOpen = false;
4840
				$dialogconfirm .= '-'.$button;
4841
			}
4842
			$pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes';
4843
			$pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : '');
4844
4845
			// Add input fields into list of fields to read during submit (inputok and inputko)
4846
			if (is_array($formquestion)) {
4847
				foreach ($formquestion as $key => $input) {
4848
					//print "xx ".$key." rr ".is_array($input)."<br>\n";
4849
					// Add name of fields to propagate with the GET when submitting the form with button OK.
4850
					if (is_array($input) && isset($input['name'])) {
4851
						if (strpos($input['name'], ',') > 0) {
4852
							$inputok = array_merge($inputok, explode(',', $input['name']));
4853
						} else {
4854
							array_push($inputok, $input['name']);
4855
						}
4856
					}
4857
					// Add name of fields to propagate with the GET when submitting the form with button KO.
4858
					if (isset($input['inputko']) && $input['inputko'] == 1) {
4859
						array_push($inputko, $input['name']);
4860
					}
4861
				}
4862
			}
4863
4864
			// Show JQuery confirm box.
4865
			$formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
4866
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4867
				$formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
4868
			}
4869
			if (!empty($more)) {
4870
				$formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
4871
			}
4872
			$formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
4873
			$formconfirm .= '</div>'."\n";
4874
4875
			$formconfirm .= "\n<!-- begin ajax formconfirm page=".$page." -->\n";
4876
			$formconfirm .= '<script type="text/javascript">'."\n";
4877
			$formconfirm .= 'jQuery(document).ready(function() {
4878
            $(function() {
4879
            	$( "#'.$dialogconfirm.'" ).dialog(
4880
            	{
4881
                    autoOpen: '.($autoOpen ? "true" : "false").',';
4882
			if ($newselectedchoice == 'no') {
4883
				$formconfirm .= '
4884
						open: function() {
4885
            				$(this).parent().find("button.ui-button:eq(2)").focus();
4886
						},';
4887
			}
4888
			$formconfirm .= '
4889
                    resizable: false,
4890
                    height: "'.$height.'",
4891
                    width: "'.$width.'",
4892
                    modal: true,
4893
                    closeOnEscape: false,
4894
                    buttons: {
4895
                        "'.dol_escape_js($langs->transnoentities("Yes")).'": function() {
4896
                        	var options = "&token='.urlencode(newToken()).'";
4897
                        	var inputok = '.json_encode($inputok).';	/* List of fields into form */
4898
                         	var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
4899
                         	if (inputok.length>0) {
4900
                         		$.each(inputok, function(i, inputname) {
4901
                         			var more = "";
4902
									var inputvalue;
4903
                         			if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
4904
										inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
4905
									} else {
4906
                         		    	if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4907
                         				inputvalue = $("#" + inputname + more).val();
4908
									}
4909
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4910
									console.log("check inputname="+inputname+" inputvalue="+inputvalue);
4911
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4912
                         		});
4913
                         	}
4914
                         	var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "") + options;
4915
            				if (pageyes.length > 0) { location.href = urljump; }
4916
                            $(this).dialog("close");
4917
                        },
4918
                        "'.dol_escape_js($langs->transnoentities("No")).'": function() {
4919
                        	var options = "&token='.urlencode(newToken()).'";
4920
                         	var inputko = '.json_encode($inputko).';	/* List of fields into form */
4921
                         	var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
4922
                         	if (inputko.length>0) {
4923
                         		$.each(inputko, function(i, inputname) {
4924
                         			var more = "";
4925
                         			if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4926
                         			var inputvalue = $("#" + inputname + more).val();
4927
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4928
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4929
                         		});
4930
                         	}
4931
                         	var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "") + options;
4932
                         	//alert(urljump);
4933
            				if (pageno.length > 0) { location.href = urljump; }
4934
                            $(this).dialog("close");
4935
                        }
4936
                    }
4937
                }
4938
                );
4939
4940
            	var button = "'.$button.'";
4941
            	if (button.length > 0) {
4942
                	$( "#" + button ).click(function() {
4943
                		$("#'.$dialogconfirm.'").dialog("open");
4944
        			});
4945
                }
4946
            });
4947
            });
4948
            </script>';
4949
			$formconfirm .= "<!-- end ajax formconfirm -->\n";
4950
		} else {
4951
			$formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
4952
4953
			if (empty($disableformtag)) {
4954
				$formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
4955
			}
4956
4957
			$formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
4958
			$formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
4959
4960
			$formconfirm .= '<table class="valid centpercent">'."\n";
4961
4962
			// Line title
4963
			$formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
4964
			$formconfirm .= img_picto('', 'recent').' '.$title;
4965
			$formconfirm .= '</td></tr>'."\n";
4966
4967
			// Line text
4968
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4969
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
4970
			}
4971
4972
			// Line form fields
4973
			if ($more) {
4974
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
4975
				$formconfirm .= $more;
4976
				$formconfirm .= '</td></tr>'."\n";
4977
			}
4978
4979
			// Line with question
4980
			$formconfirm .= '<tr class="valid">';
4981
			$formconfirm .= '<td class="valid">'.$question.'</td>';
4982
			$formconfirm .= '<td class="valid center">';
4983
			$formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly');
4984
			$formconfirm .= '<input class="button valignmiddle confirmvalidatebutton" type="submit" value="'.$langs->trans("Validate").'">';
4985
			$formconfirm .= '</td>';
4986
			$formconfirm .= '</tr>'."\n";
4987
4988
			$formconfirm .= '</table>'."\n";
4989
4990
			if (empty($disableformtag)) {
4991
				$formconfirm .= "</form>\n";
4992
			}
4993
			$formconfirm .= '<br>';
4994
4995
			if (empty($conf->use_javascript_ajax)) {
4996
				$formconfirm .= '<!-- code to disable button to avoid double clic -->';
4997
				$formconfirm .= '<script type="text/javascript">'."\n";
4998
				$formconfirm .= '
4999
				$(document).ready(function () {
5000
					$(".confirmvalidatebutton").on("click", function() {
5001
						console.log("We click on button");
5002
						$(this).attr("disabled", "disabled");
5003
						setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5004
						//console.log($(this).closest("form"));
5005
						$(this).closest("form").submit();
5006
					});
5007
				});
5008
				';
5009
				$formconfirm .= '</script>'."\n";
5010
			}
5011
5012
			$formconfirm .= "<!-- end formconfirm -->\n";
5013
		}
5014
5015
		return $formconfirm;
5016
	}
5017
5018
5019
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5020
	/**
5021
	 *    Show a form to select a project
5022
	 *
5023
	 *    @param	int		$page        		Page
5024
	 *    @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)
5025
	 *    @param    int		$selected    		Id pre-selected project
5026
	 *    @param    string	$htmlname    		Name of select field
5027
	 *    @param	int		$discard_closed		Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable)
5028
	 *    @param	int		$maxlength			Max length
5029
	 *    @param	int		$forcefocus			Force focus on field (works with javascript only)
5030
	 *    @param    int     $nooutput           No print is done. String is returned.
5031
	 *    @return	string                      Return html content
5032
	 */
5033
	public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0)
5034
	{
5035
		// phpcs:enable
5036
		global $langs;
5037
5038
		require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5039
		require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5040
5041
		$out = '';
5042
5043
		$formproject = new FormProjets($this->db);
5044
5045
		$langs->load("project");
5046
		if ($htmlname != "none") {
5047
			$out .= "\n";
5048
			$out .= '<form method="post" action="'.$page.'">';
5049
			$out .= '<input type="hidden" name="action" value="classin">';
5050
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5051
			$out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1);
5052
			$out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5053
			$out .= '</form>';
5054
		} else {
5055
			if ($selected) {
5056
				$projet = new Project($this->db);
5057
				$projet->fetch($selected);
5058
				//print '<a href="'.DOL_URL_ROOT.'/projet/card.php?id='.$selected.'">'.$projet->title.'</a>';
5059
				$out .= $projet->getNomUrl(0, '', 1);
5060
			} else {
5061
				$out .= "&nbsp;";
5062
			}
5063
		}
5064
5065
		if (empty($nooutput)) {
5066
			print $out;
5067
			return '';
5068
		}
5069
		return $out;
5070
	}
5071
5072
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5073
	/**
5074
	 *	Show a form to select payment conditions
5075
	 *
5076
	 *  @param	int		$page        	Page
5077
	 *  @param  string	$selected    	Id condition pre-selectionne
5078
	 *  @param  string	$htmlname    	Name of select html field
5079
	 *	@param	int		$addempty		Add empty entry
5080
	 *  @return	void
5081
	 */
5082
	public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0)
5083
	{
5084
		// phpcs:enable
5085
		global $langs;
5086
		if ($htmlname != "none") {
5087
			print '<form method="post" action="'.$page.'">';
5088
			print '<input type="hidden" name="action" value="setconditions">';
5089
			print '<input type="hidden" name="token" value="'.newToken().'">';
5090
			$this->select_conditions_paiements($selected, $htmlname, -1, $addempty);
5091
			print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5092
			print '</form>';
5093
		} else {
5094
			if ($selected) {
5095
				$this->load_cache_conditions_paiements();
5096
				if (isset($this->cache_conditions_paiements[$selected])) {
5097
					print $this->cache_conditions_paiements[$selected]['label'];
5098
				} else {
5099
					$langs->load('errors');
5100
					print $langs->trans('ErrorNotInDictionaryPaymentConditions');
5101
				}
5102
			} else {
5103
				print "&nbsp;";
5104
			}
5105
		}
5106
	}
5107
5108
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5109
	/**
5110
	 *  Show a form to select a delivery delay
5111
	 *
5112
	 *  @param  int		$page        	Page
5113
	 *  @param  string	$selected    	Id condition pre-selectionne
5114
	 *  @param  string	$htmlname    	Name of select html field
5115
	 *	@param	int		$addempty		Ajoute entree vide
5116
	 *  @return	void
5117
	 */
5118
	public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5119
	{
5120
		// phpcs:enable
5121
		global $langs;
5122
		if ($htmlname != "none") {
5123
			print '<form method="post" action="'.$page.'">';
5124
			print '<input type="hidden" name="action" value="setavailability">';
5125
			print '<input type="hidden" name="token" value="'.newToken().'">';
5126
			$this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5127
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5128
			print '</form>';
5129
		} else {
5130
			if ($selected) {
5131
				$this->load_cache_availability();
5132
				print $this->cache_availability[$selected]['label'];
5133
			} else {
5134
				print "&nbsp;";
5135
			}
5136
		}
5137
	}
5138
5139
	/**
5140
	 *  Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
5141
	 *  List found into table c_input_reason loaded by loadCacheInputReason
5142
	 *
5143
	 *  @param  string	$page        	Page
5144
	 *  @param  string	$selected    	Id condition pre-selectionne
5145
	 *  @param  string	$htmlname    	Name of select html field
5146
	 *  @param	int		$addempty		Add empty entry
5147
	 *  @return	void
5148
	 */
5149
	public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5150
	{
5151
		global $langs;
5152
		if ($htmlname != "none") {
5153
			print '<form method="post" action="'.$page.'">';
5154
			print '<input type="hidden" name="action" value="setdemandreason">';
5155
			print '<input type="hidden" name="token" value="'.newToken().'">';
5156
			$this->selectInputReason($selected, $htmlname, -1, $addempty);
5157
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5158
			print '</form>';
5159
		} else {
5160
			if ($selected) {
5161
				$this->loadCacheInputReason();
5162
				foreach ($this->cache_demand_reason as $key => $val) {
5163
					if ($val['id'] == $selected) {
5164
						print $val['label'];
5165
						break;
5166
					}
5167
				}
5168
			} else {
5169
				print "&nbsp;";
5170
			}
5171
		}
5172
	}
5173
5174
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5175
	/**
5176
	 *    Show a form + html select a date
5177
	 *
5178
	 *    @param	string		$page        	Page
5179
	 *    @param	string		$selected    	Date preselected
5180
	 *    @param    string		$htmlname    	Html name of date input fields or 'none'
5181
	 *    @param    int			$displayhour 	Display hour selector
5182
	 *    @param    int			$displaymin		Display minutes selector
5183
	 *    @param	int			$nooutput		1=No print output, return string
5184
	 *    @return	string
5185
	 *    @see		selectDate()
5186
	 */
5187
	public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0)
5188
	{
5189
		// phpcs:enable
5190
		global $langs;
5191
5192
		$ret = '';
5193
5194
		if ($htmlname != "none") {
5195
			$ret .= '<form method="post" action="'.$page.'" name="form'.$htmlname.'">';
5196
			$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5197
			$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5198
			$ret .= '<table class="nobordernopadding">';
5199
			$ret .= '<tr><td>';
5200
			$ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5201
			$ret .= '</td>';
5202
			$ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5203
			$ret .= '</tr></table></form>';
5204
		} else {
5205
			if ($displayhour) {
5206
				$ret .= dol_print_date($selected, 'dayhour');
5207
			} else {
5208
				$ret .= dol_print_date($selected, 'day');
5209
			}
5210
		}
5211
5212
		if (empty($nooutput)) {
5213
			print $ret;
5214
		}
5215
		return $ret;
5216
	}
5217
5218
5219
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5220
	/**
5221
	 *  Show a select form to choose a user
5222
	 *
5223
	 *  @param	string	$page        	Page
5224
	 *  @param  string	$selected    	Id of user preselected
5225
	 *  @param  string	$htmlname    	Name of input html field. If 'none', we just output the user link.
5226
	 *  @param  array	$exclude		List of users id to exclude
5227
	 *  @param  array	$include        List of users id to include
5228
	 *  @return	void
5229
	 */
5230
	public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5231
	{
5232
		// phpcs:enable
5233
		global $langs;
5234
5235
		if ($htmlname != "none") {
5236
			print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5237
			print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5238
			print '<input type="hidden" name="token" value="'.newToken().'">';
5239
			print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5240
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5241
			print '</form>';
5242
		} else {
5243
			if ($selected) {
5244
				require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5245
				$theuser = new User($this->db);
5246
				$theuser->fetch($selected);
5247
				print $theuser->getNomUrl(1);
5248
			} else {
5249
				print "&nbsp;";
5250
			}
5251
		}
5252
	}
5253
5254
5255
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5256
	/**
5257
	 *    Show form with payment mode
5258
	 *
5259
	 *    @param	string	$page        	Page
5260
	 *    @param    int		$selected    	Id mode pre-selectionne
5261
	 *    @param    string	$htmlname    	Name of select html field
5262
	 *    @param  	string	$filtertype		To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz))
5263
	 *    @param    int     $active         Active or not, -1 = all
5264
	 *    @param   int     $addempty       1=Add empty entry
5265
	 *    @return	void
5266
	 */
5267
	public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0)
5268
	{
5269
		// phpcs:enable
5270
		global $langs;
5271
		if ($htmlname != "none") {
5272
			print '<form method="POST" action="'.$page.'">';
5273
			print '<input type="hidden" name="action" value="setmode">';
5274
			print '<input type="hidden" name="token" value="'.newToken().'">';
5275
			$this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active);
5276
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5277
			print '</form>';
5278
		} else {
5279
			if ($selected) {
5280
				$this->load_cache_types_paiements();
5281
				print $this->cache_types_paiements[$selected]['label'];
5282
			} else {
5283
				print "&nbsp;";
5284
			}
5285
		}
5286
	}
5287
5288
	/**
5289
	 *    Show form with transport mode
5290
	 *
5291
	 *    @param	string	$page        	Page
5292
	 *    @param    int		$selected    	Id mode pre-select
5293
	 *    @param    string	$htmlname    	Name of select html field
5294
	 *    @param    int     $active         Active or not, -1 = all
5295
	 *    @param    int     $addempty       1=Add empty entry
5296
	 *    @return	void
5297
	 */
5298
	public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5299
	{
5300
		global $langs;
5301
		if ($htmlname != "none") {
5302
			print '<form method="POST" action="'.$page.'">';
5303
			print '<input type="hidden" name="action" value="settransportmode">';
5304
			print '<input type="hidden" name="token" value="'.newToken().'">';
5305
			$this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5306
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5307
			print '</form>';
5308
		} else {
5309
			if ($selected) {
5310
				$this->load_cache_transport_mode();
5311
				print $this->cache_transport_mode[$selected]['label'];
5312
			} else {
5313
				print "&nbsp;";
5314
			}
5315
		}
5316
	}
5317
5318
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5319
	/**
5320
	 *    Show form with multicurrency code
5321
	 *
5322
	 *    @param	string	$page        	Page
5323
	 *    @param    string	$selected    	code pre-selectionne
5324
	 *    @param    string	$htmlname    	Name of select html field
5325
	 *    @return	void
5326
	 */
5327
	public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5328
	{
5329
		// phpcs:enable
5330
		global $langs;
5331
		if ($htmlname != "none") {
5332
			print '<form method="POST" action="'.$page.'">';
5333
			print '<input type="hidden" name="action" value="setmulticurrencycode">';
5334
			print '<input type="hidden" name="token" value="'.newToken().'">';
5335
			print $this->selectMultiCurrency($selected, $htmlname, 0);
5336
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5337
			print '</form>';
5338
		} else {
5339
			dol_include_once('/core/lib/company.lib.php');
5340
			print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5341
		}
5342
	}
5343
5344
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5345
	/**
5346
	 *    Show form with multicurrency rate
5347
	 *
5348
	 *    @param	string	$page        	Page
5349
	 *    @param    double	$rate	    	Current rate
5350
	 *    @param    string	$htmlname    	Name of select html field
5351
	 *    @param    string  $currency       Currency code to explain the rate
5352
	 *    @return	void
5353
	 */
5354
	public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5355
	{
5356
		// phpcs:enable
5357
		global $langs, $mysoc, $conf;
5358
5359
		if ($htmlname != "none") {
5360
			print '<form method="POST" action="'.$page.'">';
5361
			print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5362
			print '<input type="hidden" name="token" value="'.newToken().'">';
5363
			print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5364
			print '<select name="calculation_mode">';
5365
			print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5366
			print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5367
			print '</select> ';
5368
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5369
			print '</form>';
5370
		} else {
5371
			if (!empty($rate)) {
5372
				print price($rate, 1, $langs, 1, 0);
5373
				if ($currency && $rate != 1) {
5374
					print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5375
				}
5376
			} else {
5377
				print 1;
5378
			}
5379
		}
5380
	}
5381
5382
5383
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5384
	/**
5385
	 *	Show a select box with available absolute discounts
5386
	 *
5387
	 *  @param  string	$page        	Page URL where form is shown
5388
	 *  @param  int		$selected    	Value pre-selected
5389
	 *	@param  string	$htmlname    	Name of SELECT component. If 'none', not changeable. Example 'remise_id'.
5390
	 *	@param	int		$socid			Third party id
5391
	 * 	@param	float	$amount			Total amount available
5392
	 * 	@param	string	$filter			SQL filter on discounts
5393
	 * 	@param	int		$maxvalue		Max value for lines that can be selected
5394
	 *  @param  string	$more           More string to add
5395
	 *  @param  int     $hidelist       1=Hide list
5396
	 *  @param	int		$discount_type	0 => customer discount, 1 => supplier discount
5397
	 *  @return	void
5398
	 */
5399
	public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5400
	{
5401
		// phpcs:enable
5402
		global $conf, $langs;
5403
		if ($htmlname != "none") {
5404
			print '<form method="post" action="'.$page.'">';
5405
			print '<input type="hidden" name="action" value="setabsolutediscount">';
5406
			print '<input type="hidden" name="token" value="'.newToken().'">';
5407
			print '<div class="inline-block">';
5408
			if (!empty($discount_type)) {
5409
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5410
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5411
						$translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5412
					} else {
5413
						$translationKey = 'HasCreditNoteFromSupplier';
5414
					}
5415
				} else {
5416
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5417
						$translationKey = 'HasAbsoluteDiscountFromSupplier';
5418
					} else {
5419
						$translationKey = 'HasCreditNoteFromSupplier';
5420
					}
5421
				}
5422
			} else {
5423
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5424
					if (!$filter || $filter == "fk_facture_source IS NULL") {
5425
						$translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5426
					} else {
5427
						$translationKey = 'CompanyHasCreditNote';
5428
					}
5429
				} else {
5430
					if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5431
						$translationKey = 'CompanyHasAbsoluteDiscount';
5432
					} else {
5433
						$translationKey = 'CompanyHasCreditNote';
5434
					}
5435
				}
5436
			}
5437
			print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5438
			if (empty($hidelist)) {
5439
				print ' ';
5440
			}
5441
			print '</div>';
5442
			if (empty($hidelist)) {
5443
				print '<div class="inline-block" style="padding-right: 10px">';
5444
				$newfilter = 'discount_type='.intval($discount_type);
5445
				if (!empty($discount_type)) {
5446
					$newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5447
				} else {
5448
					$newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5449
				}
5450
				if ($filter) {
5451
					$newfilter .= ' AND ('.$filter.')';
5452
				}
5453
				$nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5454
				if ($nbqualifiedlines > 0) {
5455
					print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5456
					if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5457
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5458
					}
5459
					if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5460
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5461
					}
5462
5463
					print '>';
5464
				}
5465
				print '</div>';
5466
			}
5467
			if ($more) {
5468
				print '<div class="inline-block">';
5469
				print $more;
5470
				print '</div>';
5471
			}
5472
			print '</form>';
5473
		} else {
5474
			if ($selected) {
5475
				print $selected;
5476
			} else {
5477
				print "0";
5478
			}
5479
		}
5480
	}
5481
5482
5483
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5484
	/**
5485
	 *  Show forms to select a contact
5486
	 *
5487
	 *  @param	string		$page        	Page
5488
	 *  @param	Societe		$societe		Filter on third party
5489
	 *  @param    int			$selected    	Id contact pre-selectionne
5490
	 *  @param    string		$htmlname    	Name of HTML select. If 'none', we just show contact link.
5491
	 *  @return	void
5492
	 */
5493
	public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5494
	{
5495
		// phpcs:enable
5496
		global $langs, $conf;
5497
5498
		if ($htmlname != "none") {
5499
			print '<form method="post" action="'.$page.'">';
5500
			print '<input type="hidden" name="action" value="set_contact">';
5501
			print '<input type="hidden" name="token" value="'.newToken().'">';
5502
			print '<table class="nobordernopadding">';
5503
			print '<tr><td>';
5504
			print $this->selectcontacts($societe->id, $selected, $htmlname);
5505
			$num = $this->num;
5506
			if ($num == 0) {
5507
				$addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5508
				print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5509
			}
5510
			print '</td>';
5511
			print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5512
			print '</tr></table></form>';
5513
		} else {
5514
			if ($selected) {
5515
				require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5516
				$contact = new Contact($this->db);
5517
				$contact->fetch($selected);
5518
				print $contact->getFullName($langs);
5519
			} else {
5520
				print "&nbsp;";
5521
			}
5522
		}
5523
	}
5524
5525
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5526
	/**
5527
	 *  Output html select to select thirdparty
5528
	 *
5529
	 *  @param	string	$page       	Page
5530
	 *  @param  string	$selected   	Id preselected
5531
	 *  @param  string	$htmlname		Name of HTML select
5532
	 *  @param  string	$filter         Optional filters criteras. Do not use a filter coming from input of users.
5533
	 *	@param	int		$showempty		Add an empty field
5534
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
5535
	 * 	@param	int		$forcecombo		Force to use combo box
5536
	 *  @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')))
5537
	 *  @param  int     $nooutput       No print output. Return it only.
5538
	 *  @param	array	$excludeids		Exclude IDs from the select combo
5539
	 *  @return	void|string
5540
	 */
5541
	public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array())
5542
	{
5543
		// phpcs:enable
5544
		global $langs;
5545
5546
		$out = '';
5547
		if ($htmlname != "none") {
5548
			$out .= '<form method="post" action="'.$page.'">';
5549
			$out .= '<input type="hidden" name="action" value="set_thirdparty">';
5550
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5551
			$out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
5552
			$out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5553
			$out .= '</form>';
5554
		} else {
5555
			if ($selected) {
5556
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5557
				$soc = new Societe($this->db);
5558
				$soc->fetch($selected);
5559
				$out .= $soc->getNomUrl($langs);
5560
			} else {
5561
				$out .= "&nbsp;";
5562
			}
5563
		}
5564
5565
		if ($nooutput) {
5566
			return $out;
5567
		} else {
5568
			print $out;
5569
		}
5570
	}
5571
5572
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5573
	/**
5574
	 *    Retourne la liste des devises, dans la langue de l'utilisateur
5575
	 *
5576
	 *    @param	string	$selected    preselected currency code
5577
	 *    @param    string	$htmlname    name of HTML select list
5578
	 *    @deprecated
5579
	 *    @return	void
5580
	 */
5581
	public function select_currency($selected = '', $htmlname = 'currency_id')
5582
	{
5583
		// phpcs:enable
5584
		print $this->selectCurrency($selected, $htmlname);
5585
	}
5586
5587
	/**
5588
	 *  Retourne la liste des devises, dans la langue de l'utilisateur
5589
	 *
5590
	 *  @param	string	$selected    preselected currency code
5591
	 *  @param  string	$htmlname    name of HTML select list
5592
	 *  @param  string  $mode        0 = Add currency symbol into label, 1 = Add 3 letter iso code
5593
	 * 	@return	string
5594
	 */
5595
	public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0)
5596
	{
5597
		global $conf, $langs, $user;
5598
5599
		$langs->loadCacheCurrencies('');
5600
5601
		$out = '';
5602
5603
		if ($selected == 'euro' || $selected == 'euros') {
5604
			$selected = 'EUR'; // Pour compatibilite
5605
		}
5606
5607
		$out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
5608
		foreach ($langs->cache_currencies as $code_iso => $currency) {
5609
			$labeltoshow = $currency['label'];
5610
			if ($mode == 1) {
5611
				$labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
5612
			} else {
5613
				$labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
5614
			}
5615
5616
			if ($selected && $selected == $code_iso) {
5617
				$out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
5618
			} else {
5619
				$out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
5620
			}
5621
			$out .= $labeltoshow;
5622
			$out .= '</option>';
5623
		}
5624
		$out .= '</select>';
5625
		if ($user->admin) {
5626
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5627
		}
5628
5629
		// Make select dynamic
5630
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5631
		$out .= ajax_combobox($htmlname);
5632
5633
		return $out;
5634
	}
5635
5636
	/**
5637
	 *	Return array of currencies in user language
5638
	 *
5639
	 *  @param	string	$selected    preselected currency code
5640
	 *  @param  string	$htmlname    name of HTML select list
5641
	 *  @param  integer	$useempty    1=Add empty line
5642
	 *  @param string $filter Optional filters criteras (example: 'code <> x', ' in (1,3)')
5643
	 *  @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
5644
	 * 	@return	string
5645
	 */
5646
	public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false)
5647
	{
5648
		global $db, $conf, $langs, $user;
5649
5650
		$langs->loadCacheCurrencies(''); // Load ->cache_currencies
5651
5652
		$TCurrency = array();
5653
5654
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency';
5655
		$sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
5656
		if ($filter) {
5657
			$sql .= " AND ".$filter;
5658
		}
5659
		$resql = $this->db->query($sql);
5660
		if ($resql) {
5661
			while ($obj = $this->db->fetch_object($resql)) {
5662
				$TCurrency[$obj->code] = $obj->code;
5663
			}
5664
		}
5665
5666
		$out = '';
5667
		$out .= '<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">';
5668
		if ($useempty) {
5669
			$out .= '<option value="">&nbsp;</option>';
5670
		}
5671
		// If company current currency not in table, we add it into list. Should always be available.
5672
		if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
5673
			$TCurrency[$conf->currency] = $conf->currency;
5674
		}
5675
		if (count($TCurrency) > 0) {
5676
			foreach ($langs->cache_currencies as $code_iso => $currency) {
5677
				if (isset($TCurrency[$code_iso])) {
5678
					if (!empty($selected) && $selected == $code_iso) {
5679
						$out .= '<option value="'.$code_iso.'" selected="selected">';
5680
					} else {
5681
						$out .= '<option value="'.$code_iso.'">';
5682
					}
5683
5684
					$out .= $currency['label'];
5685
					$out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
5686
					$out .= '</option>';
5687
				}
5688
			}
5689
		}
5690
5691
		$out .= '</select>';
5692
		// Make select dynamic
5693
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5694
		$out .= ajax_combobox($htmlname);
5695
5696
		return $out;
5697
	}
5698
5699
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5700
	/**
5701
	 *  Load into the cache vat rates of a country
5702
	 *
5703
	 *  @param	string	$country_code		Country code with quotes ("'CA'", or "'CA,IN,...'")
5704
	 *  @return	int							Nb of loaded lines, 0 if already loaded, <0 if KO
5705
	 */
5706
	public function load_cache_vatrates($country_code)
5707
	{
5708
		// phpcs:enable
5709
		global $langs;
5710
5711
		$num = count($this->cache_vatrates);
5712
		if ($num > 0) {
5713
			return $num; // Cache already loaded
5714
		}
5715
5716
		dol_syslog(__METHOD__, LOG_DEBUG);
5717
5718
		$sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
5719
		$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5720
		$sql .= " WHERE t.fk_pays = c.rowid";
5721
		$sql .= " AND t.active > 0";
5722
		$sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
5723
		$sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
5724
5725
		$resql = $this->db->query($sql);
5726
		if ($resql) {
5727
			$num = $this->db->num_rows($resql);
5728
			if ($num) {
5729
				for ($i = 0; $i < $num; $i++) {
5730
					$obj = $this->db->fetch_object($resql);
5731
					$this->cache_vatrates[$i]['rowid']	= $obj->rowid;
5732
					$this->cache_vatrates[$i]['code'] = $obj->code;
5733
					$this->cache_vatrates[$i]['txtva']	= $obj->taux;
5734
					$this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
5735
					$this->cache_vatrates[$i]['localtax1']	    = $obj->localtax1;
5736
					$this->cache_vatrates[$i]['localtax1_type']	= $obj->localtax1_type;
5737
					$this->cache_vatrates[$i]['localtax2']	    = $obj->localtax2;
5738
					$this->cache_vatrates[$i]['localtax2_type']	= $obj->localtax1_type;
5739
5740
					$this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
5741
					$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
5742
					$positiverates = '';
5743
					if ($obj->taux) {
5744
						$positiverates .= ($positiverates ? '/' : '').$obj->taux;
5745
					}
5746
					if ($obj->localtax1) {
5747
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
5748
					}
5749
					if ($obj->localtax2) {
5750
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
5751
					}
5752
					if (empty($positiverates)) {
5753
						$positiverates = '0';
5754
					}
5755
					$this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
5756
				}
5757
5758
				return $num;
5759
			} else {
5760
				$this->error = '<font class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</font>';
5761
				return -1;
5762
			}
5763
		} else {
5764
			$this->error = '<font class="error">'.$this->db->error().'</font>';
5765
			return -2;
5766
		}
5767
	}
5768
5769
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5770
	/**
5771
	 *  Output an HTML select vat rate.
5772
	 *  The name of this function should be selectVat. We keep bad name for compatibility purpose.
5773
	 *
5774
	 *  @param	string	      $htmlname           Name of HTML select field
5775
	 *  @param  float|string  $selectedrate       Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing.
5776
	 *  @param  Societe	      $societe_vendeuse   Thirdparty seller
5777
	 *  @param  Societe	      $societe_acheteuse  Thirdparty buyer
5778
	 *  @param  int		      $idprod             Id product. O if unknown of NA.
5779
	 *  @param  int		      $info_bits          Miscellaneous information on line (1 for NPR)
5780
	 *  @param  int|string    $type               ''=Unknown, 0=Product, 1=Service (Used if idprod not defined)
5781
	 *                  		                  Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5782
	 *                  					      Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5783
	 *                  					      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.
5784
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle.
5785
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle.
5786
	 *                  					      Sinon la TVA proposee par defaut=0. Fin de regle.
5787
	 *  @param	bool	     $options_only		  Return HTML options lines only (for ajax treatment)
5788
	 *  @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
5789
	 *  @return	string
5790
	 */
5791
	public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
5792
	{
5793
		// phpcs:enable
5794
		global $langs, $conf, $mysoc;
5795
5796
		$langs->load('errors');
5797
5798
		$return = '';
5799
5800
		// Define defaultnpr, defaultttx and defaultcode
5801
		$defaultnpr = ($info_bits & 0x01);
5802
		$defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
5803
		$defaulttx = str_replace('*', '', $selectedrate);
5804
		$defaultcode = '';
5805
		$reg = array();
5806
		if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5807
			$defaultcode = $reg[1];
5808
			$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5809
		}
5810
		//var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
5811
5812
		// Check parameters
5813
		if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
5814
			if ($societe_vendeuse->id == $mysoc->id) {
5815
				$return .= '<font class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</font>';
5816
			} else {
5817
				$return .= '<font class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</font>';
5818
			}
5819
			return $return;
5820
		}
5821
5822
		//var_dump($societe_acheteuse);
5823
		//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";
5824
		//exit;
5825
5826
		// Define list of countries to use to search VAT rates to show
5827
		// First we defined code_country to use to find list
5828
		if (is_object($societe_vendeuse)) {
5829
			$code_country = "'".$societe_vendeuse->country_code."'";
5830
		} else {
5831
			$code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
5832
		}
5833
		if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {    // If option to have vat for end customer for services is on
5834
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
5835
			if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
5836
				// We also add the buyer
5837
				if (is_numeric($type)) {
5838
					if ($type == 1) { // We know product is a service
5839
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5840
					}
5841
				} elseif (!$idprod) {  // We don't know type of product
5842
					$code_country .= ",'".$societe_acheteuse->country_code."'";
5843
				} else {
5844
					$prodstatic = new Product($this->db);
5845
					$prodstatic->fetch($idprod);
5846
					if ($prodstatic->type == Product::TYPE_SERVICE) {   // We know product is a service
5847
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5848
					}
5849
				}
5850
			}
5851
		}
5852
5853
		// Now we get list
5854
		$num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
5855
5856
		if ($num > 0) {
5857
			// Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
5858
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
5859
				$tmpthirdparty = new Societe($this->db);
5860
				$defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5861
				$defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5862
				if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5863
					$defaultcode = $reg[1];
5864
					$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5865
				}
5866
				if (empty($defaulttx)) {
5867
					$defaultnpr = 0;
5868
				}
5869
			}
5870
5871
			// Si taux par defaut n'a pu etre determine, on prend dernier de la liste.
5872
			// Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant
5873
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
5874
				if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
5875
					$defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
5876
				} else {
5877
					$defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS);
5878
				}
5879
			}
5880
5881
			// Disabled if seller is not subject to VAT
5882
			$disabled = false;
5883
			$title = '';
5884
			if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
5885
				// Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
5886
				// of using supplier invoices (this is a very bad idea !)
5887
				if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
5888
					$title = ' title="'.$langs->trans('VATIsNotUsed').'"';
5889
					$disabled = true;
5890
				}
5891
			}
5892
5893
			if (!$options_only) {
5894
				$return .= '<select class="flat minwidth75imp" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
5895
			}
5896
5897
			$selectedfound = false;
5898
			foreach ($this->cache_vatrates as $rate) {
5899
				// Keep only 0 if seller is not subject to VAT
5900
				if ($disabled && $rate['txtva'] != 0) {
5901
					continue;
5902
				}
5903
5904
				// Define key to use into select list
5905
				$key = $rate['txtva'];
5906
				$key .= $rate['nprtva'] ? '*' : '';
5907
				if ($mode > 0 && $rate['code']) {
5908
					$key .= ' ('.$rate['code'].')';
5909
				}
5910
				if ($mode < 0) {
5911
					$key = $rate['rowid'];
5912
				}
5913
5914
				$return .= '<option value="'.$key.'"';
5915
				if (!$selectedfound) {
5916
					if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
5917
						if ($defaultcode == $rate['code']) {
5918
							$return .= ' selected';
5919
							$selectedfound = true;
5920
						}
5921
					} elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
5922
						$return .= ' selected';
5923
						$selectedfound = true;
5924
					}
5925
				}
5926
				$return .= '>';
5927
				//if (! empty($conf->global->MAIN_VAT_SHOW_POSITIVE_RATES))
5928
				if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
5929
					$return .= $rate['labelpositiverates'];
5930
				} else {
5931
					$return .= vatrate($rate['label']);
5932
				}
5933
				//$return.=($rate['code']?' '.$rate['code']:'');
5934
				$return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the *  (old behaviour only if new vat code is not used)
5935
5936
				$return .= '</option>';
5937
			}
5938
5939
			if (!$options_only) {
5940
				$return .= '</select>';
5941
			}
5942
		} else {
5943
			$return .= $this->error;
5944
		}
5945
5946
		$this->num = $num;
5947
		return $return;
5948
	}
5949
5950
5951
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5952
	/**
5953
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5954
	 *  Fields are preselected with :
5955
	 *            	- set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5956
	 *            	- local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5957
	 *            	- Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5958
	 *
5959
	 *	@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).
5960
	 *	@param	string		$prefix			Prefix for fields name
5961
	 *	@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
5962
	 *	@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
5963
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5964
	 *	@param	string		$form_name 		Not used
5965
	 *	@param	int			$d				1=Show days, month, years
5966
	 * 	@param	int			$addnowlink		Add a link "Now"
5967
	 * 	@param	int			$nooutput		Do not output html string but return it
5968
	 * 	@param 	int			$disabled		Disable input fields
5969
	 *  @param  int			$fullday        When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59
5970
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another select_date field.
5971
	 *  @param  datetime    $adddateof      Add a link "Date of invoice" using the following date.
5972
	 *  @return	string|void					Nothing or string if nooutput is 1
5973
	 *  @deprecated
5974
	 *  @see    selectDate(), form_date(), select_month(), select_year(), select_dayofweek()
5975
	 */
5976
	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 = '')
5977
	{
5978
		// phpcs:enable
5979
		$retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
5980
		if (!empty($nooutput)) {
5981
			return $retstring;
5982
		}
5983
		print $retstring;
5984
		return;
5985
	}
5986
5987
	/**
5988
	 *  Show 2 HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5989
	 *  Fields are preselected with :
5990
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5991
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5992
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5993
	 *
5994
	 *  @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).
5995
	 *  @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).
5996
	 *  @param	string		$prefix			Prefix for fields name
5997
	 *  @param	string		$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5998
	 * 	@return string                      Html for selectDate
5999
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6000
	 */
6001
	public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0)
6002
	{
6003
		global $langs;
6004
6005
		$ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6006
		$ret .= '<br>';
6007
		$ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6008
		return $ret;
6009
	}
6010
6011
	/**
6012
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6013
	 *  Fields are preselected with :
6014
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6015
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6016
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6017
	 *
6018
	 *  @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).
6019
	 *  @param	string		$prefix			Prefix for fields name
6020
	 *  @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
6021
	 *	@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
6022
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6023
	 *	@param	string		$form_name 		Not used
6024
	 *	@param	int			$d				1=Show days, month, years
6025
	 * 	@param	int			$addnowlink		Add a link "Now", 1 with server time, 2 with local computer time
6026
	 * 	@param 	int			$disabled		Disable input fields
6027
	 *  @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')
6028
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another selectDate field.
6029
	 *  @param  datetime    $adddateof      Add a link "Date of ..." using the following date. See also $labeladddateof for the label used.
6030
	 *  @param  string      $openinghours   Specify hour start and hour end for the select ex 8,20
6031
	 *  @param  int         $stepminutes    Specify step for minutes between 1 and 30
6032
	 *  @param	string		$labeladddateof Label to use for the $adddateof parameter.
6033
	 *  @param	string 		$placeholder    Placeholder
6034
	 *  @param	mixed		$gm				'auto' (for backward compatibility, avoid this), 'gmt' or 'tzserver' or 'tzuserrel'
6035
	 * 	@return string                      Html for selectDate
6036
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6037
	 */
6038
	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')
6039
	{
6040
		global $conf, $langs;
6041
6042
		if ($gm === 'auto') {
6043
			$gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6044
		}
6045
6046
		$retstring = '';
6047
6048
		if ($prefix == '') {
6049
			$prefix = 're';
6050
		}
6051
		if ($h == '') {
6052
			$h = 0;
6053
		}
6054
		if ($m == '') {
6055
			$m = 0;
6056
		}
6057
		$emptydate = 0;
6058
		$emptyhours = 0;
6059
		if ($stepminutes <= 0 || $stepminutes > 30) {
6060
			$stepminutes = 1;
6061
		}
6062
		if ($empty == 1) {
6063
			$emptydate = 1;
6064
			$emptyhours = 1;
6065
		}
6066
		if ($empty == 2) {
6067
			$emptydate = 0;
6068
			$emptyhours = 1;
6069
		}
6070
		$orig_set_time = $set_time;
6071
6072
		if ($set_time === '' && $emptydate == 0) {
6073
			include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6074
			if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6075
				$set_time = dol_now($gm);
6076
			} else {
6077
				$set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6078
			}
6079
		}
6080
6081
		// Analysis of the pre-selection date
6082
		$reg = array();
6083
		if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) {	// deprecated usage
6084
			// Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6085
			$syear	= (!empty($reg[1]) ? $reg[1] : '');
6086
			$smonth = (!empty($reg[2]) ? $reg[2] : '');
6087
			$sday	= (!empty($reg[3]) ? $reg[3] : '');
6088
			$shour	= (!empty($reg[4]) ? $reg[4] : '');
6089
			$smin	= (!empty($reg[5]) ? $reg[5] : '');
6090
		} elseif (strval($set_time) != '' && $set_time != -1) {
6091
			// set_time est un timestamps (0 possible)
6092
			$syear = dol_print_date($set_time, "%Y", $gm);
6093
			$smonth = dol_print_date($set_time, "%m", $gm);
6094
			$sday = dol_print_date($set_time, "%d", $gm);
6095
			if ($orig_set_time != '') {
6096
				$shour = dol_print_date($set_time, "%H", $gm);
6097
				$smin = dol_print_date($set_time, "%M", $gm);
6098
				$ssec = dol_print_date($set_time, "%S", $gm);
6099
			} else {
6100
				$shour = '';
6101
				$smin = '';
6102
				$ssec = '';
6103
			}
6104
		} else {
6105
			// Date est '' ou vaut -1
6106
			$syear = '';
6107
			$smonth = '';
6108
			$sday = '';
6109
			$shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6110
			$smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6111
			$ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6112
		}
6113
		if ($h == 3) {
6114
			$shour = '';
6115
		}
6116
		if ($m == 3) {
6117
			$smin = '';
6118
		}
6119
6120
		$nowgmt = dol_now('gmt');
6121
		//var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6122
6123
		// You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6124
		$usecalendar = 'combo';
6125
		if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6126
			$usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6127
		}
6128
6129
		if ($d) {
6130
			// Show date with popup
6131
			if ($usecalendar != 'combo') {
6132
				$formated_date = '';
6133
				//print "e".$set_time." t ".$conf->format_date_short;
6134
				if (strval($set_time) != '' && $set_time != -1) {
6135
					//$formated_date=dol_print_date($set_time,$conf->format_date_short);
6136
					$formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6137
				}
6138
6139
				// Calendrier popup version eldy
6140
				if ($usecalendar == "eldy") {
6141
					// Input area to enter date manually
6142
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6143
					$retstring .= ($disabled ? ' disabled' : '');
6144
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6145
					$retstring .= '>';
6146
6147
					// Icon calendar
6148
					$retstringbuttom = '';
6149
					if (!$disabled) {
6150
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
6151
						$base = DOL_URL_ROOT.'/core/';
6152
						$retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
6153
						$retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
6154
					} else {
6155
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6156
					}
6157
					$retstring = $retstringbuttom.$retstring;
6158
6159
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6160
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6161
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6162
				} elseif ($usecalendar == 'jquery') {
6163
					if (!$disabled) {
6164
						// Output javascript for datepicker
6165
						$retstring .= "<script type='text/javascript'>";
6166
						$retstring .= "$(function(){ $('#".$prefix."').datepicker({
6167
							dateFormat: '".$langs->trans("FormatDateShortJQueryInput")."',
6168
							autoclose: true,
6169
							todayHighlight: true,";
6170
						if (!empty($conf->dol_use_jmobile)) {
6171
							$retstring .= "
6172
								beforeShow: function (input, datePicker) {
6173
									input.disabled = true;
6174
								},
6175
								onClose: function (dateText, datePicker) {
6176
									this.disabled = false;
6177
								},
6178
								";
6179
						}
6180
						// Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
6181
						if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS)) {
6182
							$retstring .= "
6183
								showOn: 'button',	/* both has problem with autocompletion */
6184
								buttonImage: '".DOL_URL_ROOT."/theme/".$conf->theme."/img/object_calendarday.png',
6185
								buttonImageOnly: true";
6186
						}
6187
						$retstring .= "
6188
							}) });";
6189
						$retstring .= "</script>";
6190
					}
6191
6192
					// Zone de saisie manuelle de la date
6193
					$retstring .= '<div class="nowrap inline-block divfordateinput">';
6194
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6195
					$retstring .= ($disabled ? ' disabled' : '');
6196
					$retstring .= ($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '');
6197
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6198
					$retstring .= '>';
6199
6200
					// Icone calendrier
6201
					if (!$disabled) {
6202
						/* Not required. Managed by option buttonImage of jquery
6203
						$retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
6204
						$retstring.="<script type='text/javascript'>";
6205
						$retstring.="jQuery(document).ready(function() {";
6206
						$retstring.='	jQuery("#'.$prefix.'id").click(function() {';
6207
						$retstring.="    	jQuery('#".$prefix."').focus();";
6208
						$retstring.='    });';
6209
						$retstring.='});';
6210
						$retstring.="</script>";*/
6211
					} else {
6212
						$retstringbutton = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6213
						$retsring = $retstringbutton.$retstring;
6214
					}
6215
6216
					$retstring .= '</div>';
6217
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6218
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6219
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6220
				} else {
6221
					$retstring .= "Bad value of MAIN_POPUP_CALENDAR";
6222
				}
6223
			} else {
6224
				// Show date with combo selects
6225
				// Day
6226
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
6227
6228
				if ($emptydate || $set_time == -1) {
6229
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6230
				}
6231
6232
				for ($day = 1; $day <= 31; $day++) {
6233
					$retstring .= '<option value="'.$day.'"'.($day == $sday ? ' selected' : '').'>'.$day.'</option>';
6234
				}
6235
6236
				$retstring .= "</select>";
6237
6238
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
6239
				if ($emptydate || $set_time == -1) {
6240
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6241
				}
6242
6243
				// Month
6244
				for ($month = 1; $month <= 12; $month++) {
6245
					$retstring .= '<option value="'.$month.'"'.($month == $smonth ? ' selected' : '').'>';
6246
					$retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
6247
					$retstring .= "</option>";
6248
				}
6249
				$retstring .= "</select>";
6250
6251
				// Year
6252
				if ($emptydate || $set_time == -1) {
6253
					$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.'">';
6254
				} else {
6255
					$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
6256
6257
					for ($year = $syear - 10; $year < $syear + 10; $year++) {
6258
						$retstring .= '<option value="'.$year.'"'.($year == $syear ? ' selected' : '').'>'.$year.'</option>';
6259
					}
6260
					$retstring .= "</select>\n";
6261
				}
6262
			}
6263
		}
6264
6265
		if ($d && $h) {
6266
			$retstring .= ($h == 2 ? '<br>' : ' ');
6267
			$retstring .= '<span class="nowraponall">';
6268
		}
6269
6270
		if ($h) {
6271
			$hourstart = 0;
6272
			$hourend = 24;
6273
			if ($openinghours != '') {
6274
				$openinghours = explode(',', $openinghours);
6275
				$hourstart = $openinghours[0];
6276
				$hourend = $openinghours[1];
6277
				if ($hourend < $hourstart) {
6278
					$hourend = $hourstart;
6279
				}
6280
			}
6281
			// Show hour
6282
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'hour' : '').'" id="'.$prefix.'hour" name="'.$prefix.'hour">';
6283
			if ($emptyhours) {
6284
				$retstring .= '<option value="-1">&nbsp;</option>';
6285
			}
6286
			for ($hour = $hourstart; $hour < $hourend; $hour++) {
6287
				if (strlen($hour) < 2) {
6288
					$hour = "0".$hour;
6289
				}
6290
				$retstring .= '<option value="'.$hour.'"'.(($hour == $shour) ? ' selected' : '').'>'.$hour;
6291
				//$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
6292
				$retstring .= '</option>';
6293
			}
6294
			$retstring .= '</select>';
6295
			//if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
6296
			if ($m) {
6297
				$retstring .= ":";
6298
			}
6299
		}
6300
6301
		if ($m) {
6302
			// Show minutes
6303
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'min' : '').'" id="'.$prefix.'min" name="'.$prefix.'min">';
6304
			if ($emptyhours) {
6305
				$retstring .= '<option value="-1">&nbsp;</option>';
6306
			}
6307
			for ($min = 0; $min < 60; $min += $stepminutes) {
6308
				if (strlen($min) < 2) {
6309
					$min = "0".$min;
6310
				}
6311
				$retstring .= '<option value="'.$min.'"'.(($min == $smin) ? ' selected' : '').'>'.$min.(empty($conf->dol_optimize_smallscreen) ? '' : '').'</option>';
6312
			}
6313
			$retstring .= '</select>';
6314
6315
			$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...
6316
		}
6317
6318
		if ($d && $h) {
6319
			$retstring .= '</span>';
6320
		}
6321
6322
		// Add a "Now" link
6323
		if ($conf->use_javascript_ajax && $addnowlink) {
6324
			// Script which will be inserted in the onClick of the "Now" link
6325
			$reset_scripts = "";
6326
			if ($addnowlink == 2) { // local computer time
6327
				// pad add leading 0 on numbers
6328
				$reset_scripts .= "Number.prototype.pad = function(size) {
6329
                        var s = String(this);
6330
                        while (s.length < (size || 2)) {s = '0' + s;}
6331
                        return s;
6332
                    };
6333
                    var d = new Date();";
6334
			}
6335
6336
			// Generate the date part, depending on the use or not of the javascript calendar
6337
			if ($addnowlink == 1) { // server time expressed in user time setup
6338
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6339
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6340
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6341
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6342
			} elseif ($addnowlink == 2) {
6343
				/* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
6344
				 * This break application for foreign languages.
6345
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
6346
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
6347
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
6348
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
6349
				*/
6350
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6351
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6352
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6353
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6354
			}
6355
			/*if ($usecalendar == "eldy")
6356
			{
6357
				$base=DOL_URL_ROOT.'/core/';
6358
				$reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
6359
			}
6360
			else
6361
			{
6362
				$reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
6363
				$reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
6364
				$reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
6365
			}*/
6366
			// Update the hour part
6367
			if ($h) {
6368
				if ($fullday) {
6369
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6370
				}
6371
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
6372
				if ($addnowlink == 1) {
6373
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6374
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6375
				} elseif ($addnowlink == 2) {
6376
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());';
6377
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6378
				}
6379
6380
				if ($fullday) {
6381
					$reset_scripts .= ' } ';
6382
				}
6383
			}
6384
			// Update the minute part
6385
			if ($m) {
6386
				if ($fullday) {
6387
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6388
				}
6389
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
6390
				if ($addnowlink == 1) {
6391
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6392
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6393
				} elseif ($addnowlink == 2) {
6394
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());';
6395
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6396
				}
6397
				if ($fullday) {
6398
					$reset_scripts .= ' } ';
6399
				}
6400
			}
6401
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6402
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6403
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonNow" type="button" name="_useless" value="now" onClick="'.$reset_scripts.'">';
6404
				$retstring .= $langs->trans("Now");
6405
				$retstring .= '</button> ';
6406
			}
6407
		}
6408
6409
		// Add a "Plus one hour" link
6410
		if ($conf->use_javascript_ajax && $addplusone) {
6411
			// Script which will be inserted in the onClick of the "Add plusone" link
6412
			$reset_scripts = "";
6413
6414
			// Generate the date part, depending on the use or not of the javascript calendar
6415
			$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel').'\');';
6416
			$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6417
			$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6418
			$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6419
			// Update the hour part
6420
			if ($h) {
6421
				if ($fullday) {
6422
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6423
				}
6424
				$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6425
				if ($fullday) {
6426
					$reset_scripts .= ' } ';
6427
				}
6428
			}
6429
			// Update the minute part
6430
			if ($m) {
6431
				if ($fullday) {
6432
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6433
				}
6434
				$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6435
				if ($fullday) {
6436
					$reset_scripts .= ' } ';
6437
				}
6438
			}
6439
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6440
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6441
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="'.$reset_scripts.'">';
6442
				$retstring .= $langs->trans("DateStartPlusOne");
6443
				$retstring .= '</button> ';
6444
			}
6445
		}
6446
6447
		// Add a link to set data
6448
		if ($conf->use_javascript_ajax && $adddateof) {
6449
			$tmparray = dol_getdate($adddateof);
6450
			if (empty($labeladddateof)) {
6451
				$labeladddateof = $langs->trans("DateInvoice");
6452
			}
6453
			$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>';
6454
		}
6455
6456
		return $retstring;
6457
	}
6458
6459
	/**
6460
	 * selectTypeDuration
6461
	 *
6462
	 * @param   string   	$prefix     	Prefix
6463
	 * @param   string   	$selected   	Selected duration type
6464
	 * @param	array		$excludetypes	Array of duration types to exclude. Example array('y', 'm')
6465
	 * @return  string      	         	HTML select string
6466
	 */
6467
	public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
6468
	{
6469
		global $langs;
6470
6471
		$TDurationTypes = array(
6472
			'y'=>$langs->trans('Years'),
6473
			'm'=>$langs->trans('Month'),
6474
			'w'=>$langs->trans('Weeks'),
6475
			'd'=>$langs->trans('Days'),
6476
			'h'=>$langs->trans('Hours'),
6477
			'i'=>$langs->trans('Minutes')
6478
		);
6479
6480
		// Removed undesired duration types
6481
		foreach ($excludetypes as $value) {
6482
			unset($TDurationTypes[$value]);
6483
		}
6484
6485
		$retstring = '<select class="flat" id="select_'.$prefix.'type_duration" name="'.$prefix.'type_duration">';
6486
		foreach ($TDurationTypes as $key => $typeduration) {
6487
			$retstring .= '<option value="'.$key.'"';
6488
			if ($key == $selected) {
6489
				$retstring .= " selected";
6490
			}
6491
			$retstring .= ">".$typeduration."</option>";
6492
		}
6493
		$retstring .= "</select>";
6494
6495
		$retstring .= ajax_combobox('select_'.$prefix.'type_duration');
6496
6497
		return $retstring;
6498
	}
6499
6500
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6501
	/**
6502
	 *  Function to show a form to select a duration on a page
6503
	 *
6504
	 *	@param	string		$prefix   		Prefix for input fields
6505
	 *	@param  int			$iSecond  		Default preselected duration (number of seconds or '')
6506
	 * 	@param	int			$disabled       Disable the combo box
6507
	 * 	@param	string		$typehour		If 'select' then input hour and input min is a combo,
6508
	 *						            	If 'text' input hour is in text and input min is a text,
6509
	 *						            	If 'textselect' input hour is in text and input min is a combo
6510
	 *  @param	integer		$minunderhours	If 1, show minutes selection under the hours
6511
	 * 	@param	int			$nooutput		Do not output html string but return it
6512
	 *  @return	string|void
6513
	 */
6514
	public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
6515
	{
6516
		// phpcs:enable
6517
		global $langs;
6518
6519
		$retstring = '';
6520
6521
		$hourSelected = 0;
6522
		$minSelected = 0;
6523
6524
		// Hours
6525
		if ($iSecond != '') {
6526
			require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6527
6528
			$hourSelected = convertSecondToTime($iSecond, 'allhour');
6529
			$minSelected = convertSecondToTime($iSecond, 'min');
6530
		}
6531
6532
		if ($typehour == 'select') {
6533
			$retstring .= '<select class="flat" id="select_'.$prefix.'hour" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').'>';
6534
			for ($hour = 0; $hour < 25; $hour++) {	// For a duration, we allow 24 hours
6535
				$retstring .= '<option value="'.$hour.'"';
6536
				if ($hourSelected == $hour) {
6537
					$retstring .= " selected";
6538
				}
6539
				$retstring .= ">".$hour."</option>";
6540
			}
6541
			$retstring .= "</select>";
6542
		} elseif ($typehour == 'text' || $typehour == 'textselect') {
6543
			$retstring .= '<input placeholder="'.$langs->trans('HourShort').'" type="number" min="0" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputhour" value="'.(($hourSelected != '') ? ((int) $hourSelected) : '').'">';
6544
		} else {
6545
			return 'BadValueForParameterTypeHour';
6546
		}
6547
6548
		if ($typehour != 'text') {
6549
			$retstring .= ' '.$langs->trans('HourShort');
6550
		} else {
6551
			$retstring .= '<span class="hideonsmartphone">:</span>';
6552
		}
6553
6554
		// Minutes
6555
		if ($minunderhours) {
6556
			$retstring .= '<br>';
6557
		} else {
6558
			$retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
6559
		}
6560
6561
		if ($typehour == 'select' || $typehour == 'textselect') {
6562
			$retstring .= '<select class="flat" id="select_'.$prefix.'min" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').'>';
6563
			for ($min = 0; $min <= 55; $min = $min + 5) {
6564
				$retstring .= '<option value="'.$min.'"';
6565
				if ($minSelected == $min) {
6566
					$retstring .= ' selected';
6567
				}
6568
				$retstring .= '>'.$min.'</option>';
6569
			}
6570
			$retstring .= "</select>";
6571
		} elseif ($typehour == 'text') {
6572
			$retstring .= '<input placeholder="'.$langs->trans('MinuteShort').'" type="number" min="0" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputminute" value="'.(($minSelected != '') ? ((int) $minSelected) : '').'">';
6573
		}
6574
6575
		if ($typehour != 'text') {
6576
			$retstring .= ' '.$langs->trans('MinuteShort');
6577
		}
6578
6579
		//$retstring.="&nbsp;";
6580
6581
		if (!empty($nooutput)) {
6582
			return $retstring;
6583
		}
6584
6585
		print $retstring;
6586
		return;
6587
	}
6588
6589
	/**
6590
	 *  Return list of tickets in Ajax if Ajax activated or go to selectTicketsList
6591
	 *
6592
	 *  @param		int			$selected				Preselected tickets
6593
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
6594
	 *  @param  	string		$filtertype     		To add a filter
6595
	 *  @param		int			$limit					Limit on number of returned lines
6596
	 *  @param		int			$status					Ticket status
6597
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
6598
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
6599
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
6600
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
6601
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6602
	 * 	@param		int			$forcecombo				Force to use combo box
6603
	 *  @param      string      $morecss                Add more css on select
6604
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
6605
	 *  @param		string		$nooutput				No print, return the output into a string
6606
	 *  @return		void|string
6607
	 */
6608
	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)
6609
	{
6610
		global $langs, $conf;
6611
6612
		$out = '';
6613
6614
		// check parameters
6615
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
6616
6617
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6618
			$placeholder = '';
6619
6620
			if ($selected && empty($selected_input_value)) {
6621
				require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6622
				$tickettmpselect = new Ticket($this->db);
6623
				$tickettmpselect->fetch($selected);
6624
				$selected_input_value = $tickettmpselect->ref;
6625
				unset($tickettmpselect);
6626
			}
6627
6628
			$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...
6629
6630
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
6631
			elseif ($hidelabel > 1) {
6632
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
6633
				if ($hidelabel == 2) {
6634
					$out .= img_picto($langs->trans("Search"), 'search');
6635
				}
6636
			}
6637
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
6638
			if ($hidelabel == 3) {
6639
				$out .= img_picto($langs->trans("Search"), 'search');
6640
			}
6641
		} else {
6642
			$out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
6643
		}
6644
6645
		if (empty($nooutput)) print $out;
6646
		else return $out;
6647
	}
6648
6649
6650
	/**
6651
	 *	Return list of tickets.
6652
	 *  Called by selectTickets.
6653
	 *
6654
	 *	@param      int		$selected           Preselected ticket
6655
	 *	@param      string	$htmlname           Name of select html
6656
	 *  @param		string	$filtertype         Filter on ticket type
6657
	 *	@param      int		$limit              Limit on number of returned lines
6658
	 * 	@param      string	$filterkey          Filter on product
6659
	 *	@param		int		$status             Ticket status
6660
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
6661
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6662
	 * 	@param		int		$forcecombo		    Force to use combo box
6663
	 *  @param      string  $morecss            Add more css on select
6664
	 *  @return     array    				    Array of keys for json
6665
	 */
6666
	public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
6667
	{
6668
		global $langs, $conf, $user, $db;
6669
6670
		$out = '';
6671
		$outarray = array();
6672
6673
		$selectFields = " p.rowid, p.ref, p.message";
6674
6675
		$sql = "SELECT ";
6676
		$sql .= $selectFields;
6677
		$sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
6678
		$sql .= ' WHERE p.entity IN ('.getEntity('ticket').')';
6679
6680
		// Add criteria on ref/label
6681
		if ($filterkey != '') {
6682
			$sql .= ' AND (';
6683
			$prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
6684
			// For natural search
6685
			$scrit = explode(' ', $filterkey);
6686
			$i = 0;
6687
			if (count($scrit) > 1) $sql .= "(";
6688
			foreach ($scrit as $crit) {
6689
				if ($i > 0) $sql .= " AND ";
6690
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
6691
				$sql .= ")";
6692
				$i++;
6693
			}
6694
			if (count($scrit) > 1) $sql .= ")";
6695
			$sql .= ')';
6696
		}
6697
6698
		$sql .= $this->db->plimit($limit, 0);
6699
6700
		// Build output string
6701
		dol_syslog(get_class($this)."::selectTicketsList search tickets", LOG_DEBUG);
6702
		$result = $this->db->query($sql);
6703
		if ($result) {
6704
			require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6705
			require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
6706
6707
			$num = $this->db->num_rows($result);
6708
6709
			$events = null;
6710
6711
			if (!$forcecombo) {
6712
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6713
				$out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT);
6714
			}
6715
6716
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
6717
6718
			$textifempty = '';
6719
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
6720
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
6721
			if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6722
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6723
				else $textifempty .= $langs->trans("All");
6724
			} else {
6725
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6726
			}
6727
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
6728
6729
			$i = 0;
6730
			while ($num && $i < $num) {
6731
				$opt = '';
6732
				$optJson = array();
6733
				$objp = $this->db->fetch_object($result);
6734
6735
				$this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
6736
				// Add new entry
6737
				// "key" value of json key array is used by jQuery automatically as selected value
6738
				// "label" value of json key array is used by jQuery automatically as text for combo box
6739
				$out .= $opt;
6740
				array_push($outarray, $optJson);
6741
6742
				$i++;
6743
			}
6744
6745
			$out .= '</select>';
6746
6747
			$this->db->free($result);
6748
6749
			if (empty($outputmode)) return $out;
6750
			return $outarray;
6751
		} else {
6752
			dol_print_error($db);
6753
		}
6754
	}
6755
6756
	/**
6757
	 * constructTicketListOption.
6758
	 * This define value for &$opt and &$optJson.
6759
	 *
6760
	 * @param 	resource	$objp			    Result set of fetch
6761
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
6762
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
6763
	 * @param 	string		$selected		    Preselected value
6764
	 * @param   string      $filterkey          Filter key to highlight
6765
	 * @return	void
6766
	 */
6767
	protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
6768
	{
6769
		global $langs, $conf, $user, $db;
6770
6771
		$outkey = '';
6772
		$outval = '';
6773
		$outref = '';
6774
		$outlabel = '';
6775
		$outtype = '';
6776
6777
		$label = $objp->label;
6778
6779
		$outkey = $objp->rowid;
6780
		$outref = $objp->ref;
6781
		$outlabel = $objp->label;
6782
		$outtype = $objp->fk_product_type;
6783
6784
		$opt = '<option value="'.$objp->rowid.'"';
6785
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
6786
		$opt .= '>';
6787
		$opt .= $objp->ref;
6788
		$objRef = $objp->ref;
6789
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
6790
		$outval .= $objRef;
6791
6792
		$opt .= "</option>\n";
6793
		$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...
6794
	}
6795
6796
6797
	/**
6798
	 * Generic method to select a component from a combo list.
6799
	 * Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.
6800
	 * This is the generic method that will replace all specific existing methods.
6801
	 *
6802
	 * @param 	string			$objectdesc			ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]
6803
	 * @param	string			$htmlname			Name of HTML select component
6804
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6805
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6806
	 * @param	string			$searchkey			Search criteria
6807
	 * @param	string			$placeholder		Place holder
6808
	 * @param	string			$morecss			More CSS
6809
	 * @param	string			$moreparams			More params provided to ajax call
6810
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6811
	 * @param	int				$disabled			1=Html component is disabled
6812
	 * @param	string	        $selected_input_value	Value of preselected input text (for use with ajax)
6813
	 * @return	string								Return HTML string
6814
	 * @see selectForFormsList() select_thirdparty_list()
6815
	 */
6816
	public function selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '')
6817
	{
6818
		global $conf, $user;
6819
6820
		$objecttmp = null;
6821
6822
		$InfoFieldList = explode(":", $objectdesc);
6823
		$classname = $InfoFieldList[0];
6824
		$classpath = $InfoFieldList[1];
6825
		$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
6826
		$filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
6827
6828
		if (!empty($classpath)) {
6829
			dol_include_once($classpath);
6830
6831
			if ($classname && class_exists($classname)) {
6832
				$objecttmp = new $classname($this->db);
6833
				// Make some replacement
6834
				$sharedentities = getEntity(strtolower($classname));
6835
				$objecttmp->filter = str_replace(
6836
					array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
6837
					array($conf->entity, $sharedentities, $user->id),
6838
					$filter
6839
				);
6840
			}
6841
		}
6842
		if (!is_object($objecttmp)) {
6843
			dol_syslog('Error bad setup of type for field '.$InfoFieldList, LOG_WARNING);
6844
			return 'Error bad setup of type for field '.join(',', $InfoFieldList);
6845
		}
6846
6847
		//var_dump($objecttmp->filter);
6848
		$prefixforautocompletemode = $objecttmp->element;
6849
		if ($prefixforautocompletemode == 'societe') {
6850
			$prefixforautocompletemode = 'company';
6851
		}
6852
		if ($prefixforautocompletemode == 'product') {
6853
			$prefixforautocompletemode = 'produit';
6854
		}
6855
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6856
6857
		dol_syslog(get_class($this)."::selectForForms object->filter=".$objecttmp->filter, LOG_DEBUG);
6858
		$out = '';
6859
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->$confkeyforautocompletemode) && !$forcecombo) {
6860
			// No immediate load of all database
6861
			$placeholder = '';
6862
			if ($preselectedvalue && empty($selected_input_value)) {
6863
				$objecttmp->fetch($preselectedvalue);
6864
				$selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
6865
				//unset($objecttmp);
6866
			}
6867
6868
			$objectdesc = $classname.':'.$classpath.':'.$addcreatebuttonornot.':'.$filter;
6869
			$urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php';
6870
6871
			// No immediate load of all database
6872
			$urloption = 'htmlname='.$htmlname.'&outjson=1&objectdesc='.$objectdesc.'&filter='.urlencode($objecttmp->filter);
6873
			// Activate the auto complete using ajax call.
6874
			$out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
6875
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
6876
			$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).'"' : '') .' />';
6877
		} else {
6878
			// Immediate load of table record. Note: filter is inside $objecttmp->filter
6879
			$out .= $this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled);
6880
		}
6881
6882
		return $out;
6883
	}
6884
6885
	/**
6886
	 * Function to forge a SQL criteria
6887
	 *
6888
	 * @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"
6889
	 * @return string                  Forged criteria. Example: "t.field like 'abc%'"
6890
	 */
6891
	protected static function forgeCriteriaCallback($matches)
6892
	{
6893
		global $db;
6894
6895
		//dol_syslog("Convert matches ".$matches[1]);
6896
		if (empty($matches[1])) {
6897
			return '';
6898
		}
6899
		$tmp = explode(':', $matches[1]);
6900
		if (count($tmp) < 3) {
6901
			return '';
6902
		}
6903
6904
		$tmpescaped = $tmp[2];
6905
		$regbis = array();
6906
		if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
6907
			$tmpescaped = "'".$db->escape($regbis[1])."'";
6908
		} else {
6909
			$tmpescaped = $db->escape($tmpescaped);
6910
		}
6911
		return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped;
6912
	}
6913
6914
	/**
6915
	 * Output html form to select an object.
6916
	 * Note, this function is called by selectForForms or by ajax selectobject.php
6917
	 *
6918
	 * @param 	Object			$objecttmp			Object to knwo the table to scan for combo.
6919
	 * @param	string			$htmlname			Name of HTML select component
6920
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6921
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6922
	 * @param	string			$searchkey			Search value
6923
	 * @param	string			$placeholder		Place holder
6924
	 * @param	string			$morecss			More CSS
6925
	 * @param	string			$moreparams			More params provided to ajax call
6926
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6927
	 * @param	int				$outputmode			0=HTML select string, 1=Array
6928
	 * @param	int				$disabled			1=Html component is disabled
6929
	 * @return	string|array						Return HTML string
6930
	 * @see selectForForms()
6931
	 */
6932
	public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0)
6933
	{
6934
		global $conf, $langs, $user, $hookmanager;
6935
6936
		//print "$objecttmp->filter, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled";
6937
6938
		$prefixforautocompletemode = $objecttmp->element;
6939
		if ($prefixforautocompletemode == 'societe') {
6940
			$prefixforautocompletemode = 'company';
6941
		}
6942
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6943
6944
		if (!empty($objecttmp->fields)) {	// For object that declare it, it is better to use declared fields (like societe, contact, ...)
6945
			$tmpfieldstoshow = '';
6946
			foreach ($objecttmp->fields as $key => $val) {
6947
				if (!dol_eval($val['enabled'], 1, 1)) {
6948
					continue;
6949
				}
6950
				if (!empty($val['showoncombobox'])) {
6951
					$tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '').'t.'.$key;
6952
				}
6953
			}
6954
			if ($tmpfieldstoshow) {
6955
				$fieldstoshow = $tmpfieldstoshow;
6956
			}
6957
		} else {
6958
			// For backward compatibility
6959
			$objecttmp->fields['ref'] = array('type'=>'varchar(30)', 'label'=>'Ref', 'showoncombobox'=>1);
6960
		}
6961
6962
		if (empty($fieldstoshow)) {
6963
			if (isset($objecttmp->fields['ref'])) {
6964
				$fieldstoshow = 't.ref';
6965
			} else {
6966
				$langs->load("errors");
6967
				$this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
6968
				return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
6969
			}
6970
		}
6971
6972
		$out = '';
6973
		$outarray = array();
6974
6975
		$num = 0;
6976
6977
		// Search data
6978
		$sql = "SELECT t.rowid, ".$fieldstoshow." FROM ".MAIN_DB_PREFIX.$objecttmp->table_element." as t";
6979
		if (isset($objecttmp->ismultientitymanaged)) {
6980
			if (!is_numeric($objecttmp->ismultientitymanaged)) {
6981
				$tmparray = explode('@', $objecttmp->ismultientitymanaged);
6982
				$sql .= ' INNER JOIN '.MAIN_DB_PREFIX.$tmparray[1].' as parenttable ON parenttable.rowid = t.'.$tmparray[0];
6983
			}
6984
			if ($objecttmp->ismultientitymanaged == 'fk_soc@societe') {
6985
				if (!$user->rights->societe->client->voir && !$user->socid) {
6986
					$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
6987
				}
6988
			}
6989
		}
6990
6991
		// Add where from hooks
6992
		$parameters = array();
6993
		$reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
6994
		if (!empty($hookmanager->resPrint)) {
6995
			$sql .= $hookmanager->resPrint;
6996
		} else {
6997
			$sql .= " WHERE 1=1";
6998
			if (isset($objecttmp->ismultientitymanaged)) {
6999
				if ($objecttmp->ismultientitymanaged == 1) {
7000
					$sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
7001
				}
7002
				if (!is_numeric($objecttmp->ismultientitymanaged)) {
7003
					$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...
7004
				}
7005
				if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
7006
					if ($objecttmp->element == 'societe') {
7007
						$sql .= " AND t.rowid = ".$user->socid;
7008
					} else {
7009
						$sql .= " AND t.fk_soc = ".$user->socid;
7010
					}
7011
				}
7012
				if ($objecttmp->ismultientitymanaged == 'fk_soc@societe') {
7013
					if (!$user->rights->societe->client->voir && !$user->socid) {
7014
						$sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = ".$user->id;
7015
					}
7016
				}
7017
			}
7018
			if ($searchkey != '') {
7019
				$sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
7020
			}
7021
			if ($objecttmp->filter) {	 // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
7022
				/*if (! DolibarrApi::_checkFilters($objecttmp->filter))
7023
				{
7024
					throw new RestException(503, 'Error when validating parameter sqlfilters '.$objecttmp->filter);
7025
				}*/
7026
				$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
7027
				$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'Form::forgeCriteriaCallback', $objecttmp->filter).")";
7028
			}
7029
		}
7030
		$sql .= $this->db->order($fieldstoshow, "ASC");
7031
		//$sql.=$this->db->plimit($limit, 0);
7032
		//print $sql;
7033
7034
		// Build output string
7035
		$resql = $this->db->query($sql);
7036
		if ($resql) {
7037
			// Construct $out and $outarray
7038
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').($moreparams ? ' '.$moreparams : '').' name="'.$htmlname.'">'."\n";
7039
7040
			// 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
7041
			$textifempty = '&nbsp;';
7042
7043
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7044
			if (!empty($conf->global->$confkeyforautocompletemode)) {
7045
				if ($showempty && !is_numeric($showempty)) {
7046
					$textifempty = $langs->trans($showempty);
7047
				} else {
7048
					$textifempty .= $langs->trans("All");
7049
				}
7050
			}
7051
			if ($showempty) {
7052
				$out .= '<option value="-1">'.$textifempty.'</option>'."\n";
7053
			}
7054
7055
			$num = $this->db->num_rows($resql);
7056
			$i = 0;
7057
			if ($num) {
7058
				while ($i < $num) {
7059
					$obj = $this->db->fetch_object($resql);
7060
					$label = '';
7061
					$tmparray = explode(',', $fieldstoshow);
7062
					$oldvalueforshowoncombobox = 0;
7063
					foreach ($tmparray as $key => $val) {
7064
						$val = preg_replace('/t\./', '', $val);
7065
						$label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
7066
						$label .= $obj->$val;
7067
						$oldvalueforshowoncombobox = $objecttmp->fields[$val]['showoncombobox'];
7068
					}
7069
					if (empty($outputmode)) {
7070
						if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
7071
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
7072
						} else {
7073
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
7074
						}
7075
					} else {
7076
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
7077
					}
7078
7079
					$i++;
7080
					if (($i % 10) == 0) {
7081
						$out .= "\n";
7082
					}
7083
				}
7084
			}
7085
7086
			$out .= '</select>'."\n";
7087
7088
			if (!$forcecombo) {
7089
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7090
				$out .= ajax_combobox($htmlname, null, (!empty($conf->global->$confkeyforautocompletemode) ? $conf->global->$confkeyforautocompletemode : 0));
7091
			}
7092
		} else {
7093
			dol_print_error($this->db);
7094
		}
7095
7096
		$this->result = array('nbofelement'=>$num);
7097
7098
		if ($outputmode) {
7099
			return $outarray;
7100
		}
7101
		return $out;
7102
	}
7103
7104
7105
	/**
7106
	 *	Return a HTML select string, built from an array of key+value.
7107
	 *  Note: Do not apply langs->trans function on returned content, content may be entity encoded twice.
7108
	 *
7109
	 *	@param	string			$htmlname			Name of html select area. Must start with "multi" if this is a multiselect
7110
	 *	@param	array			$array				Array like array(key => value) or array(key=>array('label'=>..., 'data-...'=>..., 'disabled'=>..., 'css'=>...))
7111
	 *	@param	string|string[]	$id					Preselected key or preselected keys for multiselect
7112
	 *	@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.
7113
	 *	@param	int				$key_in_label		1 to show key into label with format "[key] value"
7114
	 *	@param	int				$value_as_key		1 to use value as key
7115
	 *	@param  string			$moreparam			Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
7116
	 *	@param  int				$translate			1=Translate and encode value
7117
	 * 	@param	int				$maxlen				Length maximum for labels
7118
	 * 	@param	int				$disabled			Html select box is disabled
7119
	 *  @param	string			$sort				'ASC' or 'DESC' = Sort on label, '' or 'NONE' or 'POS' = Do not sort, we keep original order
7120
	 *  @param	string			$morecss			Add more class to css styles
7121
	 *  @param	int				$addjscombo			Add js combo
7122
	 *  @param  string          $moreparamonempty	Add more param on the empty option line. Not used if show_empty not set
7123
	 *  @param  int             $disablebademail	1=Check if a not valid email, 2=Check string '---', and if found into value, disable and colorize entry
7124
	 *  @param  int             $nohtmlescape		No html escaping.
7125
	 * 	@return	string								HTML select string.
7126
	 *  @see multiselectarray(), selectArrayAjax(), selectArrayFilter()
7127
	 */
7128
	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)
7129
	{
7130
		global $conf, $langs;
7131
7132
		// Do we want a multiselect ?
7133
		//$jsbeautify = 0;
7134
		//if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
7135
		$jsbeautify = 1;
7136
7137
		if ($value_as_key) {
7138
			$array = array_combine($array, $array);
7139
		}
7140
7141
		$out = '';
7142
7143
		if ($addjscombo < 0) {
7144
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7145
				$addjscombo = 1;
7146
			} else {
7147
				$addjscombo = 0;
7148
			}
7149
		}
7150
7151
		// Add code for jquery to use multiselect
7152
		if ($addjscombo && $jsbeautify) {
7153
			// Enhance with select2
7154
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7155
			$out .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', $show_empty < 0 ? (string) $show_empty : '-1');
7156
		}
7157
7158
		$out .= '<select id="'.preg_replace('/^\./', '', $htmlname).'" '.($disabled ? 'disabled="disabled" ' : '').'class="flat '.(preg_replace('/^\./', '', $htmlname)).($morecss ? ' '.$morecss : '').'"';
7159
		$out .= ' name="'.preg_replace('/^\./', '', $htmlname).'" '.($moreparam ? $moreparam : '');
7160
		$out .= '>';
7161
7162
		if ($show_empty) {
7163
			$textforempty = ' ';
7164
			if (!empty($conf->use_javascript_ajax)) {
7165
				$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
7166
			}
7167
			if (!is_numeric($show_empty)) {
7168
				$textforempty = $show_empty;
7169
			}
7170
			$out .= '<option class="optiongrey" '.($moreparamonempty ? $moreparamonempty.' ' : '').'value="'.($show_empty < 0 ? $show_empty : -1).'"'.($id == $show_empty ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
7171
		}
7172
7173
		if (is_array($array)) {
7174
			// Translate
7175
			if ($translate) {
7176
				foreach ($array as $key => $value) {
7177
					if (!is_array($value)) {
7178
						$array[$key] = $langs->trans($value);
7179
					} else {
7180
						$array[$key]['label'] = $langs->trans($value['label']);
7181
					}
7182
				}
7183
			}
7184
7185
			// Sort
7186
			if ($sort == 'ASC') {
7187
				asort($array);
7188
			} elseif ($sort == 'DESC') {
7189
				arsort($array);
7190
			}
7191
7192
			foreach ($array as $key => $tmpvalue) {
7193
				if (is_array($tmpvalue)) {
7194
					$value = $tmpvalue['label'];
7195
					$disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
7196
					$style = empty($tmpvalue['css']) ? ' class="'.$tmpvalue['css'].'"' : '';
7197
				} else {
7198
					$value = $tmpvalue;
7199
					$disabled = '';
7200
					$style = '';
7201
				}
7202
				if (!empty($disablebademail)) {
7203
					if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
7204
						|| ($disablebademail == 2 && preg_match('/---/', $value))) {
7205
						$disabled = ' disabled';
7206
						$style = ' class="warning"';
7207
					}
7208
				}
7209
7210
				if ($key_in_label) {
7211
					if (empty($nohtmlescape)) {
7212
						$selectOptionValue = dol_escape_htmltag($key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value));
7213
					} else {
7214
						$selectOptionValue = $key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value);
7215
					}
7216
				} else {
7217
					if (empty($nohtmlescape)) {
7218
						$selectOptionValue = dol_escape_htmltag($maxlen ?dol_trunc($value, $maxlen) : $value);
7219
					} else {
7220
						$selectOptionValue = $maxlen ?dol_trunc($value, $maxlen) : $value;
7221
					}
7222
					if ($value == '' || $value == '-') {
7223
						$selectOptionValue = '&nbsp;';
7224
					}
7225
				}
7226
7227
				$out .= '<option value="'.$key.'"';
7228
				$out .= $style.$disabled;
7229
				if (is_array($id)) {
7230
					if (in_array($key, $id) && !$disabled) {
7231
						$out .= ' selected'; // To preselect a value
7232
					}
7233
				} else {
7234
					$id = (string) $id; // if $id = 0, then $id = '0'
7235
					if ($id != '' && $id == $key && !$disabled) {
7236
						$out .= ' selected'; // To preselect a value
7237
					}
7238
				}
7239
				if ($nohtmlescape) {
7240
					$out .= ' data-html="'.dol_escape_htmltag($selectOptionValue).'"';
7241
				}
7242
				if (is_array($tmpvalue)) {
7243
					foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
7244
						if (preg_match('/^data-/', $keyforvalue)) {
7245
							$out .= ' '.$keyforvalue.'="'.$valueforvalue.'"';
7246
						}
7247
					}
7248
				}
7249
				$out .= '>';
7250
				//var_dump($selectOptionValue);
7251
				$out .= $selectOptionValue;
7252
				$out .= "</option>\n";
7253
			}
7254
		}
7255
7256
		$out .= "</select>";
7257
		return $out;
7258
	}
7259
7260
7261
	/**
7262
	 *	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.
7263
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7264
	 *
7265
	 *	@param	string	$htmlname       		Name of html select area
7266
	 *	@param	string	$url					Url. Must return a json_encode of array(key=>array('text'=>'A text', 'url'=>'An url'), ...)
7267
	 *	@param	string	$id             		Preselected key
7268
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7269
	 *	@param  string	$moreparamtourl 		Add more parameters onto the Ajax called URL
7270
	 * 	@param	int		$disabled				Html select box is disabled
7271
	 *  @param	int		$minimumInputLength		Minimum Input Length
7272
	 *  @param	string	$morecss				Add more class to css styles
7273
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7274
	 *  @param  string  $placeholder            String to use as placeholder
7275
	 *  @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)
7276
	 * 	@return	string   						HTML select string
7277
	 *  @see selectArrayFilter(), ajax_combobox() in ajax.lib.php
7278
	 */
7279
	public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7280
	{
7281
		global $conf, $langs;
7282
		global $delayedhtmlcontent;	// Will be used later outside of this function
7283
7284
		// TODO Use an internal dolibarr component instead of select2
7285
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7286
			return '';
7287
		}
7288
7289
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"></select>';
7290
7291
		$outdelayed = '';
7292
		if (!empty($conf->use_javascript_ajax)) {
7293
			$tmpplugin = 'select2';
7294
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7295
		    	<script>
7296
		    	$(document).ready(function () {
7297
7298
	    	        '.($callurlonselect ? 'var saveRemoteData = [];' : '').'
7299
7300
	                $(".'.$htmlname.'").select2({
7301
				    	ajax: {
7302
					    	dir: "ltr",
7303
					    	url: "'.$url.'",
7304
					    	dataType: \'json\',
7305
					    	delay: 250,
7306
					    	data: function (params) {
7307
					    		return {
7308
							    	q: params.term, 	// search term
7309
					    			page: params.page
7310
					    		};
7311
				    		},
7312
				    		processResults: function (data) {
7313
				    			// parse the results into the format expected by Select2.
7314
				    			// since we are using custom formatting functions we do not need to alter the remote JSON data
7315
				    			//console.log(data);
7316
								saveRemoteData = data;
7317
					    	    /* format json result for select2 */
7318
					    	    result = []
7319
					    	    $.each( data, function( key, value ) {
7320
					    	       result.push({id: key, text: value.text});
7321
	                            });
7322
				    			//return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
7323
				    			//console.log(result);
7324
				    			return {results: result, more: false}
7325
				    		},
7326
				    		cache: true
7327
				    	},
7328
		 				language: select2arrayoflanguage,
7329
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7330
					    placeholder: "'.dol_escape_js($placeholder).'",
7331
				    	escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7332
				    	minimumInputLength: '.$minimumInputLength.',
7333
				        formatResult: function(result, container, query, escapeMarkup) {
7334
	                        return escapeMarkup(result.text);
7335
	                    },
7336
				    });
7337
7338
	                '.($callurlonselect ? '
7339
	                /* Code to execute a GET when we select a value */
7340
	                $(".'.$htmlname.'").change(function() {
7341
				    	var selected = $(".'.$htmlname.'").val();
7342
	                	console.log("We select in selectArrayAjax the entry "+selected)
7343
				        $(".'.$htmlname.'").val("");  /* reset visible combo value */
7344
	    			    $.each( saveRemoteData, function( key, value ) {
7345
	    				        if (key == selected)
7346
	    			            {
7347
	    			                 console.log("selectArrayAjax - Do a redirect to "+value.url)
7348
	    			                 location.assign(value.url);
7349
	    			            }
7350
	                    });
7351
	    			});' : '').'
7352
7353
	    	   });
7354
		       </script>';
7355
		}
7356
7357
		if ($acceptdelayedhtml) {
7358
			$delayedhtmlcontent .= $outdelayed;
7359
		} else {
7360
			$out .= $outdelayed;
7361
		}
7362
		return $out;
7363
	}
7364
7365
	/**
7366
	 *  Return a HTML select string, built from an array of key+value, but content returned into select is defined into $array parameter.
7367
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7368
	 *
7369
	 *  @param  string	$htmlname               Name of html select area
7370
	 *	@param	array	$array					Array (key=>array('text'=>'A text', 'url'=>'An url'), ...)
7371
	 *	@param	string	$id             		Preselected key
7372
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7373
	 *	@param	int		$disableFiltering		If set to 1, results are not filtered with searched string
7374
	 * 	@param	int		$disabled				Html select box is disabled
7375
	 *  @param	int		$minimumInputLength		Minimum Input Length
7376
	 *  @param	string	$morecss				Add more class to css styles
7377
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7378
	 *  @param  string  $placeholder            String to use as placeholder
7379
	 *  @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)
7380
	 *  @return	string   						HTML select string
7381
	 *  @see selectArrayAjax(), ajax_combobox() in ajax.lib.php
7382
	 */
7383
	public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7384
	{
7385
		global $conf, $langs;
7386
		global $delayedhtmlcontent;	// Will be used later outside of this function
7387
7388
		// TODO Use an internal dolibarr component instead of select2
7389
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7390
			return '';
7391
		}
7392
7393
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"><option></option></select>';
7394
7395
		$formattedarrayresult = array();
7396
7397
		foreach ($array as $key => $value) {
7398
			$o = new stdClass();
7399
			$o->id = $key;
7400
			$o->text = $value['text'];
7401
			$o->url = $value['url'];
7402
			$formattedarrayresult[] = $o;
7403
		}
7404
7405
		$outdelayed = '';
7406
		if (!empty($conf->use_javascript_ajax)) {
7407
			$tmpplugin = 'select2';
7408
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7409
				<script>
7410
				$(document).ready(function () {
7411
					var data = '.json_encode($formattedarrayresult).';
7412
7413
					'.($callurlonselect ? 'var saveRemoteData = '.json_encode($array).';' : '').'
7414
7415
					$(".'.$htmlname.'").select2({
7416
						data: data,
7417
						language: select2arrayoflanguage,
7418
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7419
						placeholder: "'.dol_escape_js($placeholder).'",
7420
						escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7421
						minimumInputLength: '.$minimumInputLength.',
7422
						formatResult: function(result, container, query, escapeMarkup) {
7423
							return escapeMarkup(result.text);
7424
						},
7425
						matcher: function (params, data) {
7426
7427
							if(! data.id) return null;';
7428
7429
			if ($callurlonselect) {
7430
				$outdelayed .= '
7431
7432
							var urlBase = data.url;
7433
							var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
7434
							/* console.log("params.term="+params.term); */
7435
							/* console.log("params.term encoded="+encodeURIComponent(params.term)); */
7436
							saveRemoteData[data.id].url = urlBase + separ + "sall=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
7437
			}
7438
7439
			if (!$disableFiltering) {
7440
				$outdelayed .= '
7441
7442
							if(data.text.match(new RegExp(params.term))) {
7443
								return data;
7444
							}
7445
7446
							return null;';
7447
			} else {
7448
				$outdelayed .= '
7449
7450
							return data;';
7451
			}
7452
7453
			$outdelayed .= '
7454
						}
7455
					});
7456
7457
					'.($callurlonselect ? '
7458
					/* Code to execute a GET when we select a value */
7459
					$(".'.$htmlname.'").change(function() {
7460
						var selected = $(".'.$htmlname.'").val();
7461
						console.log("We select "+selected)
7462
7463
						$(".'.$htmlname.'").val("");  /* reset visible combo value */
7464
						$.each( saveRemoteData, function( key, value ) {
7465
							if (key == selected)
7466
							{
7467
								console.log("selectArrayFilter - Do a redirect to "+value.url)
7468
								location.assign(value.url);
7469
							}
7470
						});
7471
					});' : '').'
7472
7473
				});
7474
				</script>';
7475
		}
7476
7477
		if ($acceptdelayedhtml) {
7478
			$delayedhtmlcontent .= $outdelayed;
7479
		} else {
7480
			$out .= $outdelayed;
7481
		}
7482
		return $out;
7483
	}
7484
7485
	/**
7486
	 *	Show a multiselect form from an array. WARNING: Use this only for short lists.
7487
	 *
7488
	 *	@param	string	$htmlname		Name of select
7489
	 *	@param	array	$array			Array with key+value
7490
	 *	@param	array	$selected		Array with key+value preselected
7491
	 *	@param	int		$key_in_label   1 to show key like in "[key] value"
7492
	 *	@param	int		$value_as_key   1 to use value as key
7493
	 *	@param  string	$morecss        Add more css style
7494
	 *	@param  int		$translate		Translate and encode value
7495
	 *  @param	int		$width			Force width of select box. May be used only when using jquery couch. Example: 250, 95%
7496
	 *  @param	string	$moreattrib		Add more options on select component. Example: 'disabled'
7497
	 *  @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.
7498
	 *  @param	string	$placeholder	String to use as placeholder
7499
	 *  @param	int		$addjscombo		Add js combo
7500
	 *	@return	string					HTML multiselect string
7501
	 *  @see selectarray(), selectArrayAjax(), selectArrayFilter()
7502
	 */
7503
	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)
7504
	{
7505
		global $conf, $langs;
7506
7507
		$out = '';
7508
7509
		if ($addjscombo < 0) {
7510
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7511
				$addjscombo = 1;
7512
			} else {
7513
				$addjscombo = 0;
7514
			}
7515
		}
7516
7517
		// Add code for jquery to use multiselect
7518
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
7519
			$out .= "\n".'<!-- JS CODE TO ENABLE select for id '.$htmlname.', addjscombo='.$addjscombo.' -->';
7520
			$out .= "\n".'<script>'."\n";
7521
			if ($addjscombo == 1) {
7522
				$tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
7523
				$out .= 'function formatResult(record) {'."\n";
7524
				if ($elemtype == 'category') {
7525
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
7526
				} else {
7527
					$out .= 'return record.text;';
7528
				}
7529
				$out .= '};'."\n";
7530
				$out .= 'function formatSelection(record) {'."\n";
7531
				if ($elemtype == 'category') {
7532
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
7533
				} else {
7534
					$out .= 'return record.text;';
7535
				}
7536
				$out .= '};'."\n";
7537
				$out .= '$(document).ready(function () {
7538
							$(\'#'.$htmlname.'\').'.$tmpplugin.'({
7539
								dir: \'ltr\',
7540
								// Specify format function for dropdown item
7541
								formatResult: formatResult,
7542
							 	templateResult: formatResult,		/* For 4.0 */
7543
								// Specify format function for selected item
7544
								formatSelection: formatSelection,
7545
							 	templateSelection: formatSelection		/* For 4.0 */
7546
							});
7547
7548
							/* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
7549
								 the size only if component is not hidden by default on load */
7550
							$(\'#'.$htmlname.' + .select2\').addClass(\''.$morecss.'\');
7551
						});'."\n";
7552
			} elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
7553
				// Add other js lib
7554
				// TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
7555
				// ...
7556
				$out .= 'console.log(\'addjscombo=2 for htmlname='.$htmlname.'\');';
7557
				$out .= '$(document).ready(function () {
7558
							$(\'#'.$htmlname.'\').multiSelect({
7559
								containerHTML: \'<div class="multi-select-container">\',
7560
								menuHTML: \'<div class="multi-select-menu">\',
7561
								buttonHTML: \'<span class="multi-select-button '.$morecss.'">\',
7562
								menuItemHTML: \'<label class="multi-select-menuitem">\',
7563
								activeClass: \'multi-select-container--open\',
7564
								noneText: \''.$placeholder.'\'
7565
							});
7566
						})';
7567
			}
7568
			$out .= '</script>';
7569
		}
7570
7571
		// Try also magic suggest
7572
		$out .= '<select id="'.$htmlname.'" class="multiselect'.($morecss ? ' '.$morecss : '').'" multiple name="'.$htmlname.'[]"'.($moreattrib ? ' '.$moreattrib : '').($width ? ' style="width: '.(preg_match('/%/', $width) ? $width : $width.'px').'"' : '').'>'."\n";
7573
		if (is_array($array) && !empty($array)) {
7574
			if ($value_as_key) {
7575
				$array = array_combine($array, $array);
7576
			}
7577
7578
			if (!empty($array)) {
7579
				foreach ($array as $key => $value) {
7580
					$newval = ($translate ? $langs->trans($value) : $value);
7581
					$newval = ($key_in_label ? $key.' - '.$newval : $newval);
7582
7583
					$out .= '<option value="'.$key.'"';
7584
					if (is_array($selected) && !empty($selected) && in_array((string) $key, $selected) && ((string) $key != '')) {
7585
						$out .= ' selected';
7586
					}
7587
					$out .= ' data-html="'.dol_escape_htmltag($newval).'"';
7588
					$out .= '>';
7589
					$out .= dol_htmlentitiesbr($newval);
7590
					$out .= '</option>'."\n";
7591
				}
7592
			}
7593
		}
7594
		$out .= '</select>'."\n";
7595
7596
		return $out;
7597
	}
7598
7599
7600
	/**
7601
	 *	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.
7602
	 *
7603
	 *	@param	string	$htmlname		Name of HTML field
7604
	 *	@param	array	$array			Array with array of fields we could show. This array may be modified according to setup of user.
7605
	 *  @param  string  $varpage        Id of context for page. Can be set by caller with $varpage=(empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage);
7606
	 *	@return	string					HTML multiselect string
7607
	 *  @see selectarray()
7608
	 */
7609
	public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage)
7610
	{
7611
		global $conf, $langs, $user, $extrafields;
7612
7613
		if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7614
			return '';
7615
		}
7616
7617
		$tmpvar = "MAIN_SELECTEDFIELDS_".$varpage; // To get list of saved selected fields to show
7618
7619
		if (!empty($user->conf->$tmpvar)) {		// A list of fields was already customized for user
7620
			$tmparray = explode(',', $user->conf->$tmpvar);
7621
			foreach ($array as $key => $val) {
7622
				//var_dump($key);
7623
				//var_dump($tmparray);
7624
				if (in_array($key, $tmparray)) {
7625
					$array[$key]['checked'] = 1;
7626
				} else {
7627
					$array[$key]['checked'] = 0;
7628
				}
7629
			}
7630
		} else {								// There is no list of fields already customized for user
7631
			foreach ($array as $key => $val) {
7632
				if ($array[$key]['checked'] < 0) {
7633
					$array[$key]['checked'] = 0;
7634
				}
7635
			}
7636
		}
7637
7638
		$listoffieldsforselection = '';
7639
		$listcheckedstring = '';
7640
7641
		foreach ($array as $key => $val) {
7642
			/* var_dump($val);
7643
			var_dump(array_key_exists('enabled', $val));
7644
			var_dump(!$val['enabled']);*/
7645
			if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
7646
				unset($array[$key]); // We don't want this field
7647
				continue;
7648
			}
7649
			if (!empty($val['type']) && $val['type'] == 'separate') {
7650
				// Field remains in array but we don't add it into $listoffieldsforselection
7651
				//$listoffieldsforselection .= '<li>-----</li>';
7652
				continue;
7653
			}
7654
			if ($val['label']) {
7655
				if (!empty($val['langfile']) && is_object($langs)) {
7656
					$langs->load($val['langfile']);
7657
				}
7658
7659
				// Note: $val['checked'] <> 0 means we must show the field into the combo list
7660
				$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>';
7661
				$listcheckedstring .= (empty($val['checked']) ? '' : $key.',');
7662
			}
7663
		}
7664
7665
		$out = '<!-- Component multiSelectArrayWithCheckbox '.$htmlname.' -->
7666
7667
        <dl class="dropdown">
7668
            <dt>
7669
            <a href="#'.$htmlname.'">
7670
              '.img_picto('', 'list').'
7671
            </a>
7672
            <input type="hidden" class="'.$htmlname.'" name="'.$htmlname.'" value="'.$listcheckedstring.'">
7673
            </dt>
7674
            <dd class="dropdowndd">
7675
                <div class="multiselectcheckbox'.$htmlname.'">
7676
                    <ul class="ul'.$htmlname.'">
7677
                    '.$listoffieldsforselection.'
7678
                    </ul>
7679
                </div>
7680
            </dd>
7681
        </dl>
7682
7683
        <script type="text/javascript">
7684
          jQuery(document).ready(function () {
7685
              $(\'.multiselectcheckbox'.$htmlname.' input[type="checkbox"]\').on(\'click\', function () {
7686
                  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
7687
7688
                  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\');	// Update field so we know we changed something on selected fields after POST
7689
7690
                  var title = $(this).val() + ",";
7691
                  if ($(this).is(\':checked\')) {
7692
                      $(\'.'.$htmlname.'\').val(title + $(\'.'.$htmlname.'\').val());
7693
                  }
7694
                  else {
7695
                      $(\'.'.$htmlname.'\').val( $(\'.'.$htmlname.'\').val().replace(title, \'\') )
7696
                  }
7697
                  // Now, we submit page
7698
                  //$(this).parents(\'form:first\').submit();
7699
              });
7700
7701
7702
           });
7703
        </script>
7704
7705
        ';
7706
		return $out;
7707
	}
7708
7709
	/**
7710
	 * 	Render list of categories linked to object with id $id and type $type
7711
	 *
7712
	 * 	@param		int		$id				Id of object
7713
	 * 	@param		string	$type			Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
7714
	 *  @param		int		$rendermode		0=Default, use multiselect. 1=Emulate multiselect (recommended)
7715
	 *  @param		int		$nolink			1=Do not add html links
7716
	 * 	@return		string					String with categories
7717
	 */
7718
	public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
7719
	{
7720
		global $db;
7721
7722
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7723
7724
		$cat = new Categorie($db);
7725
		$categories = $cat->containing($id, $type);
7726
7727
		if ($rendermode == 1) {
7728
			$toprint = array();
7729
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7730
				$ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text
7731
				foreach ($ways as $way) {
7732
					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.$way.'</li>';
7733
				}
7734
			}
7735
			return '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7736
		}
7737
7738
		if ($rendermode == 0) {
7739
			$arrayselected = array();
7740
			$cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 1);
7741
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7742
				$arrayselected[] = $c->id;
7743
			}
7744
7745
			return $this->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, '', 0, '100%', 'disabled', 'category');
7746
		}
7747
7748
		return 'ErrorBadValueForParameterRenderMode'; // Should not happened
7749
	}
7750
7751
	/**
7752
	 *  Show linked object block.
7753
	 *
7754
	 *  @param	CommonObject	$object		      Object we want to show links to
7755
	 *  @param  string          $morehtmlright    More html to show on right of title
7756
	 *  @param  array           $compatibleImportElementsList  Array of compatibles elements object for "import from" action
7757
	 *  @return	int							      <0 if KO, >=0 if OK
7758
	 */
7759
	public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = false)
7760
	{
7761
		global $conf, $langs, $hookmanager;
7762
		global $bc, $action;
7763
7764
		$object->fetchObjectLinked();
7765
7766
		// Bypass the default method
7767
		$hookmanager->initHooks(array('commonobject'));
7768
		$parameters = array(
7769
			'morehtmlright' => $morehtmlright,
7770
			'compatibleImportElementsList' => &$compatibleImportElementsList,
7771
		);
7772
		$reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
7773
7774
		if (empty($reshook)) {
7775
			$nbofdifferenttypes = count($object->linkedObjects);
7776
7777
			print '<!-- showLinkedObjectBlock -->';
7778
			print load_fiche_titre($langs->trans('RelatedObjects'), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
7779
7780
7781
			print '<div class="div-table-responsive-no-min">';
7782
			print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="'.$object->element.'"  data-elementid="'.$object->id.'"   >';
7783
7784
			print '<tr class="liste_titre">';
7785
			print '<td>'.$langs->trans("Type").'</td>';
7786
			print '<td>'.$langs->trans("Ref").'</td>';
7787
			print '<td class="center"></td>';
7788
			print '<td class="center">'.$langs->trans("Date").'</td>';
7789
			print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
7790
			print '<td class="right">'.$langs->trans("Status").'</td>';
7791
			print '<td></td>';
7792
			print '</tr>';
7793
7794
			$nboftypesoutput = 0;
7795
7796
			foreach ($object->linkedObjects as $objecttype => $objects) {
7797
				$tplpath = $element = $subelement = $objecttype;
7798
7799
				// to display inport button on tpl
7800
				$showImportButton = false;
7801
				if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
7802
					$showImportButton = true;
7803
				}
7804
7805
				$regs = array();
7806
				if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
7807
					$element = $regs[1];
7808
					$subelement = $regs[2];
7809
					$tplpath = $element.'/'.$subelement;
7810
				}
7811
				$tplname = 'linkedobjectblock';
7812
7813
				// To work with non standard path
7814
				if ($objecttype == 'facture') {
7815
					$tplpath = 'compta/'.$element;
7816
					if (empty($conf->facture->enabled)) {
7817
						continue; // Do not show if module disabled
7818
					}
7819
				} elseif ($objecttype == 'facturerec') {
7820
					$tplpath = 'compta/facture';
7821
					$tplname = 'linkedobjectblockForRec';
7822
					if (empty($conf->facture->enabled)) {
7823
						continue; // Do not show if module disabled
7824
					}
7825
				} elseif ($objecttype == 'propal') {
7826
					$tplpath = 'comm/'.$element;
7827
					if (empty($conf->propal->enabled)) {
7828
						continue; // Do not show if module disabled
7829
					}
7830
				} elseif ($objecttype == 'supplier_proposal') {
7831
					if (empty($conf->supplier_proposal->enabled)) {
7832
						continue; // Do not show if module disabled
7833
					}
7834
				} elseif ($objecttype == 'shipping' || $objecttype == 'shipment') {
7835
					$tplpath = 'expedition';
7836
					if (empty($conf->expedition->enabled)) {
7837
						continue; // Do not show if module disabled
7838
					}
7839
				} elseif ($objecttype == 'reception') {
7840
					$tplpath = 'reception';
7841
					if (empty($conf->reception->enabled)) {
7842
						continue; // Do not show if module disabled
7843
					}
7844
				} elseif ($objecttype == 'delivery') {
7845
					$tplpath = 'delivery';
7846
					if (empty($conf->expedition->enabled)) {
7847
						continue; // Do not show if module disabled
7848
					}
7849
				} elseif ($objecttype == 'invoice_supplier') {
7850
					$tplpath = 'fourn/facture';
7851
				} elseif ($objecttype == 'order_supplier') {
7852
					$tplpath = 'fourn/commande';
7853
				} elseif ($objecttype == 'expensereport') {
7854
					$tplpath = 'expensereport';
7855
				} elseif ($objecttype == 'subscription') {
7856
					$tplpath = 'adherents';
7857
				} elseif ($objecttype == 'conferenceorbooth') {
7858
					$tplpath = 'eventorganization';
7859
				} elseif ($objecttype == 'conferenceorboothattendee') {
7860
					$tplpath = 'eventorganization';
7861
				}
7862
7863
				global $linkedObjectBlock;
7864
				$linkedObjectBlock = $objects;
7865
7866
7867
				// Output template part (modules that overwrite templates must declare this into descriptor)
7868
				$dirtpls = array_merge($conf->modules_parts['tpl'], array('/'.$tplpath.'/tpl'));
7869
				foreach ($dirtpls as $reldir) {
7870
					if ($nboftypesoutput == ($nbofdifferenttypes - 1)) {    // No more type to show after
7871
						global $noMoreLinkedObjectBlockAfter;
7872
						$noMoreLinkedObjectBlockAfter = 1;
7873
					}
7874
7875
					$res = @include dol_buildpath($reldir.'/'.$tplname.'.tpl.php');
7876
					if ($res) {
7877
						$nboftypesoutput++;
7878
						break;
7879
					}
7880
				}
7881
			}
7882
7883
			if (!$nboftypesoutput) {
7884
				print '<tr><td class="impair opacitymedium" colspan="7">'.$langs->trans("None").'</td></tr>';
7885
			}
7886
7887
			print '</table>';
7888
7889
			if (!empty($compatibleImportElementsList)) {
7890
				$res = @include dol_buildpath('core/tpl/ajax/objectlinked_lineimport.tpl.php');
7891
			}
7892
7893
7894
			print '</div>';
7895
7896
			return $nbofdifferenttypes;
7897
		}
7898
	}
7899
7900
	/**
7901
	 *  Show block with links to link to other objects.
7902
	 *
7903
	 *  @param	CommonObject	$object				Object we want to show links to
7904
	 *  @param	array			$restrictlinksto	Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
7905
	 *  @param	array			$excludelinksto		Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
7906
	 *  @return	string								<0 if KO, >0 if OK
7907
	 */
7908
	public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
7909
	{
7910
		global $conf, $langs, $hookmanager;
7911
		global $bc, $action;
7912
7913
		$linktoelem = '';
7914
		$linktoelemlist = '';
7915
		$listofidcompanytoscan = '';
7916
7917
		if (!is_object($object->thirdparty)) {
7918
			$object->fetch_thirdparty();
7919
		}
7920
7921
		$possiblelinks = array();
7922
		if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
7923
			$listofidcompanytoscan = $object->thirdparty->id;
7924
			if (($object->thirdparty->parent > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PARENT_IN_LINKTO)) {
7925
				$listofidcompanytoscan .= ','.$object->thirdparty->parent;
7926
			}
7927
			if (($object->fk_project > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO)) {
7928
				include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
7929
				$tmpproject = new Project($this->db);
7930
				$tmpproject->fetch($object->fk_project);
7931
				if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) {
7932
					$listofidcompanytoscan .= ','.$tmpproject->socid;
7933
				}
7934
				unset($tmpproject);
7935
			}
7936
7937
			$possiblelinks = array(
7938
				'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').')'),
7939
				'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').')'),
7940
				'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').')'),
7941
				'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').')'),
7942
				'contrat'=>array(
7943
					'enabled'=>$conf->contrat->enabled,
7944
					'perms'=>1,
7945
					'label'=>'LinkToContract',
7946
					'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'
7947
				),
7948
				'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').')'),
7949
				'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').')'),
7950
				'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').')'),
7951
				'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').')'),
7952
				'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').')')
7953
			);
7954
		}
7955
7956
		// Can complete the possiblelink array
7957
		$hookmanager->initHooks(array('commonobject'));
7958
		$parameters = array('listofidcompanytoscan' => $listofidcompanytoscan);
7959
7960
		if (!empty($listofidcompanytoscan)) {  // If empty, we don't have criteria to scan the object we can link to
7961
			$reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
7962
		}
7963
7964
		if (empty($reshook)) {
7965
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
7966
				$possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
7967
			}
7968
		} 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...
7969
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
7970
				$possiblelinks = $hookmanager->resArray;
7971
			}
7972
		}
7973
7974
		foreach ($possiblelinks as $key => $possiblelink) {
7975
			$num = 0;
7976
7977
			if (empty($possiblelink['enabled'])) {
7978
				continue;
7979
			}
7980
7981
			if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
7982
				print '<div id="'.$key.'list"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display:none"').'>';
7983
7984
				if (!empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
7985
					print '<br><form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
7986
					print '<input type="hidden" name="id" value="' . $object->id . '">';
7987
					print '<input type="hidden" name="action" value="addlinkbyref">';
7988
					print '<input type="hidden" name="addlink" value="' . $key . '">';
7989
					print '<table class="noborder">';
7990
					print '<tr>';
7991
					print '<td>' . $langs->trans("Ref") . '</td>';
7992
					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>';
7993
					print '</tr>';
7994
					print '</table>';
7995
					print '</form>';
7996
				}
7997
7998
				$sql = $possiblelink['sql'];
7999
8000
				$resqllist = $this->db->query($sql);
8001
				if ($resqllist) {
8002
					$num = $this->db->num_rows($resqllist);
8003
					$i = 0;
8004
8005
					print '<br>';
8006
					print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
8007
					print '<input type="hidden" name="action" value="addlink">';
8008
					print '<input type="hidden" name="token" value="'.newToken().'">';
8009
					print '<input type="hidden" name="id" value="'.$object->id.'">';
8010
					print '<input type="hidden" name="addlink" value="'.$key.'">';
8011
					print '<table class="noborder">';
8012
					print '<tr class="liste_titre">';
8013
					print '<td class="nowrap"></td>';
8014
					print '<td class="center">'.$langs->trans("Ref").'</td>';
8015
					print '<td class="left">'.$langs->trans("RefCustomer").'</td>';
8016
					print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
8017
					print '<td class="left">'.$langs->trans("Company").'</td>';
8018
					print '</tr>';
8019
					while ($i < $num) {
8020
						$objp = $this->db->fetch_object($resqllist);
8021
8022
						print '<tr class="oddeven">';
8023
						print '<td class="left">';
8024
						print '<input type="radio" name="idtolinkto" id="'.$key.'_'.$objp->rowid.'" value="'.$objp->rowid.'">';
8025
						print '</td>';
8026
						print '<td class="center"><label for="'.$key.'_'.$objp->rowid.'">'.$objp->ref.'</label></td>';
8027
						print '<td>'.(!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')).'</td>';
8028
						print '<td class="right">';
8029
						if ($possiblelink['label'] == 'LinkToContract') {
8030
							$form = new Form($this->db);
8031
							print $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")).' ';
8032
						}
8033
						print '<span class="amount">'.price($objp->total_ht).'</span>';
8034
						print '</td>';
8035
						print '<td>'.$objp->name.'</td>';
8036
						print '</tr>';
8037
						$i++;
8038
					}
8039
					print '</table>';
8040
					print '<div class="center">';
8041
					print '<input type="submit" class="button valignmiddle marginleftonly marginrightonly" value="'.$langs->trans('ToLink').'">';
8042
					if (empty($conf->use_javascript_ajax)) {
8043
						print '<input type="submit" class="button button-cancel marginleftonly marginrightonly" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
8044
					} else {
8045
						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>';
8046
					}
8047
					print '</form>';
8048
					$this->db->free($resqllist);
8049
				} else {
8050
					dol_print_error($this->db);
8051
				}
8052
				print '</div>';
8053
8054
				//$linktoelem.=($linktoelem?' &nbsp; ':'');
8055
				if ($num > 0 || !empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8056
					$linktoelemlist .= '<li><a href="#linkto'.$key.'" class="linkto dropdowncloseonclick" rel="'.$key.'">'.$langs->trans($possiblelink['label']).' ('.$num.')</a></li>';
8057
					// } else $linktoelem.=$langs->trans($possiblelink['label']);
8058
				} else {
8059
					$linktoelemlist .= '<li><span class="linktodisabled">'.$langs->trans($possiblelink['label']).' (0)</span></li>';
8060
				}
8061
			}
8062
		}
8063
8064
		if ($linktoelemlist) {
8065
			$linktoelem = '
8066
    		<dl class="dropdown" id="linktoobjectname">
8067
    		';
8068
			if (!empty($conf->use_javascript_ajax)) {
8069
				$linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>'.$langs->trans("LinkTo").'...</a></dt>';
8070
			}
8071
			$linktoelem .= '<dd>
8072
    		<div class="multiselectlinkto">
8073
    		<ul class="ulselectedfields">'.$linktoelemlist.'
8074
    		</ul>
8075
    		</div>
8076
    		</dd>
8077
    		</dl>';
8078
		} else {
8079
			$linktoelem = '';
8080
		}
8081
8082
		if (!empty($conf->use_javascript_ajax)) {
8083
			print '<!-- Add js to show linkto box -->
8084
				<script>
8085
				jQuery(document).ready(function() {
8086
					jQuery(".linkto").click(function() {
8087
						console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
8088
					    jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
8089
					});
8090
				});
8091
				</script>
8092
		    ';
8093
		}
8094
8095
		return $linktoelem;
8096
	}
8097
8098
	/**
8099
	 *	Return an html string with a select combo box to choose yes or no
8100
	 *
8101
	 *	@param	string		$htmlname		Name of html select field
8102
	 *	@param	string		$value			Pre-selected value
8103
	 *	@param	int			$option			0 return yes/no, 1 return 1/0
8104
	 *	@param	bool		$disabled		true or false
8105
	 *  @param	int      	$useempty		1=Add empty line
8106
	 *  @param	int			$addjscombo		1=Add js beautifier on combo box
8107
	 *  @param	string		$morecss		More CSS
8108
	 *	@return	string						See option
8109
	 */
8110
	public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = '')
8111
	{
8112
		global $langs;
8113
8114
		$yes = "yes";
8115
		$no = "no";
8116
		if ($option) {
8117
			$yes = "1";
8118
			$no = "0";
8119
		}
8120
8121
		$disabled = ($disabled ? ' disabled' : '');
8122
8123
		$resultyesno = '<select class="flat width75'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'"'.$disabled.'>'."\n";
8124
		if ($useempty) {
8125
			$resultyesno .= '<option value="-1"'.(($value < 0) ? ' selected' : '').'>&nbsp;</option>'."\n";
8126
		}
8127
		if (("$value" == 'yes') || ($value == 1)) {
8128
			$resultyesno .= '<option value="'.$yes.'" selected>'.$langs->trans("Yes").'</option>'."\n";
8129
			$resultyesno .= '<option value="'.$no.'">'.$langs->trans("No").'</option>'."\n";
8130
		} else {
8131
			$selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
8132
			$resultyesno .= '<option value="'.$yes.'">'.$langs->trans("Yes").'</option>'."\n";
8133
			$resultyesno .= '<option value="'.$no.'"'.$selected.'>'.$langs->trans("No").'</option>'."\n";
8134
		}
8135
		$resultyesno .= '</select>'."\n";
8136
8137
		if ($addjscombo) {
8138
			$resultyesno .= ajax_combobox($htmlname);
8139
		}
8140
8141
		return $resultyesno;
8142
	}
8143
8144
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8145
	/**
8146
	 *  Return list of export templates
8147
	 *
8148
	 *  @param	string	$selected          Id modele pre-selectionne
8149
	 *  @param  string	$htmlname          Name of HTML select
8150
	 *  @param  string	$type              Type of searched templates
8151
	 *  @param  int		$useempty          Affiche valeur vide dans liste
8152
	 *  @return	void
8153
	 */
8154
	public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
8155
	{
8156
		// phpcs:enable
8157
		$sql = "SELECT rowid, label";
8158
		$sql .= " FROM ".MAIN_DB_PREFIX."export_model";
8159
		$sql .= " WHERE type = '".$this->db->escape($type)."'";
8160
		$sql .= " ORDER BY rowid";
8161
		$result = $this->db->query($sql);
8162
		if ($result) {
8163
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
8164
			if ($useempty) {
8165
				print '<option value="-1">&nbsp;</option>';
8166
			}
8167
8168
			$num = $this->db->num_rows($result);
8169
			$i = 0;
8170
			while ($i < $num) {
8171
				$obj = $this->db->fetch_object($result);
8172
				if ($selected == $obj->rowid) {
8173
					print '<option value="'.$obj->rowid.'" selected>';
8174
				} else {
8175
					print '<option value="'.$obj->rowid.'">';
8176
				}
8177
				print $obj->label;
8178
				print '</option>';
8179
				$i++;
8180
			}
8181
			print "</select>";
8182
		} else {
8183
			dol_print_error($this->db);
8184
		}
8185
	}
8186
8187
	/**
8188
	 *    Return a HTML area with the reference of object and a navigation bar for a business object
8189
	 *    Note: To complete search with a particular filter on select, you can set $object->next_prev_filter set to define SQL criterias.
8190
	 *
8191
	 *    @param	object	$object			Object to show.
8192
	 *    @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link.
8193
	 *    @param	string	$morehtml  		More html content to output just before the nav bar.
8194
	 *    @param	int		$shownav	  	Show Condition (navigation is shown if value is 1).
8195
	 *    @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.
8196
	 *    @param	string	$fieldref   	Name of field ref of object (object->ref) to show or 'none' to not show ref.
8197
	 *    @param	string	$morehtmlref  	More html to show after ref.
8198
	 *    @param	string	$moreparam  	More param to add in nav link url. Must start with '&...'.
8199
	 *	  @param	int		$nodbprefix		Do not include DB prefix to forge table name.
8200
	 *	  @param	string	$morehtmlleft	More html code to show before ref.
8201
	 *	  @param	string	$morehtmlstatus	More html code to show under navigation arrows (status place).
8202
	 *	  @param	string	$morehtmlright	More html code to show after ref.
8203
	 * 	  @return	string    				Portion HTML with ref + navigation buttons
8204
	 */
8205
	public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
8206
	{
8207
		global $langs, $conf, $hookmanager, $extralanguages;
8208
8209
		$ret = '';
8210
		if (empty($fieldid)) {
8211
			$fieldid = 'rowid';
8212
		}
8213
		if (empty($fieldref)) {
8214
			$fieldref = 'ref';
8215
		}
8216
8217
		// Preparing gender's display if there is one
8218
		$addgendertxt = '';
8219
		if (!empty($object->gender)) {
8220
			$addgendertxt = ' ';
8221
			switch ($object->gender) {
8222
				case 'man':
8223
					$addgendertxt .= '<i class="fas fa-mars"></i>';
8224
					break;
8225
				case 'woman':
8226
					$addgendertxt .= '<i class="fas fa-venus"></i>';
8227
					break;
8228
				case 'other':
8229
					$addgendertxt .= '<i class="fas fa-genderless"></i>';
8230
					break;
8231
			}
8232
		}
8233
8234
		// Add where from hooks
8235
		if (is_object($hookmanager)) {
8236
			$parameters = array();
8237
			$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
8238
			$object->next_prev_filter .= $hookmanager->resPrint;
8239
		}
8240
		$previous_ref = $next_ref = '';
8241
		if ($shownav) {
8242
			//print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
8243
			$object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
8244
8245
			$navurl = $_SERVER["PHP_SELF"];
8246
			// Special case for project/task page
8247
			if ($paramid == 'project_ref') {
8248
				if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) {     // TODO Remove this when nav with project_ref on task pages are ok
8249
					$navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
8250
					$paramid = 'ref';
8251
				}
8252
			}
8253
8254
			// accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
8255
			// accesskey is for Mac:               CTRL + key for all browsers
8256
			$stringforfirstkey = $langs->trans("KeyboardShortcut");
8257
			if ($conf->browser->name == 'chrome') {
8258
				$stringforfirstkey .= ' ALT +';
8259
			} elseif ($conf->browser->name == 'firefox') {
8260
				$stringforfirstkey .= ' ALT + SHIFT +';
8261
			} else {
8262
				$stringforfirstkey .= ' CTL +';
8263
			}
8264
8265
			$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>';
8266
			$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>';
8267
		}
8268
8269
		//print "xx".$previous_ref."x".$next_ref;
8270
		$ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
8271
8272
		// Right part of banner
8273
		if ($morehtmlright) {
8274
			$ret .= '<div class="inline-block floatleft">'.$morehtmlright.'</div>';
8275
		}
8276
8277
		if ($previous_ref || $next_ref || $morehtml) {
8278
			$ret .= '<div class="pagination paginationref"><ul class="right">';
8279
		}
8280
		if ($morehtml) {
8281
			$ret .= '<li class="noborder litext'.(($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '').'">'.$morehtml.'</li>';
8282
		}
8283
		if ($shownav && ($previous_ref || $next_ref)) {
8284
			$ret .= '<li class="pagination">'.$previous_ref.'</li>';
8285
			$ret .= '<li class="pagination">'.$next_ref.'</li>';
8286
		}
8287
		if ($previous_ref || $next_ref || $morehtml) {
8288
			$ret .= '</ul></div>';
8289
		}
8290
8291
		$parameters = array();
8292
		$reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
8293
		if (empty($reshook)) {
8294
			$morehtmlstatus .= $hookmanager->resPrint;
8295
		} else {
8296
			$morehtmlstatus = $hookmanager->resPrint;
8297
		}
8298
		if ($morehtmlstatus) {
8299
			$ret .= '<div class="statusref">'.$morehtmlstatus.'</div>';
8300
		}
8301
8302
		$parameters = array();
8303
		$reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
8304
		if (empty($reshook)) {
8305
			$morehtmlref .= $hookmanager->resPrint;
8306
		} elseif ($reshook > 0) {
8307
			$morehtmlref = $hookmanager->resPrint;
8308
		}
8309
8310
		// Left part of banner
8311
		if ($morehtmlleft) {
8312
			if ($conf->browser->layout == 'phone') {
8313
				$ret .= '<!-- morehtmlleft --><div class="floatleft">'.$morehtmlleft.'</div>'; // class="center" to have photo in middle
8314
			} else {
8315
				$ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">'.$morehtmlleft.'</div>';
8316
			}
8317
		}
8318
8319
		//if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
8320
		$ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid'.(($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '').'">';
8321
8322
		// For thirdparty, contact, user, member, the ref is the id, so we show something else
8323
		if ($object->element == 'societe') {
8324
			$ret .= dol_htmlentities($object->name);
8325
8326
			// List of extra languages
8327
			$arrayoflangcode = array();
8328
			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
8329
				$arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
8330
			}
8331
8332
			if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
8333
				if (!is_object($extralanguages)) {
8334
					include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
8335
					$extralanguages = new ExtraLanguages($this->db);
8336
				}
8337
				$extralanguages->fetch_name_extralanguages('societe');
8338
8339
				if (!empty($extralanguages->attributes['societe']['name'])) {
8340
					$object->fetchValuesForExtraLanguages();
8341
8342
					$htmltext = '';
8343
					// If there is extra languages
8344
					foreach ($arrayoflangcode as $extralangcode) {
8345
						$htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
8346
						if ($object->array_languages['name'][$extralangcode]) {
8347
							$htmltext .= $object->array_languages['name'][$extralangcode];
8348
						} else {
8349
							$htmltext .= '<span class="opacitymedium">'.$langs->trans("SwitchInEditModeToAddTranslation").'</span>';
8350
						}
8351
					}
8352
					$ret .= '<!-- Show translations of name -->'."\n";
8353
					$ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
8354
				}
8355
			}
8356
		} elseif ($object->element == 'member') {
8357
			$ret .= $object->ref.'<br>';
8358
			$fullname = $object->getFullName($langs);
8359
			if ($object->morphy == 'mor' && $object->societe) {
8360
				$ret .= dol_htmlentities($object->societe).((!empty($fullname) && $object->societe != $fullname) ? ' ('.dol_htmlentities($fullname).$addgendertxt.')' : '');
8361
			} else {
8362
				$ret .= dol_htmlentities($fullname).$addgendertxt.((!empty($object->societe) && $object->societe != $fullname) ? ' ('.dol_htmlentities($object->societe).')' : '');
8363
			}
8364
		} elseif (in_array($object->element, array('contact', 'user', 'usergroup'))) {
8365
			$ret .= dol_htmlentities($object->getFullName($langs)).$addgendertxt;
8366
		} elseif (in_array($object->element, array('action', 'agenda'))) {
8367
			$ret .= $object->ref.'<br>'.$object->label;
8368
		} elseif (in_array($object->element, array('adherent_type'))) {
8369
			$ret .= $object->label;
8370
		} elseif ($object->element == 'ecm_directories') {
8371
			$ret .= '';
8372
		} elseif ($fieldref != 'none') {
8373
			$ret .= dol_htmlentities($object->$fieldref);
8374
		}
8375
8376
		if ($morehtmlref) {
8377
			// don't add a additional space, when "$morehtmlref" starts with a HTML div tag
8378
			if (substr($morehtmlref, 0, 4) != '<div') {
8379
				$ret .= ' ';
8380
			}
8381
8382
			$ret .= $morehtmlref;
8383
		}
8384
8385
		$ret .= '</div>';
8386
8387
		$ret .= '</div><!-- End banner content -->';
8388
8389
		return $ret;
8390
	}
8391
8392
8393
	/**
8394
	 *  Return HTML code to output a barcode
8395
	 *
8396
	 *  @param	Object	$object			Object containing data to retrieve file name
8397
	 * 	@param	int		$width			Width of photo
8398
	 * 	@param	string	$morecss		More CSS on img of barcode
8399
	 * 	@return string    				HTML code to output barcode
8400
	 */
8401
	public function showbarcode(&$object, $width = 100, $morecss = '')
8402
	{
8403
		global $conf;
8404
8405
		//Check if barcode is filled in the card
8406
		if (empty($object->barcode)) {
8407
			return '';
8408
		}
8409
8410
		// Complete object if not complete
8411
		if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
8412
			$result = $object->fetch_barcode();
8413
			//Check if fetch_barcode() failed
8414
			if ($result < 1) {
8415
				return '<!-- ErrorFetchBarcode -->';
8416
			}
8417
		}
8418
8419
		// Barcode image
8420
		$url = DOL_URL_ROOT.'/viewimage.php?modulepart=barcode&generator='.urlencode($object->barcode_type_coder).'&code='.urlencode($object->barcode).'&encoding='.urlencode($object->barcode_type_code);
8421
		$out = '<!-- url barcode = '.$url.' -->';
8422
		$out .= '<img src="'.$url.'"'.($morecss ? ' class="'.$morecss.'"' : '').'>';
8423
		return $out;
8424
	}
8425
8426
	/**
8427
	 *    	Return HTML code to output a photo
8428
	 *
8429
	 *    	@param	string		$modulepart			Key to define module concerned ('societe', 'userphoto', 'memberphoto')
8430
	 *     	@param  object		$object				Object containing data to retrieve file name
8431
	 * 		@param	int			$width				Width of photo
8432
	 * 		@param	int			$height				Height of photo (auto if 0)
8433
	 * 		@param	int			$caneditfield		Add edit fields
8434
	 * 		@param	string		$cssclass			CSS name to use on img for photo
8435
	 * 		@param	string		$imagesize		    'mini', 'small' or '' (original)
8436
	 *      @param  int         $addlinktofullsize  Add link to fullsize image
8437
	 *      @param  int         $cache              1=Accept to use image in cache
8438
	 *      @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 ''.
8439
	 *      @param	int			$noexternsourceoverwrite	No overwrite image with extern source (like 'gravatar' or other module)
8440
	 * 	  	@return string    						HTML code to output photo
8441
	 */
8442
	public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
8443
	{
8444
		global $conf, $langs;
8445
8446
		$entity = (!empty($object->entity) ? $object->entity : $conf->entity);
8447
		$id = (!empty($object->id) ? $object->id : $object->rowid);
8448
8449
		$ret = '';
8450
		$dir = '';
8451
		$file = '';
8452
		$originalfile = '';
8453
		$altfile = '';
8454
		$email = '';
8455
		$capture = '';
8456
		if ($modulepart == 'societe') {
8457
			$dir = $conf->societe->multidir_output[$entity];
8458
			if (!empty($object->logo)) {
8459
				if (dolIsAllowedForPreview($object->logo)) {
8460
					if ((string) $imagesize == 'mini') {
8461
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
8462
					} elseif ((string) $imagesize == 'small') {
8463
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_small');
8464
					} else {
8465
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
8466
					}
8467
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
8468
				}
8469
			}
8470
			$email = $object->email;
8471
		} elseif ($modulepart == 'contact')	{
8472
			$dir = $conf->societe->multidir_output[$entity].'/contact';
8473
			if (!empty($object->photo)) {
8474
				if (dolIsAllowedForPreview($object->photo)) {
8475
					if ((string) $imagesize == 'mini') {
8476
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_mini');
8477
					} elseif ((string) $imagesize == 'small') {
8478
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_small');
8479
					} else {
8480
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
8481
					}
8482
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
8483
				}
8484
			}
8485
			$email = $object->email;
8486
			$capture = 'user';
8487
		} elseif ($modulepart == 'userphoto') {
8488
			$dir = $conf->user->dir_output;
8489
			if (!empty($object->photo)) {
8490
				if (dolIsAllowedForPreview($object->photo)) {
8491
					if ((string) $imagesize == 'mini') {
8492
						$file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_mini');
8493
					} elseif ((string) $imagesize == 'small') {
8494
						$file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_small');
8495
					} else {
8496
						$file = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
8497
					}
8498
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
8499
				}
8500
			}
8501
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8502
				$altfile = $object->id.".jpg"; // For backward compatibility
8503
			}
8504
			$email = $object->email;
8505
			$capture = 'user';
8506
		} elseif ($modulepart == 'memberphoto')	{
8507
			$dir = $conf->adherent->dir_output;
8508
			if (!empty($object->photo)) {
8509
				if (dolIsAllowedForPreview($object->photo)) {
8510
					if ((string) $imagesize == 'mini') {
8511
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_mini');
8512
					} elseif ((string) $imagesize == 'small') {
8513
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_small');
8514
					} else {
8515
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
8516
					}
8517
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
8518
				}
8519
			}
8520
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8521
				$altfile = $object->id.".jpg"; // For backward compatibility
8522
			}
8523
			$email = $object->email;
8524
			$capture = 'user';
8525
		} else {
8526
			// Generic case to show photos
8527
			$dir = $conf->$modulepart->dir_output;
8528
			if (!empty($object->photo)) {
8529
				if (dolIsAllowedForPreview($object->photo)) {
8530
					if ((string) $imagesize == 'mini') {
8531
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_mini');
8532
					} elseif ((string) $imagesize == 'small') {
8533
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_small');
8534
					} else {
8535
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
8536
					}
8537
					$originalfile = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
8538
				}
8539
			}
8540
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8541
				$altfile = $object->id.".jpg"; // For backward compatibility
8542
			}
8543
			$email = $object->email;
8544
		}
8545
8546
		if ($forcecapture) {
8547
			$capture = $forcecapture;
8548
		}
8549
8550
		if ($dir) {
8551
			if ($file && file_exists($dir."/".$file)) {
8552
				if ($addlinktofullsize) {
8553
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
8554
					if ($urladvanced) {
8555
						$ret .= '<a href="'.$urladvanced.'">';
8556
					} else {
8557
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
8558
					}
8559
				}
8560
				$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.'">';
8561
				if ($addlinktofullsize) {
8562
					$ret .= '</a>';
8563
				}
8564
			} elseif ($altfile && file_exists($dir."/".$altfile)) {
8565
				if ($addlinktofullsize) {
8566
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
8567
					if ($urladvanced) {
8568
						$ret .= '<a href="'.$urladvanced.'">';
8569
					} else {
8570
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
8571
					}
8572
				}
8573
				$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.'">';
8574
				if ($addlinktofullsize) {
8575
					$ret .= '</a>';
8576
				}
8577
			} else {
8578
				$nophoto = '/public/theme/common/nophoto.png';
8579
				$defaultimg = 'identicon';		// For gravatar
8580
				if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) {	// For modules that need a special image when photo not found
8581
					if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && strpos($object->morphy, 'mor')) !== false) {
8582
						$nophoto = 'company';
8583
					} else {
8584
						$nophoto = '/public/theme/common/user_anonymous.png';
8585
						if (!empty($object->gender) && $object->gender == 'man') {
8586
							$nophoto = '/public/theme/common/user_man.png';
8587
						}
8588
						if (!empty($object->gender) && $object->gender == 'woman') {
8589
							$nophoto = '/public/theme/common/user_woman.png';
8590
						}
8591
					}
8592
				}
8593
8594
				if (!empty($conf->gravatar->enabled) && $email && empty($noexternsourceoverwrite)) {
8595
					// see https://gravatar.com/site/implement/images/php/
8596
					$ret .= '<!-- Put link to gravatar -->';
8597
					$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
8598
				} else {
8599
					if ($nophoto == 'company') {
8600
						$ret .= '<div class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').'">'.img_picto('', 'company').'</div>';
8601
					} else {
8602
						$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.$nophoto.'">';
8603
					}
8604
				}
8605
			}
8606
8607
			if ($caneditfield) {
8608
				if ($object->photo) {
8609
					$ret .= "<br>\n";
8610
				}
8611
				$ret .= '<table class="nobordernopadding centpercent">';
8612
				if ($object->photo) {
8613
					$ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> '.$langs->trans("Delete").'<br><br></td></tr>';
8614
				}
8615
				$ret .= '<tr><td class="tdoverflow"><input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"'.($capture ? ' capture="'.$capture.'"' : '').'></td></tr>';
8616
				$ret .= '</table>';
8617
			}
8618
		} else {
8619
			dol_print_error('', 'Call of showphoto with wrong parameters modulepart='.$modulepart);
8620
		}
8621
8622
		return $ret;
8623
	}
8624
8625
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8626
	/**
8627
	 *	Return select list of groups
8628
	 *
8629
	 *  @param	string	$selected       Id group preselected
8630
	 *  @param  string	$htmlname       Field name in form
8631
	 *  @param  int		$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
8632
	 *  @param  string	$exclude        Array list of groups id to exclude
8633
	 * 	@param	int		$disabled		If select list must be disabled
8634
	 *  @param  string	$include        Array list of groups id to include
8635
	 * 	@param	int		$enableonly		Array list of groups id to be enabled. All other must be disabled
8636
	 * 	@param	string	$force_entity	'0' or Ids of environment to force
8637
	 * 	@param	bool	$multiple		add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
8638
	 *  @param  string	$morecss		More css to add to html component
8639
	 *  @return	string
8640
	 *  @see select_dolusers()
8641
	 */
8642
	public function select_dolgroups($selected = '', $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $multiple = false, $morecss = '')
8643
	{
8644
		// phpcs:enable
8645
		global $conf, $user, $langs;
8646
8647
		// Permettre l'exclusion de groupes
8648
		if (is_array($exclude)) {
0 ignored issues
show
introduced by
The condition is_array($exclude) is always false.
Loading history...
8649
			$excludeGroups = implode(",", $exclude);
8650
		}
8651
		// Permettre l'inclusion de groupes
8652
		if (is_array($include)) {
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
8653
			$includeGroups = implode(",", $include);
8654
		}
8655
8656
		if (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
8657
			$selected = array($selected);
8658
		}
8659
8660
		$out = '';
8661
8662
		// On recherche les groupes
8663
		$sql = "SELECT ug.rowid, ug.nom as name";
8664
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
8665
			$sql .= ", e.label";
8666
		}
8667
		$sql .= " FROM ".MAIN_DB_PREFIX."usergroup as ug ";
8668
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
8669
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=ug.entity";
8670
			if ($force_entity) {
8671
				$sql .= " WHERE ug.entity IN (0, ".$force_entity.")";
8672
			} else {
8673
				$sql .= " WHERE ug.entity IS NOT NULL";
8674
			}
8675
		} else {
8676
			$sql .= " WHERE ug.entity IN (0, ".$conf->entity.")";
8677
		}
8678
		if (is_array($exclude) && $excludeGroups) {
0 ignored issues
show
introduced by
The condition is_array($exclude) is always false.
Loading history...
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...
8679
			$sql .= " AND ug.rowid NOT IN (".$this->db->sanitize($excludeGroups).")";
8680
		}
8681
		if (is_array($include) && $includeGroups) {
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
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...
8682
			$sql .= " AND ug.rowid IN (".$this->db->sanitize($includeGroups).")";
8683
		}
8684
		$sql .= " ORDER BY ug.nom ASC";
8685
8686
		dol_syslog(get_class($this)."::select_dolgroups", LOG_DEBUG);
8687
		$resql = $this->db->query($sql);
8688
		if ($resql) {
8689
			// Enhance with select2
8690
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
8691
			$out .= ajax_combobox($htmlname);
8692
8693
			$out .= '<select class="flat minwidth200'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
8694
8695
			$num = $this->db->num_rows($resql);
8696
			$i = 0;
8697
			if ($num) {
8698
				if ($show_empty && !$multiple) {
8699
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>&nbsp;</option>'."\n";
8700
				}
8701
8702
				while ($i < $num) {
8703
					$obj = $this->db->fetch_object($resql);
8704
					$disableline = 0;
8705
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
8706
						$disableline = 1;
8707
					}
8708
8709
					$out .= '<option value="'.$obj->rowid.'"';
8710
					if ($disableline) {
8711
						$out .= ' disabled';
8712
					}
8713
					if ((is_object($selected[0]) && $selected[0]->id == $obj->rowid) || (!is_object($selected[0]) && in_array($obj->rowid, $selected))) {
8714
						$out .= ' selected';
8715
					}
8716
					$out .= '>';
8717
8718
					$out .= $obj->name;
8719
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1) {
8720
						$out .= " (".$obj->label.")";
8721
					}
8722
8723
					$out .= '</option>';
8724
					$i++;
8725
				}
8726
			} else {
8727
				if ($show_empty) {
8728
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'></option>'."\n";
8729
				}
8730
				$out .= '<option value="" disabled>'.$langs->trans("NoUserGroupDefined").'</option>';
8731
			}
8732
			$out .= '</select>';
8733
		} else {
8734
			dol_print_error($this->db);
8735
		}
8736
8737
		return $out;
8738
	}
8739
8740
8741
	/**
8742
	 *	Return HTML to show the search and clear seach button
8743
	 *
8744
	 *  @return	string
8745
	 */
8746
	public function showFilterButtons()
8747
	{
8748
		$out = '<div class="nowraponall">';
8749
		$out .= '<button type="submit" class="liste_titre button_search" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
8750
		$out .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
8751
		$out .= '</div>';
8752
8753
		return $out;
8754
	}
8755
8756
	/**
8757
	 *	Return HTML to show the search and clear search button
8758
	 *
8759
	 *  @param  string  $cssclass                  CSS class
8760
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
8761
	 *  @param  string  $massactionname            Mass action button name that will launch an action on the selected items
8762
	 *  @return	string
8763
	 */
8764
	public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
8765
	{
8766
		global $conf, $langs;
8767
8768
		$out = '';
8769
8770
		if (!empty($conf->use_javascript_ajax)) {
8771
			$out .= '<div class="inline-block checkallactions"><input type="checkbox" id="'.$cssclass.'s" name="'.$cssclass.'s" class="checkallactions"></div>';
8772
		}
8773
		$out .= '<script>
8774
            $(document).ready(function() {
8775
                $("#' . $cssclass.'s").click(function() {
8776
                    if($(this).is(\':checked\')){
8777
                        console.log("We check all '.$cssclass.' and trigger the change method");
8778
                		$(".'.$cssclass.'").prop(\'checked\', true).trigger(\'change\');
8779
                    }
8780
                    else
8781
                    {
8782
                        console.log("We uncheck all");
8783
                		$(".'.$cssclass.'").prop(\'checked\', false).trigger(\'change\');
8784
                    }'."\n";
8785
		if ($calljsfunction) {
8786
			$out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "'.$massactionname.'", "'.$cssclass.'"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
8787
		}
8788
		$out .= '         });
8789
        	        $(".' . $cssclass.'").change(function() {
8790
					$(this).closest("tr").toggleClass("highlight", this.checked);
8791
				});
8792
		 	});
8793
    	</script>';
8794
8795
		return $out;
8796
	}
8797
8798
	/**
8799
	 *	Return HTML to show the search and clear seach button
8800
	 *
8801
	 *  @param	int  	$addcheckuncheckall        Add the check all/uncheck all checkbox (use javascript) and code to manage this
8802
	 *  @param  string  $cssclass                  CSS class
8803
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
8804
	 *  @param  string  $massactionname            Mass action name
8805
	 *  @return	string
8806
	 */
8807
	public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
8808
	{
8809
		$out = $this->showFilterButtons();
8810
		if ($addcheckuncheckall) {
8811
			$out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
8812
		}
8813
		return $out;
8814
	}
8815
8816
	/**
8817
	 * Return HTML to show the select of expense categories
8818
	 *
8819
	 * @param	string	$selected              preselected category
8820
	 * @param	string	$htmlname              name of HTML select list
8821
	 * @param	integer	$useempty              1=Add empty line
8822
	 * @param	array	$excludeid             id to exclude
8823
	 * @param	string	$target                htmlname of target select to bind event
8824
	 * @param	int		$default_selected      default category to select if fk_c_type_fees change = EX_KME
8825
	 * @param	array	$params                param to give
8826
	 * @param	int		$info_admin			   Show the tooltip help picto to setup list
8827
	 * @return	string
8828
	 */
8829
	public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
8830
	{
8831
		global $db, $langs, $user;
8832
8833
		$out = '';
8834
		$sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1';
8835
		$sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')';
8836
		if (!empty($excludeid)) {
8837
			$sql .= ' AND rowid NOT IN ('.$this->db->sanitize(implode(',', $excludeid)).')';
8838
		}
8839
		$sql .= ' ORDER BY label';
8840
8841
		$resql = $db->query($sql);
8842
		if ($resql) {
8843
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp maxwidth200">';
8844
			if ($useempty) {
8845
				$out .= '<option value="0">&nbsp;</option>';
8846
			}
8847
8848
			while ($obj = $db->fetch_object($resql)) {
8849
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.$langs->trans($obj->label).'</option>';
8850
			}
8851
			$out .= '</select>';
8852
			$out .= ajax_combobox('select_'.$htmlname);
8853
8854
			if (!empty($htmlname) && $user->admin && $info_admin) {
8855
				$out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
8856
			}
8857
8858
			if (!empty($target)) {
8859
				$sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
8860
				$resql = $db->query($sql);
8861
				if ($resql) {
8862
					if ($db->num_rows($resql) > 0) {
8863
						$obj = $db->fetch_object($resql);
8864
						$out .= '<script>
8865
							$(function() {
8866
								$("select[name='.$target.']").on("change", function() {
8867
									var current_val = $(this).val();
8868
									if (current_val == '.$obj->id.') {';
8869
						if (!empty($default_selected) || !empty($selected)) {
8870
							$out .= '$("select[name='.$htmlname.']").val("'.($default_selected > 0 ? $default_selected : $selected).'");';
8871
						}
8872
8873
						$out .= '
8874
										$("select[name='.$htmlname.']").change();
8875
									}
8876
								});
8877
8878
								$("select[name='.$htmlname.']").change(function() {
8879
8880
									if ($("select[name='.$target.']").val() == '.$obj->id.') {
8881
										// get price of kilometer to fill the unit price
8882
										$.ajax({
8883
											method: "POST",
8884
											dataType: "json",
8885
											data: { fk_c_exp_tax_cat: $(this).val(), token: \''.currentToken().'\' },
8886
											url: "'.(DOL_URL_ROOT.'/expensereport/ajax/ajaxik.php?'.$params).'",
8887
										}).done(function( data, textStatus, jqXHR ) {
8888
											console.log(data);
8889
											if (typeof data.up != "undefined") {
8890
												$("input[name=value_unit]").val(data.up);
8891
												$("select[name='.$htmlname.']").attr("title", data.title);
8892
											} else {
8893
												$("input[name=value_unit]").val("");
8894
												$("select[name='.$htmlname.']").attr("title", "");
8895
											}
8896
										});
8897
									}
8898
								});
8899
							});
8900
						</script>';
8901
					}
8902
				}
8903
			}
8904
		} else {
8905
			dol_print_error($db);
8906
		}
8907
8908
		return $out;
8909
	}
8910
8911
	/**
8912
	 * Return HTML to show the select ranges of expense range
8913
	 *
8914
	 * @param	string	$selected    preselected category
8915
	 * @param	string	$htmlname    name of HTML select list
8916
	 * @param	integer	$useempty    1=Add empty line
8917
	 * @return	string
8918
	 */
8919
	public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
8920
	{
8921
		global $db, $conf, $langs;
8922
8923
		$out = '';
8924
		$sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range';
8925
		$sql .= ' WHERE entity = '.$conf->entity.' AND active = 1';
8926
8927
		$resql = $db->query($sql);
8928
		if ($resql) {
8929
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
8930
			if ($useempty) {
8931
				$out .= '<option value="0"></option>';
8932
			}
8933
8934
			while ($obj = $db->fetch_object($resql)) {
8935
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.price($obj->range_ik, 0, $langs, 1, 0).'</option>';
8936
			}
8937
			$out .= '</select>';
8938
		} else {
8939
			dol_print_error($db);
8940
		}
8941
8942
		return $out;
8943
	}
8944
8945
	/**
8946
	 * Return HTML to show a select of expense
8947
	 *
8948
	 * @param	string	$selected    preselected category
8949
	 * @param	string	$htmlname    name of HTML select list
8950
	 * @param	integer	$useempty    1=Add empty choice
8951
	 * @param	integer	$allchoice   1=Add all choice
8952
	 * @param	integer	$useid       0=use 'code' as key, 1=use 'id' as key
8953
	 * @return	string
8954
	 */
8955
	public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
8956
	{
8957
		global $db, $langs;
8958
8959
		$out = '';
8960
		$sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees';
8961
		$sql .= ' WHERE active = 1';
8962
8963
		$resql = $db->query($sql);
8964
		if ($resql) {
8965
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
8966
			if ($useempty) {
8967
				$out .= '<option value="0"></option>';
8968
			}
8969
			if ($allchoice) {
8970
				$out .= '<option value="-1">'.$langs->trans('AllExpenseReport').'</option>';
8971
			}
8972
8973
			$field = 'code';
8974
			if ($useid) {
8975
				$field = 'id';
8976
			}
8977
8978
			while ($obj = $db->fetch_object($resql)) {
8979
				$key = $langs->trans($obj->code);
8980
				$out .= '<option '.($selected == $obj->{$field} ? 'selected="selected"' : '').' value="'.$obj->{$field}.'">'.($key != $obj->code ? $key : $obj->label).'</option>';
8981
			}
8982
			$out .= '</select>';
8983
		} else {
8984
			dol_print_error($db);
8985
		}
8986
8987
		return $out;
8988
	}
8989
8990
	/**
8991
	 *  Output a combo list with invoices qualified for a third party
8992
	 *
8993
	 *  @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)
8994
	 *  @param  int		$selected   	Id invoice preselected
8995
	 *  @param  string	$htmlname   	Name of HTML select
8996
	 *	@param	int		$maxlength		Maximum length of label
8997
	 *	@param	int		$option_only	Return only html options lines without the select tag
8998
	 *	@param	string	$show_empty		Add an empty line ('1' or string to show for empty line)
8999
	 *  @param	int		$discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
9000
	 *  @param	int		$forcefocus		Force focus on field (works with javascript only)
9001
	 *  @param	int		$disabled		Disabled
9002
	 *  @param	string	$morecss        More css added to the select component
9003
	 *  @param	string	$projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
9004
	 *  @param	string	$showproject	'all' = Show project info, ''=Hide project info
9005
	 *  @param	User	$usertofilter	User object to use for filtering
9006
	 *	@return int         			Nbr of project if OK, <0 if KO
9007
	 */
9008
	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)
9009
	{
9010
		global $user, $conf, $langs;
9011
9012
		require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
9013
9014
		if (is_null($usertofilter)) {
9015
			$usertofilter = $user;
9016
		}
9017
9018
		$out = '';
9019
9020
		$hideunselectables = false;
9021
		if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) {
9022
			$hideunselectables = true;
9023
		}
9024
9025
		if (empty($projectsListId)) {
9026
			if (empty($usertofilter->rights->projet->all->lire)) {
9027
				$projectstatic = new Project($this->db);
9028
				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
9029
			}
9030
		}
9031
9032
		// Search all projects
9033
		$sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref,
9034
            p.title, p.fk_soc, p.fk_statut, p.public,";
9035
		$sql .= ' s.nom as name';
9036
		$sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p';
9037
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,';
9038
		$sql .= ' '.MAIN_DB_PREFIX.'facture as f';
9039
		$sql .= " WHERE p.entity IN (".getEntity('project').")";
9040
		$sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
9041
		//if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
9042
		//if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
9043
		//if ($socid > 0)  $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)";
9044
		$sql .= " ORDER BY p.ref, f.ref ASC";
9045
9046
		$resql = $this->db->query($sql);
9047
		if ($resql) {
9048
			// Use select2 selector
9049
			if (!empty($conf->use_javascript_ajax)) {
9050
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
9051
				$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
9052
				$out .= $comboenhancement;
9053
				$morecss = 'minwidth200imp maxwidth500';
9054
			}
9055
9056
			if (empty($option_only)) {
9057
				$out .= '<select class="valignmiddle flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').' id="'.$htmlname.'" name="'.$htmlname.'">';
9058
			}
9059
			if (!empty($show_empty)) {
9060
				$out .= '<option value="0" class="optiongrey">';
9061
				if (!is_numeric($show_empty)) {
9062
					$out .= $show_empty;
9063
				} else {
9064
					$out .= '&nbsp;';
9065
				}
9066
				$out .= '</option>';
9067
			}
9068
			$num = $this->db->num_rows($resql);
9069
			$i = 0;
9070
			if ($num) {
9071
				while ($i < $num) {
9072
					$obj = $this->db->fetch_object($resql);
9073
					// 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.
9074
					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire)) {
9075
						// Do nothing
9076
					} else {
9077
						if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
9078
							$i++;
9079
							continue;
9080
						}
9081
9082
						$labeltoshow = '';
9083
9084
						if ($showproject == 'all') {
9085
							$labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
9086
							if ($obj->name) {
9087
								$labeltoshow .= ' - '.$obj->name; // Soc name
9088
							}
9089
9090
							$disabled = 0;
9091
							if ($obj->fk_statut == Project::STATUS_DRAFT) {
9092
								$disabled = 1;
9093
								$labeltoshow .= ' - '.$langs->trans("Draft");
9094
							} elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
9095
								if ($discard_closed == 2) {
9096
									$disabled = 1;
9097
								}
9098
								$labeltoshow .= ' - '.$langs->trans("Closed");
9099
							} elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
9100
								$disabled = 1;
9101
								$labeltoshow .= ' - '.$langs->trans("LinkedToAnotherCompany");
9102
							}
9103
						}
9104
9105
						if (!empty($selected) && $selected == $obj->rowid) {
9106
							$out .= '<option value="'.$obj->rowid.'" selected';
9107
							//if ($disabled) $out.=' disabled';						// with select2, field can't be preselected if disabled
9108
							$out .= '>'.$labeltoshow.'</option>';
9109
						} else {
9110
							if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
9111
								$resultat = '';
9112
							} else {
9113
								$resultat = '<option value="'.$obj->rowid.'"';
9114
								if ($disabled) {
9115
									$resultat .= ' disabled';
9116
								}
9117
								//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
9118
								//else $labeltoshow.=' ('.$langs->trans("Private").')';
9119
								$resultat .= '>';
9120
								$resultat .= $labeltoshow;
9121
								$resultat .= '</option>';
9122
							}
9123
							$out .= $resultat;
9124
						}
9125
					}
9126
					$i++;
9127
				}
9128
			}
9129
			if (empty($option_only)) {
9130
				$out .= '</select>';
9131
			}
9132
9133
			print $out;
9134
9135
			$this->db->free($resql);
9136
			return $num;
9137
		} else {
9138
			dol_print_error($this->db);
9139
			return -1;
9140
		}
9141
	}
9142
9143
	/**
9144
	 * Output the component to make advanced search criteries
9145
	 *
9146
	 * @param	array		$arrayofcriterias			          Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...)
9147
	 * @param	array		$search_component_params	          Array of selected search criterias
9148
	 * @param   array       $arrayofinputfieldsalreadyoutput      Array of input fields already inform. The component will not generate a hidden input field if it is in this list.
9149
	 * @return	string									          HTML component for advanced search
9150
	 */
9151
	public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array())
9152
	{
9153
		global $langs;
9154
9155
		$ret = '';
9156
9157
		$ret .= '<div class="nowrap centpercent">';
9158
		//$ret .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9159
		$ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor paddingright">';
9160
		$ret .= '<span class="fas fa-filter linkobject boxfilter" title="Filter" id="idsubimgproductdistribution"></span>';
9161
		$ret .= $langs->trans("Filters");
9162
		$ret .= '</a>';
9163
		//$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9164
		$ret .= '<div name="search_component_params" class="search_component_params inline-block minwidth500 maxwidth300onsmartphone valignmiddle">';
9165
		$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
9166
9167
		$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
9168
		$ret .= '</div>';
9169
		$ret .= '<input type="hidden" name="search_component_params_hidden" class="search_component_params_hidden" value="'.GETPOST("search_component_params_hidden").'">';
9170
		// For compatibility with forms that show themself the search criteria in addition of this component, we output the fields
9171
		foreach ($arrayofcriterias as $criterias) {
9172
			foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {
9173
				if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
9174
					continue;
9175
				}
9176
				if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
9177
					continue;
9178
				}
9179
				if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
9180
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_start">';
9181
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startyear">';
9182
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startmonth">';
9183
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startday">';
9184
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_end">';
9185
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endyear">';
9186
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endmonth">';
9187
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endday">';
9188
				} else {
9189
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'">';
9190
				}
9191
			}
9192
		}
9193
		$ret .= '</div>';
9194
9195
9196
		return $ret;
9197
	}
9198
9199
	/**
9200
	 * selectModelMail
9201
	 *
9202
	 * @param   string   $prefix     	Prefix
9203
	 * @param   string   $modelType  	Model type
9204
	 * @param	int		 $default	 	1=Show also Default mail template
9205
	 * @param	int		 $addjscombo	Add js combobox
9206
	 * @return  string               	HTML select string
9207
	 */
9208
	public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
9209
	{
9210
		global $langs, $db, $user;
9211
9212
		$retstring = '';
9213
9214
		$TModels = array();
9215
9216
		include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
9217
		$formmail = new FormMail($db);
9218
		$result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
9219
9220
		if ($default) {
9221
			$TModels[0] = $langs->trans('DefaultMailModel');
9222
		}
9223
		if ($result > 0) {
9224
			foreach ($formmail->lines_model as $model) {
9225
				$TModels[$model->id] = $model->label;
9226
			}
9227
		}
9228
9229
		$retstring .= '<select class="flat" id="select_'.$prefix.'model_mail" name="'.$prefix.'model_mail">';
9230
9231
		foreach ($TModels as $id_model => $label_model) {
9232
			$retstring .= '<option value="'.$id_model.'"';
9233
			$retstring .= ">".$label_model."</option>";
9234
		}
9235
9236
		$retstring .= "</select>";
9237
9238
		if ($addjscombo) {
9239
			$retstring .= ajax_combobox('select_'.$prefix.'model_mail');
9240
		}
9241
9242
		return $retstring;
9243
	}
9244
}
9245