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

Form::select_company()   F

Complexity

Conditions 15
Paths 513

Size

Total Lines 46
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 28
nc 513
nop 15
dl 0
loc 46
rs 2.4263
c 0
b 0
f 0

How to fix   Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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

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

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

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

915
					array_multisort($favorite, SORT_DESC, $label, /** @scrutinizer ignore-type */ SORT_ASC, $countryArray);
Loading history...
916
				} else {
917
					$countryArray = dol_sort_array($countryArray, 'label');
918
				}
919
920
				if ($showempty) {
921
					$out .= '<option value="">&nbsp;</option>'."\n";
922
				}
923
924
				if ($addspecialentries) {	// Add dedicated entries for groups of countries
925
					//if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
926
					$out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
927
					$out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
928
					if ($mysoc->isInEEC()) {
929
						$out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
930
					}
931
					$out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
932
					$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
933
				}
934
935
				foreach ($countryArray as $row) {
936
					//if (empty($showempty) && empty($row['rowid'])) continue;
937
					if (empty($row['rowid'])) {
938
						continue;
939
					}
940
					if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
941
						continue; // exclude some countries
942
					}
943
944
					if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
945
						$atleastonefavorite++;
946
					}
947
					if (empty($row['favorite']) && $atleastonefavorite) {
948
						$atleastonefavorite = 0;
949
						$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
950
					}
951
952
					$labeltoshow = '';
953
					if ($row['label']) {
954
						$labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
955
					} else {
956
						$labeltoshow .= '&nbsp;';
957
					}
958
					if ($row['code_iso']) {
959
						$labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
960
						if (empty($hideflags)) {
961
							$tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
962
							$labeltoshow = $tmpflag.' '.$labeltoshow;
963
						}
964
					}
965
966
					if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
967
						$out .= '<option value="'.($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']).'" selected data-html="'.dol_escape_htmltag($labeltoshow).'" data-eec="'.((int) $row['eec']).'">';
968
					} else {
969
						$out .= '<option value="'.($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']).'" data-html="'.dol_escape_htmltag($labeltoshow).'" data-eec="'.((int) $row['eec']).'">';
970
					}
971
					$out .= $labeltoshow;
972
					$out .= '</option>'."\n";
973
				}
974
			}
975
			$out .= '</select>';
976
		} else {
977
			dol_print_error($this->db);
978
		}
979
980
		// Make select dynamic
981
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
982
		$out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
983
984
		return $out;
985
	}
986
987
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
988
	/**
989
	 *  Return select list of incoterms
990
	 *
991
	 *  @param	string	$selected       		Id or Code of preselected incoterm
992
	 *  @param	string	$location_incoterms     Value of input location
993
	 *  @param	string	$page       			Defined the form action
994
	 *  @param  string	$htmlname       		Name of html select object
995
	 *  @param  string	$htmloption     		Options html on select object
996
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
997
	 *  @param	array	$events					Event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
998
	 *  @return string           				HTML string with select and input
999
	 */
1000
	public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array())
1001
	{
1002
		// phpcs:enable
1003
		global $conf, $langs;
1004
1005
		$langs->load("dict");
1006
1007
		$out = '';
1008
		$incotermArray = array();
1009
1010
		$sql = "SELECT rowid, code";
1011
		$sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms";
1012
		$sql .= " WHERE active > 0";
1013
		$sql .= " ORDER BY code ASC";
1014
1015
		dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
1016
		$resql = $this->db->query($sql);
1017
		if ($resql) {
1018
			if ($conf->use_javascript_ajax && !$forcecombo) {
1019
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1020
				$out .= ajax_combobox($htmlname, $events);
1021
			}
1022
1023
			if (!empty($page)) {
1024
				$out .= '<form method="post" action="'.$page.'">';
1025
				$out .= '<input type="hidden" name="action" value="set_incoterms">';
1026
				$out .= '<input type="hidden" name="token" value="'.newToken().'">';
1027
			}
1028
1029
			$out .= '<select id="'.$htmlname.'" class="flat selectincoterm width75" name="'.$htmlname.'" '.$htmloption.'>';
1030
			$out .= '<option value="0">&nbsp;</option>';
1031
			$num = $this->db->num_rows($resql);
1032
			$i = 0;
1033
			if ($num) {
1034
				$foundselected = false;
1035
1036
				while ($i < $num) {
1037
					$obj = $this->db->fetch_object($resql);
1038
					$incotermArray[$i]['rowid'] = $obj->rowid;
1039
					$incotermArray[$i]['code'] = $obj->code;
1040
					$i++;
1041
				}
1042
1043
				foreach ($incotermArray as $row) {
1044
					if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1045
						$out .= '<option value="'.$row['rowid'].'" selected>';
1046
					} else {
1047
						$out .= '<option value="'.$row['rowid'].'">';
1048
					}
1049
1050
					if ($row['code']) {
1051
						$out .= $row['code'];
1052
					}
1053
1054
					$out .= '</option>';
1055
				}
1056
			}
1057
			$out .= '</select>';
1058
1059
			if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $disableautocomplete seems to never exist and therefore empty should always be true.
Loading history...
1060
				$out .= ajax_multiautocompleter('location_incoterms', '', DOL_URL_ROOT.'/core/ajax/locationincoterms.php')."\n";
1061
				$moreattrib .= ' autocomplete="off"';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $moreattrib seems to be never defined.
Loading history...
1062
			}
1063
			$out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="'.$location_incoterms.'">'."\n";
1064
1065
			if (!empty($page)) {
1066
				$out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1067
			}
1068
		} else {
1069
			dol_print_error($this->db);
1070
		}
1071
1072
		return $out;
1073
	}
1074
1075
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1076
	/**
1077
	 *	Return list of types of lines (product or service)
1078
	 * 	Example: 0=product, 1=service, 9=other (for external module)
1079
	 *
1080
	 *	@param  string	$selected       Preselected type
1081
	 *	@param  string	$htmlname       Name of field in html form
1082
	 * 	@param	int		$showempty		Add an empty field
1083
	 * 	@param	int		$hidetext		Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
1084
	 * 	@param	integer	$forceall		1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service')
1085
	 *  @return	void
1086
	 */
1087
	public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1088
	{
1089
		// phpcs:enable
1090
		global $db, $langs, $user, $conf;
1091
1092
		// If product & services are enabled or both disabled.
1093
		if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled))
1094
			|| (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) {
1095
			if (empty($hidetext)) {
1096
				print $langs->trans("Type").': ';
1097
			}
1098
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1099
			if ($showempty) {
1100
				print '<option value="-1"';
1101
				if ($selected == -1) {
1102
					print ' selected';
1103
				}
1104
				print '>&nbsp;</option>';
1105
			}
1106
1107
			print '<option value="0"';
1108
			if (0 == $selected) {
1109
				print ' selected';
1110
			}
1111
			print '>'.$langs->trans("Product");
1112
1113
			print '<option value="1"';
1114
			if (1 == $selected) {
1115
				print ' selected';
1116
			}
1117
			print '>'.$langs->trans("Service");
1118
1119
			print '</select>';
1120
			print ajax_combobox('select_'.$htmlname);
1121
			//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1122
		}
1123
		if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) {
1124
			print $langs->trans("Service");
1125
			print '<input type="hidden" name="'.$htmlname.'" value="1">';
1126
		}
1127
		if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) {
1128
			print $langs->trans("Product");
1129
			print '<input type="hidden" name="'.$htmlname.'" value="0">';
1130
		}
1131
		if ($forceall < 0) {	// This should happened only for contracts when both predefined product and service are disabled.
1132
			print '<input type="hidden" name="'.$htmlname.'" value="1">'; // By default we set on service for contract. If CONTRACT_SUPPORT_PRODUCTS is set, forceall should be 1 not -1
1133
		}
1134
	}
1135
1136
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1137
	/**
1138
	 *	Load into cache cache_types_fees, array of types of fees
1139
	 *
1140
	 *	@return     int             Nb of lines loaded, <0 if KO
1141
	 */
1142
	public function load_cache_types_fees()
1143
	{
1144
		// phpcs:enable
1145
		global $langs;
1146
1147
		$num = count($this->cache_types_fees);
1148
		if ($num > 0) {
1149
			return 0; // Cache already loaded
1150
		}
1151
1152
		dol_syslog(__METHOD__, LOG_DEBUG);
1153
1154
		$langs->load("trips");
1155
1156
		$sql = "SELECT c.code, c.label";
1157
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
1158
		$sql .= " WHERE active > 0";
1159
1160
		$resql = $this->db->query($sql);
1161
		if ($resql) {
1162
			$num = $this->db->num_rows($resql);
1163
			$i = 0;
1164
1165
			while ($i < $num) {
1166
				$obj = $this->db->fetch_object($resql);
1167
1168
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1169
				$label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1170
				$this->cache_types_fees[$obj->code] = $label;
1171
				$i++;
1172
			}
1173
1174
			asort($this->cache_types_fees);
1175
1176
			return $num;
1177
		} else {
1178
			dol_print_error($this->db);
1179
			return -1;
1180
		}
1181
	}
1182
1183
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1184
	/**
1185
	 *	Return list of types of notes
1186
	 *
1187
	 *	@param	string		$selected		Preselected type
1188
	 *	@param  string		$htmlname		Name of field in form
1189
	 * 	@param	int			$showempty		Add an empty field
1190
	 * 	@return	void
1191
	 */
1192
	public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1193
	{
1194
		// phpcs:enable
1195
		global $user, $langs;
1196
1197
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1198
1199
		$this->load_cache_types_fees();
1200
1201
		print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1202
		if ($showempty) {
1203
			print '<option value="-1"';
1204
			if ($selected == -1) {
1205
				print ' selected';
1206
			}
1207
			print '>&nbsp;</option>';
1208
		}
1209
1210
		foreach ($this->cache_types_fees as $key => $value) {
1211
			print '<option value="'.$key.'"';
1212
			if ($key == $selected) {
1213
				print ' selected';
1214
			}
1215
			print '>';
1216
			print $value;
1217
			print '</option>';
1218
		}
1219
1220
		print '</select>';
1221
		if ($user->admin) {
1222
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1223
		}
1224
	}
1225
1226
1227
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1228
	/**
1229
	 *  Output html form to select a third party
1230
	 *
1231
	 *	@param	string	$selected       		Preselected type
1232
	 *	@param  string	$htmlname       		Name of field in form
1233
	 *  @param  string	$filter         		Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here (example: 's.rowid <> x', 's.client IN (1,3)')
1234
	 *	@param	string	$showempty				Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
1235
	 * 	@param	int		$showtype				Show third party type in combolist (customer, prospect or supplier)
1236
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
1237
	 *  @param	array	$events					Ajax event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1238
	 *	@param	int		$limit					Maximum number of elements
1239
	 *  @param	string	$morecss				Add more css styles to the SELECT component
1240
	 *	@param  string	$moreparam      		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1241
	 *	@param	string	$selected_input_value	Value of preselected input text (for use with ajax)
1242
	 *  @param	int		$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1243
	 *  @param	array	$ajaxoptions			Options for ajax_autocompleter
1244
	 * 	@param  bool	$multiple				add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
1245
	 *  @param	array	$excludeids				Exclude IDs from the select combo
1246
	 * 	@return	string							HTML string with select box for thirdparty.
1247
	 */
1248
	public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false, $excludeids = array())
1249
	{
1250
		// phpcs:enable
1251
		global $conf, $user, $langs;
1252
1253
		$out = '';
1254
1255
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1256
			if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
1257
				$ajaxoptions = array();
1258
			}
1259
1260
			require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1261
1262
			// No immediate load of all database
1263
			$placeholder = '';
1264
			if ($selected && empty($selected_input_value)) {
1265
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1266
				$societetmp = new Societe($this->db);
1267
				$societetmp->fetch($selected);
1268
				$selected_input_value = $societetmp->name;
1269
				unset($societetmp);
1270
			}
1271
			// mode 1
1272
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '');
1273
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1274
1275
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1276
			if (empty($hidelabel)) {
1277
				print $langs->trans("RefOrLabel").' : ';
1278
			} elseif ($hidelabel > 1) {
1279
				$placeholder = $langs->trans("RefOrLabel");
1280
				if ($hidelabel == 2) {
1281
					$out .= img_picto($langs->trans("Search"), 'search');
1282
				}
1283
			}
1284
			$out .= '<input type="text" class="'.$morecss.'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '').' '.(!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
1285
			if ($hidelabel == 3) {
1286
				$out .= img_picto($langs->trans("Search"), 'search');
1287
			}
1288
		} else {
1289
			// Immediate load of all database
1290
			$out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids);
1291
		}
1292
1293
		return $out;
1294
	}
1295
1296
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1297
	/**
1298
	 *  Output html form to select a third party.
1299
	 *  Note, you must use the select_company to get the component to select a third party. This function must only be called by select_company.
1300
	 *
1301
	 *	@param	string	$selected       Preselected type
1302
	 *	@param  string	$htmlname       Name of field in form
1303
	 *  @param  string	$filter         Optional filters criteras (example: 's.rowid NOT IN (x)', 's.client IN (1,3)'). Do not use a filter coming from input of users.
1304
	 *	@param	string	$showempty		Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
1305
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
1306
	 * 	@param	int		$forcecombo		Force to use standard HTML select component without beautification
1307
	 *  @param	array	$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1308
	 *  @param	string	$filterkey		Filter on key value
1309
	 *  @param	int		$outputmode		0=HTML select string, 1=Array
1310
	 *  @param	int		$limit			Limit number of answers
1311
	 *  @param	string	$morecss		Add more css styles to the SELECT component
1312
	 *	@param  string	$moreparam      Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1313
	 *	@param  bool	$multiple       add [] in the name of element and add 'multiple' attribut
1314
	 *  @param	array	$excludeids		Exclude IDs from the select combo
1315
	 * 	@return	string					HTML string with
1316
	 */
1317
	public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false, $excludeids = array())
1318
	{
1319
		// phpcs:enable
1320
		global $conf, $user, $langs;
1321
1322
		$out = '';
1323
		$num = 0;
1324
		$outarray = array();
1325
1326
		if ($selected === '') {
1327
			$selected = array();
1328
		} elseif (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
1329
			$selected = array($selected);
1330
		}
1331
1332
		// Clean $filter that may contains sql conditions so sql code
1333
		if (function_exists('testSqlAndScriptInject')) {
1334
			if (testSqlAndScriptInject($filter, 3) > 0) {
1335
				$filter = '';
1336
			}
1337
		}
1338
1339
		// We search companies
1340
		$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1341
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1342
			$sql .= ", s.address, s.zip, s.town";
1343
			$sql .= ", dictp.code as country_code";
1344
		}
1345
		$sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
1346
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1347
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid = s.fk_pays";
1348
		}
1349
		if (!$user->rights->societe->client->voir && !$user->socid) {
1350
			$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1351
		}
1352
		$sql .= " WHERE s.entity IN (".getEntity('societe').")";
1353
		if (!empty($user->socid)) {
1354
			$sql .= " AND s.rowid = ".((int) $user->socid);
1355
		}
1356
		if ($filter) {
1357
			$sql .= " AND (".$filter.")";
1358
		}
1359
		if (!$user->rights->societe->client->voir && !$user->socid) {
1360
			$sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1361
		}
1362
		if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1363
			$sql .= " AND s.status <> 0";
1364
		}
1365
		if (!empty($excludeids)) {
1366
			$sql .= " AND s.rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1367
		}
1368
		// Add criteria
1369
		if ($filterkey && $filterkey != '') {
1370
			$sql .= " AND (";
1371
			$prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1372
			// For natural search
1373
			$scrit = explode(' ', $filterkey);
1374
			$i = 0;
1375
			if (count($scrit) > 1) {
1376
				$sql .= "(";
1377
			}
1378
			foreach ($scrit as $crit) {
1379
				if ($i > 0) {
1380
					$sql .= " AND ";
1381
				}
1382
				$sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1383
				$i++;
1384
			}
1385
			if (count($scrit) > 1) {
1386
				$sql .= ")";
1387
			}
1388
			if (!empty($conf->barcode->enabled)) {
1389
				$sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1390
			}
1391
			$sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1392
			$sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1393
			$sql .= ")";
1394
		}
1395
		$sql .= $this->db->order("nom", "ASC");
1396
		$sql .= $this->db->plimit($limit, 0);
1397
1398
		// Build output string
1399
		dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1400
		$resql = $this->db->query($sql);
1401
		if ($resql) {
1402
			if (!$forcecombo) {
1403
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1404
				$out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1405
			}
1406
1407
			// Construct $out and $outarray
1408
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1409
1410
			$textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1411
			if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1412
				// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1413
				//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1414
				if ($showempty && !is_numeric($showempty)) {
1415
					$textifempty = $langs->trans($showempty);
1416
				} else {
1417
					$textifempty .= $langs->trans("All");
1418
				}
1419
			}
1420
			if ($showempty) {
1421
				$out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.($textifempty ? $textifempty : '&nbsp;').'</span>').'">'.$textifempty.'</option>'."\n";
1422
			}
1423
1424
			$num = $this->db->num_rows($resql);
1425
			$i = 0;
1426
			if ($num) {
1427
				while ($i < $num) {
1428
					$obj = $this->db->fetch_object($resql);
1429
					$label = '';
1430
					if ($conf->global->SOCIETE_ADD_REF_IN_LIST) {
1431
						if (($obj->client) && (!empty($obj->code_client))) {
1432
							$label = $obj->code_client.' - ';
1433
						}
1434
						if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1435
							$label .= $obj->code_fournisseur.' - ';
1436
						}
1437
						$label .= ' '.$obj->name;
1438
					} else {
1439
						$label = $obj->name;
1440
					}
1441
1442
					if (!empty($obj->name_alias)) {
1443
						$label .= ' ('.$obj->name_alias.')';
1444
					}
1445
1446
					if ($showtype) {
1447
						if ($obj->client || $obj->fournisseur) {
1448
							$label .= ' (';
1449
						}
1450
						if ($obj->client == 1 || $obj->client == 3) {
1451
							$label .= $langs->trans("Customer");
1452
						}
1453
						if ($obj->client == 2 || $obj->client == 3) {
1454
							$label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1455
						}
1456
						if ($obj->fournisseur) {
1457
							$label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1458
						}
1459
						if ($obj->client || $obj->fournisseur) {
1460
							$label .= ')';
1461
						}
1462
					}
1463
1464
					if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1465
						$label .= ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1466
						if (!empty($obj->country_code)) {
1467
							$label .= ', '.$langs->trans('Country'.$obj->country_code);
1468
						}
1469
					}
1470
1471
					if (empty($outputmode)) {
1472
						if (in_array($obj->rowid, $selected)) {
1473
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
1474
						} else {
1475
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
1476
						}
1477
					} else {
1478
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
1479
					}
1480
1481
					$i++;
1482
					if (($i % 10) == 0) {
1483
						$out .= "\n";
1484
					}
1485
				}
1486
			}
1487
			$out .= '</select>'."\n";
1488
		} else {
1489
			dol_print_error($this->db);
1490
		}
1491
1492
		$this->result = array('nbofthirdparties'=>$num);
1493
1494
		if ($outputmode) {
1495
			return $outarray;
1496
		}
1497
		return $out;
1498
	}
1499
1500
1501
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1502
	/**
1503
	 *  Return HTML combo list of absolute discounts
1504
	 *
1505
	 *  @param	string	$selected       Id remise fixe pre-selectionnee
1506
	 *  @param  string	$htmlname       Nom champ formulaire
1507
	 *  @param  string	$filter         Criteres optionnels de filtre
1508
	 *  @param	int		$socid			Id of thirdparty
1509
	 *  @param	int		$maxvalue		Max value for lines that can be selected
1510
	 *  @return	int						Return number of qualifed lines in list
1511
	 */
1512
	public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1513
	{
1514
		// phpcs:enable
1515
		global $langs, $conf;
1516
1517
		// On recherche les remises
1518
		$sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1519
		$sql .= " re.description, re.fk_facture_source";
1520
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1521
		$sql .= " WHERE re.fk_soc = ".(int) $socid;
1522
		$sql .= " AND re.entity = ".$conf->entity;
1523
		if ($filter) {
1524
			$sql .= " AND ".$filter;
1525
		}
1526
		$sql .= " ORDER BY re.description ASC";
1527
1528
		dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1529
		$resql = $this->db->query($sql);
1530
		if ($resql) {
1531
			print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1532
			$num = $this->db->num_rows($resql);
1533
1534
			$qualifiedlines = $num;
1535
1536
			$i = 0;
1537
			if ($num) {
1538
				print '<option value="0">&nbsp;</option>';
1539
				while ($i < $num) {
1540
					$obj = $this->db->fetch_object($resql);
1541
					$desc = dol_trunc($obj->description, 40);
1542
					if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
1543
						$desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1544
					}
1545
					if (preg_match('/\(DEPOSIT\)/', $desc)) {
1546
						$desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1547
					}
1548
					if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
1549
						$desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1550
					}
1551
					if (preg_match('/\(EXCESS PAID\)/', $desc)) {
1552
						$desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1553
					}
1554
1555
					$selectstring = '';
1556
					if ($selected > 0 && $selected == $obj->rowid) {
1557
						$selectstring = ' selected';
1558
					}
1559
1560
					$disabled = '';
1561
					if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1562
						$qualifiedlines--;
1563
						$disabled = ' disabled';
1564
					}
1565
1566
					if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1567
						$tmpfac = new Facture($this->db);
1568
						if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1569
							$desc = $desc.' - '.$tmpfac->ref;
1570
						}
1571
					}
1572
1573
					print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1574
					$i++;
1575
				}
1576
			}
1577
			print '</select>';
1578
			return $qualifiedlines;
1579
		} else {
1580
			dol_print_error($this->db);
1581
			return -1;
1582
		}
1583
	}
1584
1585
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1586
	/**
1587
	 *  Return list of all contacts (for a third party or all)
1588
	 *
1589
	 *  @param	int		$socid      	Id ot third party or 0 for all
1590
	 *  @param  string	$selected   	Id contact pre-selectionne
1591
	 *  @param  string	$htmlname  	    Name of HTML field ('none' for a not editable field)
1592
	 *  @param  int		$showempty      0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list
1593
	 *  @param  string	$exclude        List of contacts id to exclude
1594
	 *  @param	string	$limitto		Disable answers that are not id in this array list
1595
	 *  @param	integer	$showfunction   Add function into label
1596
	 *  @param	string	$moreclass		Add more class to class style
1597
	 *  @param	integer	$showsoc	    Add company into label
1598
	 *  @param	int		$forcecombo		Force to use combo box
1599
	 *  @param	array	$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1600
	 *  @param	bool	$options_only	Return options only (for ajax treatment)
1601
	 *  @param	string	$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1602
	 *  @param	string	$htmlid			Html id to use instead of htmlname
1603
	 *  @return	int						<0 if KO, Nb of contact in list if OK
1604
	 *  @deprecated						You can use selectcontacts directly (warning order of param was changed)
1605
	 */
1606
	public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '')
1607
	{
1608
		// phpcs:enable
1609
		print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1610
		return $this->num;
1611
	}
1612
1613
	/**
1614
	 *	Return HTML code of the SELECT of list of all contacts (for a third party or all).
1615
	 *  This also set the number of contacts found into $this->num
1616
	 *
1617
	 * @since 9.0 Add afterSelectContactOptions hook
1618
	 *
1619
	 *	@param	int			$socid      	Id ot third party or 0 for all or -1 for empty list
1620
	 *	@param  array|int	$selected   	Array of ID of pre-selected contact id
1621
	 *	@param  string		$htmlname  	    Name of HTML field ('none' for a not editable field)
1622
	 *	@param  int			$showempty     	0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list
1623
	 *	@param  string		$exclude        List of contacts id to exclude
1624
	 *	@param	string		$limitto		Disable answers that are not id in this array list
1625
	 *	@param	integer		$showfunction   Add function into label
1626
	 *	@param	string		$moreclass		Add more class to class style
1627
	 *	@param	bool		$options_only	Return options only (for ajax treatment)
1628
	 *	@param	integer		$showsoc	    Add company into label
1629
	 * 	@param	int			$forcecombo		Force to use combo box (so no ajax beautify effect)
1630
	 *  @param	array		$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1631
	 *  @param	string		$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1632
	 *  @param	string		$htmlid			Html id to use instead of htmlname
1633
	 *  @param	bool		$multiple		add [] in the name of element and add 'multiple' attribut
1634
	 *  @param	integer		$disableifempty Set tag 'disabled' on select if there is no choice
1635
	 *	@return	 int|string					<0 if KO, HTML with select string if OK.
1636
	 */
1637
	public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0)
1638
	{
1639
		global $conf, $langs, $hookmanager, $action;
1640
1641
		$langs->load('companies');
1642
1643
		if (empty($htmlid)) {
1644
			$htmlid = $htmlname;
1645
		}
1646
		$num = 0;
1647
1648
		if ($selected === '') {
1649
			$selected = array();
1650
		} elseif (!is_array($selected)) {
1651
			$selected = array($selected);
1652
		}
1653
		$out = '';
1654
1655
		if (!is_object($hookmanager)) {
1656
			include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1657
			$hookmanager = new HookManager($this->db);
1658
		}
1659
1660
		// We search third parties
1661
		$sql = "SELECT sp.rowid, sp.lastname, sp.statut, sp.firstname, sp.poste, sp.email, sp.phone, sp.phone_perso, sp.phone_mobile, sp.town AS contact_town";
1662
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1663
			$sql .= ", s.nom as company, s.town AS company_town";
1664
		}
1665
		$sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp";
1666
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1667
			$sql .= " LEFT OUTER JOIN  ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc";
1668
		}
1669
		$sql .= " WHERE sp.entity IN (".getEntity('socpeople').")";
1670
		if ($socid > 0 || $socid == -1) {
1671
			$sql .= " AND sp.fk_soc = ".((int) $socid);
1672
		}
1673
		if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1674
			$sql .= " AND sp.statut <> 0";
1675
		}
1676
		$sql .= " ORDER BY sp.lastname ASC";
1677
1678
		dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1679
		$resql = $this->db->query($sql);
1680
		if ($resql) {
1681
			$num = $this->db->num_rows($resql);
1682
1683
			if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1684
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1685
				$out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1686
			}
1687
1688
			if ($htmlname != 'none' && !$options_only) {
1689
				$out .= '<select class="flat'.($moreclass ? ' '.$moreclass : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1690
			}
1691
1692
			if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
1693
				$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1694
			}
1695
			if ($showempty == 2) {
1696
				$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1697
			}
1698
1699
			$i = 0;
1700
			if ($num) {
1701
				include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1702
				$contactstatic = new Contact($this->db);
1703
1704
				while ($i < $num) {
1705
					$obj = $this->db->fetch_object($resql);
1706
1707
					// Set email (or phones) and town extended infos
1708
					$extendedInfos = '';
1709
					if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1710
						$extendedInfos = array();
1711
						$email = trim($obj->email);
1712
						if (!empty($email)) {
1713
							$extendedInfos[] = $email;
1714
						} else {
1715
							$phone = trim($obj->phone);
1716
							$phone_perso = trim($obj->phone_perso);
1717
							$phone_mobile = trim($obj->phone_mobile);
1718
							if (!empty($phone)) {
1719
								$extendedInfos[] = $phone;
1720
							}
1721
							if (!empty($phone_perso)) {
1722
								$extendedInfos[] = $phone_perso;
1723
							}
1724
							if (!empty($phone_mobile)) {
1725
								$extendedInfos[] = $phone_mobile;
1726
							}
1727
						}
1728
						$contact_town = trim($obj->contact_town);
1729
						$company_town = trim($obj->company_town);
1730
						if (!empty($contact_town)) {
1731
							$extendedInfos[] = $contact_town;
1732
						} elseif (!empty($company_town)) {
1733
							$extendedInfos[] = $company_town;
1734
						}
1735
						$extendedInfos = implode(' - ', $extendedInfos);
1736
						if (!empty($extendedInfos)) {
1737
							$extendedInfos = ' - '.$extendedInfos;
1738
						}
1739
					}
1740
1741
					$contactstatic->id = $obj->rowid;
1742
					$contactstatic->lastname = $obj->lastname;
1743
					$contactstatic->firstname = $obj->firstname;
1744
					if ($obj->statut == 1) {
1745
						if ($htmlname != 'none') {
1746
							$disabled = 0;
1747
							if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1748
								$disabled = 1;
1749
							}
1750
							if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1751
								$disabled = 1;
1752
							}
1753
							if (!empty($selected) && in_array($obj->rowid, $selected)) {
1754
								$out .= '<option value="'.$obj->rowid.'"';
1755
								if ($disabled) {
1756
									$out .= ' disabled';
1757
								}
1758
								$out .= ' selected>';
1759
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1760
								if ($showfunction && $obj->poste) {
1761
									$out .= ' ('.$obj->poste.')';
1762
								}
1763
								if (($showsoc > 0) && $obj->company) {
1764
									$out .= ' - ('.$obj->company.')';
1765
								}
1766
								$out .= '</option>';
1767
							} else {
1768
								$out .= '<option value="'.$obj->rowid.'"';
1769
								if ($disabled) {
1770
									$out .= ' disabled';
1771
								}
1772
								$out .= '>';
1773
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1774
								if ($showfunction && $obj->poste) {
1775
									$out .= ' ('.$obj->poste.')';
1776
								}
1777
								if (($showsoc > 0) && $obj->company) {
1778
									$out .= ' - ('.$obj->company.')';
1779
								}
1780
								$out .= '</option>';
1781
							}
1782
						} else {
1783
							if (in_array($obj->rowid, $selected)) {
1784
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1785
								if ($showfunction && $obj->poste) {
1786
									$out .= ' ('.$obj->poste.')';
1787
								}
1788
								if (($showsoc > 0) && $obj->company) {
1789
									$out .= ' - ('.$obj->company.')';
1790
								}
1791
							}
1792
						}
1793
					}
1794
					$i++;
1795
				}
1796
			} else {
1797
				$labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1798
				$out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1799
				$out .= $labeltoshow;
1800
				$out .= '</option>';
1801
			}
1802
1803
			$parameters = array(
1804
				'socid'=>$socid,
1805
				'htmlname'=>$htmlname,
1806
				'resql'=>$resql,
1807
				'out'=>&$out,
1808
				'showfunction'=>$showfunction,
1809
				'showsoc'=>$showsoc,
1810
			);
1811
1812
			$reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1813
1814
			if ($htmlname != 'none' && !$options_only) {
1815
				$out .= '</select>';
1816
			}
1817
1818
			$this->num = $num;
1819
			return $out;
1820
		} else {
1821
			dol_print_error($this->db);
1822
			return -1;
1823
		}
1824
	}
1825
1826
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1827
	/**
1828
	 *	Return the HTML select list of users
1829
	 *
1830
	 *  @param	string			$selected       Id user preselected
1831
	 *  @param  string			$htmlname       Field name in form
1832
	 *  @param  int				$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
1833
	 *  @param  array			$exclude        Array list of users id to exclude
1834
	 * 	@param	int				$disabled		If select list must be disabled
1835
	 *  @param  array|string	$include        Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me
1836
	 * 	@param	int				$enableonly		Array list of users id to be enabled. All other must be disabled
1837
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1838
	 * 	@return	void
1839
	 *  @deprecated		Use select_dolusers instead
1840
	 *  @see select_dolusers()
1841
	 */
1842
	public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1843
	{
1844
		// phpcs:enable
1845
		print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1846
	}
1847
1848
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1849
	/**
1850
	 *	Return select list of users
1851
	 *
1852
	 *  @param	string			$selected       User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed)
1853
	 *  @param  string			$htmlname       Field name in form
1854
	 *  @param  int|string		$show_empty     0=list with no empty value, 1=add also an empty value into list
1855
	 *  @param  array			$exclude        Array list of users id to exclude
1856
	 * 	@param	int				$disabled		If select list must be disabled
1857
	 *  @param  array|string	$include        Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me
1858
	 * 	@param	array			$enableonly		Array list of users id to be enabled. If defined, it means that others will be disabled
1859
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1860
	 *  @param	int				$maxlength		Maximum length of string into list (0=no limit)
1861
	 *  @param	int				$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
1862
	 *  @param	string			$morefilter		Add more filters into sql request (Example: 'employee = 1'). This value must not come from user input.
1863
	 *  @param	integer			$show_every		0=default list, 1=add also a value "Everybody" at beginning of list
1864
	 *  @param	string			$enableonlytext	If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty.
1865
	 *  @param	string			$morecss		More css
1866
	 *  @param  int     		$noactive       Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
1867
	 *  @param  int				$outputmode     0=HTML select string, 1=Array
1868
	 *  @param  bool			$multiple       add [] in the name of element and add 'multiple' attribut
1869
	 * 	@return	string							HTML select string
1870
	 *  @see select_dolgroups()
1871
	 */
1872
	public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $noactive = 0, $outputmode = 0, $multiple = false)
1873
	{
1874
		// phpcs:enable
1875
		global $conf, $user, $langs, $hookmanager;
1876
1877
		// If no preselected user defined, we take current user
1878
		if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1879
			$selected = $user->id;
1880
		}
1881
1882
		if ($selected === '') {
1883
			$selected = array();
1884
		} elseif (!is_array($selected)) {
1885
			$selected = array($selected);
1886
		}
1887
1888
		$excludeUsers = null;
1889
		$includeUsers = null;
1890
1891
		// Permettre l'exclusion d'utilisateurs
1892
		if (is_array($exclude)) {
1893
			$excludeUsers = implode(",", $exclude);
1894
		}
1895
		// Permettre l'inclusion d'utilisateurs
1896
		if (is_array($include)) {
1897
			$includeUsers = implode(",", $include);
1898
		} elseif ($include == 'hierarchy') {
1899
			// Build list includeUsers to have only hierarchy
1900
			$includeUsers = implode(",", $user->getAllChildIds(0));
1901
		} elseif ($include == 'hierarchyme') {
1902
			// Build list includeUsers to have only hierarchy and current user
1903
			$includeUsers = implode(",", $user->getAllChildIds(1));
1904
		}
1905
1906
		$out = '';
1907
		$outarray = array();
1908
1909
		// Forge request to select users
1910
		$sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1911
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1912
			$sql .= ", e.label";
1913
		}
1914
		$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
1915
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1916
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid = u.entity";
1917
			if ($force_entity) {
1918
				$sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
1919
			} else {
1920
				$sql .= " WHERE u.entity IS NOT NULL";
1921
			}
1922
		} else {
1923
			if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1924
				$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug";
1925
				$sql .= " ON ug.fk_user = u.rowid";
1926
				$sql .= " WHERE ug.entity = ".$conf->entity;
1927
			} else {
1928
				$sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
1929
			}
1930
		}
1931
		if (!empty($user->socid)) {
1932
			$sql .= " AND u.fk_soc = ".((int) $user->socid);
1933
		}
1934
		if (is_array($exclude) && $excludeUsers) {
1935
			$sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
1936
		}
1937
		if ($includeUsers) {
1938
			$sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
1939
		}
1940
		if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) {
1941
			$sql .= " AND u.statut <> 0";
1942
		}
1943
		if (!empty($morefilter)) {
1944
			$sql .= " ".$morefilter;
1945
		}
1946
1947
		//Add hook to filter on user (for exemple on usergroup define in custom modules)
1948
		$reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $action seems to be never defined.
Loading history...
1949
		if (!empty($reshook)) {
1950
			$sql .= $hookmanager->resPrint;
1951
		}
1952
1953
		if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {	// MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
1954
			$sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
1955
		} else {
1956
			$sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
1957
		}
1958
1959
		dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
1960
1961
		$resql = $this->db->query($sql);
1962
		if ($resql) {
1963
			$num = $this->db->num_rows($resql);
1964
			$i = 0;
1965
			if ($num) {
1966
				// do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
1967
				$out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
1968
				if ($show_empty && !$multiple) {
1969
					$textforempty = ' ';
1970
					if (!empty($conf->use_javascript_ajax)) {
1971
						$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
1972
					}
1973
					if (!is_numeric($show_empty)) {
1974
						$textforempty = $show_empty;
1975
					}
1976
					$out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
1977
				}
1978
				if ($show_every) {
1979
					$out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
1980
				}
1981
1982
				$userstatic = new User($this->db);
1983
1984
				while ($i < $num) {
1985
					$obj = $this->db->fetch_object($resql);
1986
1987
					$userstatic->id = $obj->rowid;
1988
					$userstatic->lastname = $obj->lastname;
1989
					$userstatic->firstname = $obj->firstname;
1990
					$userstatic->photo = $obj->photo;
1991
					$userstatic->statut = $obj->status;
1992
					$userstatic->entity = $obj->entity;
1993
					$userstatic->admin = $obj->admin;
1994
1995
					$disableline = '';
1996
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
1997
						$disableline = ($enableonlytext ? $enableonlytext : '1');
1998
					}
1999
2000
					$labeltoshow = '';
2001
2002
					// $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2003
					$fullNameMode = 0;
2004
					if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2005
						$fullNameMode = 1; //Firstname+lastname
2006
					}
2007
					$labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2008
					if (empty($obj->firstname) && empty($obj->lastname)) {
2009
						$labeltoshow .= $obj->login;
2010
					}
2011
2012
					// Complete name with more info
2013
					$moreinfo = '';
2014
					if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2015
						$moreinfo .= ($moreinfo ? ' - ' : ' (').$obj->login;
2016
					}
2017
					if ($showstatus >= 0) {
2018
						if ($obj->status == 1 && $showstatus == 1) {
2019
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2020
						}
2021
						if ($obj->status == 0 && $showstatus == 1) {
2022
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2023
						}
2024
					}
2025
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2026
						if (!$obj->entity) {
2027
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2028
						} else {
2029
							if ($obj->entity != $conf->entity) {
2030
								$moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2031
							}
2032
						}
2033
					}
2034
					$moreinfo .= ($moreinfo ? ')' : '');
2035
					if ($disableline && $disableline != '1') {
2036
						$moreinfo .= ' - '.$disableline; // This is text from $enableonlytext parameter
2037
					}
2038
					$labeltoshow .= $moreinfo;
2039
2040
					$out .= '<option value="'.$obj->rowid.'"';
2041
					if ($disableline) {
2042
						$out .= ' disabled';
2043
					}
2044
					if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2045
						$out .= ' selected';
2046
					}
2047
					$out .= ' data-html="';
2048
					$outhtml = '';
2049
					// if (!empty($obj->photo)) {
2050
					$outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2051
					// }
2052
					if ($showstatus >= 0 && $obj->status == 0) {
2053
						$outhtml .= '<strike class="opacitymediumxxx">';
2054
					}
2055
					$outhtml .= $labeltoshow;
2056
					if ($showstatus >= 0 && $obj->status == 0) {
2057
						$outhtml .= '</strike>';
2058
					}
2059
					$out .= dol_escape_htmltag($outhtml);
2060
					$out .= '">';
2061
					$out .= $labeltoshow;
2062
					$out .= '</option>';
2063
2064
					$outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2065
2066
					$i++;
2067
				}
2068
			} else {
2069
				$out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2070
				$out .= '<option value="">'.$langs->trans("None").'</option>';
2071
			}
2072
			$out .= '</select>';
2073
2074
			if ($num) {
2075
				// Enhance with select2
2076
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2077
				$out .= ajax_combobox($htmlname);
2078
			}
2079
		} else {
2080
			dol_print_error($this->db);
2081
		}
2082
2083
		if ($outputmode) {
2084
			return $outarray;
2085
		}
2086
		return $out;
2087
	}
2088
2089
2090
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2091
	/**
2092
	 *	Return select list of users. Selected users are stored into session.
2093
	 *  List of users are provided into $_SESSION['assignedtouser'].
2094
	 *
2095
	 *  @param  string	$action         Value for $action
2096
	 *  @param  string	$htmlname       Field name in form
2097
	 *  @param  int		$show_empty     0=list without the empty value, 1=add empty value
2098
	 *  @param  array	$exclude        Array list of users id to exclude
2099
	 * 	@param	int		$disabled		If select list must be disabled
2100
	 *  @param  array	$include        Array list of users id to include or 'hierarchy' to have only supervised users
2101
	 * 	@param	array	$enableonly		Array list of users id to be enabled. All other must be disabled
2102
	 *  @param	int		$force_entity	'0' or Ids of environment to force
2103
	 *  @param	int		$maxlength		Maximum length of string into list (0=no limit)
2104
	 *  @param	int		$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2105
	 *  @param	string	$morefilter		Add more filters into sql request
2106
	 *  @param	int		$showproperties		Show properties of each attendees
2107
	 *  @param	array	$listofuserid		Array with properties of each user
2108
	 *  @param	array	$listofcontactid	Array with properties of each contact
2109
	 *  @param	array	$listofotherid		Array with properties of each other contact
2110
	 * 	@return	string					HTML select string
2111
	 *  @see select_dolgroups()
2112
	 */
2113
	public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
2114
	{
2115
		// phpcs:enable
2116
		global $conf, $user, $langs;
2117
2118
		$userstatic = new User($this->db);
2119
		$out = '';
2120
2121
2122
		$assignedtouser = array();
2123
		if (!empty($_SESSION['assignedtouser'])) {
2124
			$assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2125
		}
2126
		$nbassignetouser = count($assignedtouser);
2127
2128
		//if ($nbassignetouser && $action != 'view') $out .= '<br>';
2129
		if ($nbassignetouser) {
2130
			$out .= '<ul class="attendees">';
2131
		}
2132
		$i = 0;
2133
		$ownerid = 0;
2134
		foreach ($assignedtouser as $key => $value) {
2135
			if ($value['id'] == $ownerid) {
2136
				continue;
2137
			}
2138
2139
			$out .= '<li>';
2140
			$userstatic->fetch($value['id']);
2141
			$out .= $userstatic->getNomUrl(-1);
2142
			if ($i == 0) {
2143
				$ownerid = $value['id'];
2144
				$out .= ' ('.$langs->trans("Owner").')';
2145
			}
2146
			if ($nbassignetouser > 1 && $action != 'view') {
2147
				$out .= ' <input type="image" style="border: 0px;" src="'.img_picto($langs->trans("Remove"), 'delete', '', 0, 1).'" value="'.$userstatic->id.'" class="removedassigned reposition" id="removedassigned_'.$userstatic->id.'" name="removedassigned_'.$userstatic->id.'">';
2148
			}
2149
			// Show my availability
2150
			if ($showproperties) {
2151
				if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2152
					$out .= '<div class="myavailability inline-block">';
2153
					$out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;<span class="opacitymedium">'.$langs->trans("Availability").':</span>  </span><input id="transparency" class="paddingrightonly" '.($action == 'view' ? 'disabled' : '').' type="checkbox" name="transparency"'.($listofuserid[$ownerid]['transparency'] ? ' checked' : '').'><label for="transparency">'.$langs->trans("Busy").'</label>';
2154
					$out .= '</div>';
2155
				}
2156
			}
2157
			//$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2158
			//$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2159
2160
			$out .= '</li>';
2161
			$i++;
2162
		}
2163
		if ($nbassignetouser) {
2164
			$out .= '</ul>';
2165
		}
2166
2167
		// Method with no ajax
2168
		if ($action != 'view') {
2169
			$out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2170
			$out .= '<script type="text/javascript" language="javascript">jQuery(document).ready(function () {';
2171
			$out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2172
			$out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2173
			$out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2174
			$out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2175
			$out .= '});';
2176
			$out .= '})</script>';
2177
			$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2178
			$out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2179
			$out .= '<br>';
2180
		}
2181
2182
		return $out;
2183
	}
2184
2185
2186
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2187
	/**
2188
	 *  Return list of products for customer in Ajax if Ajax activated or go to select_produits_list
2189
	 *
2190
	 *  @param		int			$selected				Preselected products
2191
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
2192
	 *  @param		int|string	$filtertype				Filter on product type (''=nofilter, 0=product, 1=service)
2193
	 *  @param		int			$limit					Limit on number of returned lines
2194
	 *  @param		int			$price_level			Level of price to show
2195
	 *  @param		int			$status					Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell
2196
	 *  @param		int			$finished				2=all, 1=finished, 0=raw material
2197
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
2198
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
2199
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
2200
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
2201
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2202
	 * 	@param		int			$forcecombo				Force to use combo box
2203
	 *  @param      string      $morecss                Add more css on select
2204
	 *  @param      int         $hidepriceinlabel       1=Hide prices in label
2205
	 *  @param      string      $warehouseStatus        Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used
2206
	 *										            'warehouseopen' = count products from open warehouses,
2207
	 *										            'warehouseclosed' = count products from closed warehouses,
2208
	 *										            'warehouseinternal' = count products from warehouses for internal correct/transfer only
2209
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
2210
	 *  @param		string		$nooutput				No print, return the output into a string
2211
	 *  @return		void|string
2212
	 */
2213
	public function select_produits($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 0, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = null, $nooutput = 0)
2214
	{
2215
		// phpcs:enable
2216
		global $langs, $conf;
2217
2218
		$out = '';
2219
2220
		// check parameters
2221
		$price_level = (!empty($price_level) ? $price_level : 0);
2222
		if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
2223
			$ajaxoptions = array();
2224
		}
2225
2226
		if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) {
2227
			if (!empty($conf->product->enabled) && empty($conf->service->enabled)) {
2228
				$filtertype = '0';
2229
			} elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) {
2230
				$filtertype = '1';
2231
			}
2232
		}
2233
2234
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2235
			$placeholder = '';
2236
2237
			if ($selected && empty($selected_input_value)) {
2238
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2239
				$producttmpselect = new Product($this->db);
2240
				$producttmpselect->fetch($selected);
2241
				$selected_input_value = $producttmpselect->ref;
2242
				unset($producttmpselect);
2243
			}
2244
			// handle case where product or service module is disabled + no filter specified
2245
			if ($filtertype == '') {
2246
				if (empty($conf->product->enabled)) { // when product module is disabled, show services only
2247
					$filtertype = 1;
2248
				} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2249
					$filtertype = 0;
2250
				}
2251
			}
2252
			// mode=1 means customers products
2253
			$urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
2254
			//Price by customer
2255
			if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2256
				$urloption .= '&socid='.$socid;
2257
			}
2258
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2259
2260
			if (!empty($conf->variants->enabled) && is_array($selected_combinations)) {
2261
				// Code to automatically insert with javascript the select of attributes under the select of product
2262
				// when a parent of variant has been selected.
2263
				$out .= '
2264
				<!-- script to auto show attributes select tags if a variant was selected -->
2265
				<script>
2266
					// auto show attributes fields
2267
					selected = '.json_encode($selected_combinations).';
2268
					combvalues = {};
2269
2270
					jQuery(document).ready(function () {
2271
2272
						jQuery("input[name=\'prod_entry_mode\']").change(function () {
2273
							if (jQuery(this).val() == \'free\') {
2274
								jQuery(\'div#attributes_box\').empty();
2275
							}
2276
						});
2277
2278
						jQuery("input#'.$htmlname.'").change(function () {
2279
2280
							if (!jQuery(this).val()) {
2281
								jQuery(\'div#attributes_box\').empty();
2282
								return;
2283
							}
2284
2285
							console.log("A change has started. We get variants fields to inject html select");
2286
2287
							jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2288
								id: jQuery(this).val()
2289
							}, function (data) {
2290
								jQuery(\'div#attributes_box\').empty();
2291
2292
								jQuery.each(data, function (key, val) {
2293
2294
									combvalues[val.id] = val.values;
2295
2296
									var span = jQuery(document.createElement(\'div\')).css({
2297
										\'display\': \'table-row\'
2298
									});
2299
2300
									span.append(
2301
										jQuery(document.createElement(\'div\')).text(val.label).css({
2302
											\'font-weight\': \'bold\',
2303
											\'display\': \'table-cell\'
2304
										})
2305
									);
2306
2307
									var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2308
										\'margin-left\': \'15px\',
2309
										\'white-space\': \'pre\'
2310
									}).append(
2311
										jQuery(document.createElement(\'option\')).val(\'\')
2312
									);
2313
2314
									jQuery.each(combvalues[val.id], function (key, val) {
2315
										var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2316
2317
										if (selected[val.fk_product_attribute] == val.id) {
2318
											tag.attr(\'selected\', \'selected\');
2319
										}
2320
2321
										html.append(tag);
2322
									});
2323
2324
									span.append(html);
2325
									jQuery(\'div#attributes_box\').append(span);
2326
								});
2327
							})
2328
						});
2329
2330
						'.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2331
					});
2332
				</script>
2333
                ';
2334
			}
2335
2336
			if (empty($hidelabel)) {
2337
				$out .= $langs->trans("RefOrLabel").' : ';
2338
			} elseif ($hidelabel > 1) {
2339
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2340
				if ($hidelabel == 2) {
2341
					$out .= img_picto($langs->trans("Search"), 'search');
2342
				}
2343
			}
2344
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
2345
			if ($hidelabel == 3) {
2346
				$out .= img_picto($langs->trans("Search"), 'search');
2347
			}
2348
		} else {
2349
			$out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus);
2350
		}
2351
2352
		if (empty($nooutput)) {
2353
			print $out;
2354
		} else {
2355
			return $out;
2356
		}
2357
	}
2358
2359
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2360
	/**
2361
	 *	Return list of products for a customer.
2362
	 *  Called by select_produits.
2363
	 *
2364
	 *	@param      int		$selected           Preselected product
2365
	 *	@param      string	$htmlname           Name of select html
2366
	 *  @param		string	$filtertype         Filter on product type (''=nofilter, 0=product, 1=service)
2367
	 *	@param      int		$limit              Limit on number of returned lines
2368
	 *	@param      int		$price_level        Level of price to show
2369
	 * 	@param      string	$filterkey          Filter on product
2370
	 *	@param		int		$status             -1=Return all products, 0=Products not on sell, 1=Products on sell
2371
	 *  @param      int		$finished           Filter on finished field: 2=No filter
2372
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
2373
	 *  @param      int		$socid     		    Thirdparty Id (to get also price dedicated to this customer)
2374
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2375
	 * 	@param		int		$forcecombo		    Force to use combo box
2376
	 *  @param      string  $morecss            Add more css on select
2377
	 *  @param      int     $hidepriceinlabel   1=Hide prices in label
2378
	 *  @param      string  $warehouseStatus    Warehouse status filter to group/count stock. Following comma separated filter options can be used.
2379
	 *										    'warehouseopen' = count products from open warehouses,
2380
	 *										    'warehouseclosed' = count products from closed warehouses,
2381
	 *										    'warehouseinternal' = count products from warehouses for internal correct/transfer only
2382
	 *  @return     array    				    Array of keys for json
2383
	 */
2384
	public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '')
2385
	{
2386
		// phpcs:enable
2387
		global $langs, $conf, $user, $db;
2388
2389
		$out = '';
2390
		$outarray = array();
2391
2392
		// Units
2393
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2394
			$langs->load('other');
2395
		}
2396
2397
		$warehouseStatusArray = array();
2398
		if (!empty($warehouseStatus)) {
2399
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2400
			if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2401
				$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2402
			}
2403
			if (preg_match('/warehouseopen/', $warehouseStatus)) {
2404
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2405
			}
2406
			if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2407
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2408
			}
2409
		}
2410
2411
		$selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression";
2412
		if (count($warehouseStatusArray)) {
2413
			$selectFieldsGrouped = ", sum(".$this->db->ifsql("e.statut IS NULL", "0", "ps.reel").") as stock"; // e.statut is null if there is no record in stock
2414
		} else {
2415
			$selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2416
		}
2417
2418
		$sql = "SELECT ";
2419
		$sql .= $selectFields.$selectFieldsGrouped;
2420
2421
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2422
			//Product category
2423
			$sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie
2424
						FROM ".MAIN_DB_PREFIX."categorie_product
2425
						WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid
2426
						LIMIT 1
2427
				) AS categorie_product_id ";
2428
		}
2429
2430
		//Price by customer
2431
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2432
			$sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2433
			$sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.ref_customer as custref';
2434
			$selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custref";
2435
		}
2436
		// Units
2437
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2438
			$sql .= ", u.label as unit_long, u.short_label as unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units";
2439
			$selectFields .= ', unit_long, unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units';
2440
		}
2441
2442
		// Multilang : we add translation
2443
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2444
			$sql .= ", pl.label as label_translated";
2445
			$sql .= ", pl.description as description_translated";
2446
			$selectFields .= ", label_translated";
2447
			$selectFields .= ", description_translated";
2448
		}
2449
		// Price by quantity
2450
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2451
			$sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid";
2452
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2453
				$sql .= " AND price_level = ".((int) $price_level);
2454
			}
2455
			$sql .= " ORDER BY date_price";
2456
			$sql .= " DESC LIMIT 1) as price_rowid";
2457
			$sql .= ", (SELECT pp.price_by_qty FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable
2458
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2459
				$sql .= " AND price_level = ".((int) $price_level);
2460
			}
2461
			$sql .= " ORDER BY date_price";
2462
			$sql .= " DESC LIMIT 1) as price_by_qty";
2463
			$selectFields .= ", price_rowid, price_by_qty";
2464
		}
2465
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
2466
		if (count($warehouseStatusArray)) {
2467
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid";
2468
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2469
			$sql .= ' AND e.statut IN ('.$this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))).')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0.
2470
		}
2471
2472
		// include search in supplier ref
2473
		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2474
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2475
		}
2476
2477
		//Price by customer
2478
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2479
			$sql .= " LEFT JOIN  ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".((int) $socid)." AND pcp.fk_product=p.rowid";
2480
		}
2481
		// Units
2482
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2483
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
2484
		}
2485
		// Multilang : we add translation
2486
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2487
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid ";
2488
			if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2489
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2490
				$soc = new Societe($db);
2491
				$result = $soc->fetch($socid);
2492
				if ($result > 0 && !empty($soc->default_lang)) {
2493
					$sql .= " AND pl.lang='" . $this->db->escape($soc->default_lang) . "'";
2494
				} else {
2495
					$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2496
				}
2497
			} else {
2498
				$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2499
			}
2500
		}
2501
2502
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2503
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2504
		}
2505
2506
		$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2507
2508
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2509
			$sql .= " AND pac.rowid IS NULL";
2510
		}
2511
2512
		if ($finished == 0) {
2513
			$sql .= " AND p.finished = ".((int) $finished);
2514
		} elseif ($finished == 1) {
2515
			$sql .= " AND p.finished = ".((int) $finished);
2516
			if ($status >= 0) {
2517
				$sql .= " AND p.tosell = ".((int) $status);
2518
			}
2519
		} elseif ($status >= 0) {
2520
			$sql .= " AND p.tosell = ".((int) $status);
2521
		}
2522
		// Filter by product type
2523
		if (strval($filtertype) != '') {
2524
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
2525
		} elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only
2526
			$sql .= " AND p.fk_product_type = 1";
2527
		} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2528
			$sql .= " AND p.fk_product_type = 0";
2529
		}
2530
		// Add criteria on ref/label
2531
		if ($filterkey != '') {
2532
			$sql .= ' AND (';
2533
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2534
			// For natural search
2535
			$scrit = explode(' ', $filterkey);
2536
			$i = 0;
2537
			if (count($scrit) > 1) {
2538
				$sql .= "(";
2539
			}
2540
			foreach ($scrit as $crit) {
2541
				if ($i > 0) {
2542
					$sql .= " AND ";
2543
				}
2544
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2545
				if (!empty($conf->global->MAIN_MULTILANGS)) {
2546
					$sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2547
				}
2548
				if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) {
2549
					$sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2550
				}
2551
				if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2552
					$sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2553
					if (!empty($conf->global->MAIN_MULTILANGS)) {
2554
						$sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2555
					}
2556
				}
2557
				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2558
					$sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2559
				}
2560
				$sql .= ")";
2561
				$i++;
2562
			}
2563
			if (count($scrit) > 1) {
2564
				$sql .= ")";
2565
			}
2566
			if (!empty($conf->barcode->enabled)) {
2567
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2568
			}
2569
			$sql .= ')';
2570
		}
2571
		if (count($warehouseStatusArray)) {
2572
			$sql .= " GROUP BY ".$selectFields;
2573
		}
2574
2575
		//Sort by category
2576
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2577
			$sql .= " ORDER BY categorie_product_id ";
2578
			//ASC OR DESC order
2579
			($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2580
		} else {
2581
			$sql .= $this->db->order("p.ref");
2582
		}
2583
2584
		$sql .= $this->db->plimit($limit, 0);
2585
2586
		// Build output string
2587
		dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2588
		$result = $this->db->query($sql);
2589
		if ($result) {
2590
			require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2591
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2592
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2593
2594
			$num = $this->db->num_rows($result);
2595
2596
			$events = null;
2597
2598
			if (!$forcecombo) {
2599
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2600
				$out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT);
2601
			}
2602
2603
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2604
2605
			$textifempty = '';
2606
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2607
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2608
			if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2609
				if ($showempty && !is_numeric($showempty)) {
2610
					$textifempty = $langs->trans($showempty);
2611
				} else {
2612
					$textifempty .= $langs->trans("All");
2613
				}
2614
			} else {
2615
				if ($showempty && !is_numeric($showempty)) {
2616
					$textifempty = $langs->trans($showempty);
2617
				}
2618
			}
2619
			if ($showempty) {
2620
				$out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2621
			}
2622
2623
			$i = 0;
2624
			while ($num && $i < $num) {
2625
				$opt = '';
2626
				$optJson = array();
2627
				$objp = $this->db->fetch_object($result);
2628
2629
				if ((!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) && !empty($objp->price_by_qty) && $objp->price_by_qty == 1) { // Price by quantity will return many prices for the same product
2630
					$sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2631
					$sql .= " FROM ".MAIN_DB_PREFIX."product_price_by_qty";
2632
					$sql .= " WHERE fk_product_price = ".((int) $objp->price_rowid);
2633
					$sql .= " ORDER BY quantity ASC";
2634
2635
					dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2636
					$result2 = $this->db->query($sql);
2637
					if ($result2) {
2638
						$nb_prices = $this->db->num_rows($result2);
2639
						$j = 0;
2640
						while ($nb_prices && $j < $nb_prices) {
2641
							$objp2 = $this->db->fetch_object($result2);
2642
2643
							$objp->price_by_qty_rowid = $objp2->rowid;
2644
							$objp->price_by_qty_price_base_type = $objp2->price_base_type;
2645
							$objp->price_by_qty_quantity = $objp2->quantity;
2646
							$objp->price_by_qty_unitprice = $objp2->unitprice;
2647
							$objp->price_by_qty_remise_percent = $objp2->remise_percent;
2648
							// For backward compatibility
2649
							$objp->quantity = $objp2->quantity;
2650
							$objp->price = $objp2->price;
2651
							$objp->unitprice = $objp2->unitprice;
2652
							$objp->remise_percent = $objp2->remise_percent;
2653
							$objp->remise = $objp2->remise;
2654
2655
							$this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2656
2657
							$j++;
2658
2659
							// Add new entry
2660
							// "key" value of json key array is used by jQuery automatically as selected value
2661
							// "label" value of json key array is used by jQuery automatically as text for combo box
2662
							$out .= $opt;
2663
							array_push($outarray, $optJson);
2664
						}
2665
					}
2666
				} else {
2667
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
2668
						$price_product = new Product($this->db);
2669
						$price_product->fetch($objp->rowid, '', '', 1);
2670
						$priceparser = new PriceParser($this->db);
2671
						$price_result = $priceparser->parseProduct($price_product);
2672
						if ($price_result >= 0) {
2673
							$objp->price = $price_result;
2674
							$objp->unitprice = $price_result;
2675
							//Calculate the VAT
2676
							$objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2677
							$objp->price_ttc = price2num($objp->price_ttc, 'MU');
2678
						}
2679
					}
2680
2681
					$this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2682
					// Add new entry
2683
					// "key" value of json key array is used by jQuery automatically as selected value
2684
					// "label" value of json key array is used by jQuery automatically as text for combo box
2685
					$out .= $opt;
2686
					array_push($outarray, $optJson);
2687
				}
2688
2689
				$i++;
2690
			}
2691
2692
			$out .= '</select>';
2693
2694
			$this->db->free($result);
2695
2696
			if (empty($outputmode)) {
2697
				return $out;
2698
			}
2699
			return $outarray;
2700
		} else {
2701
			dol_print_error($db);
2702
		}
2703
	}
2704
2705
	/**
2706
	 * constructProductListOption.
2707
	 * This define value for &$opt and &$optJson.
2708
	 *
2709
	 * @param 	resource	$objp			    Resultset of fetch
2710
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
2711
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
2712
	 * @param 	int			$price_level	    Price level
2713
	 * @param 	string		$selected		    Preselected value
2714
	 * @param   int         $hidepriceinlabel   Hide price in label
2715
	 * @param   string      $filterkey          Filter key to highlight
2716
	 * @param	int			$novirtualstock 	Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on.
2717
	 * @return	void
2718
	 */
2719
	protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2720
	{
2721
		global $langs, $conf, $user, $db;
2722
2723
		$outkey = '';
2724
		$outval = '';
2725
		$outref = '';
2726
		$outlabel = '';
2727
		$outlabel_translated = '';
2728
		$outdesc = '';
2729
		$outdesc_translated = '';
2730
		$outbarcode = '';
2731
		$outorigin = '';
2732
		$outtype = '';
2733
		$outprice_ht = '';
2734
		$outprice_ttc = '';
2735
		$outpricebasetype = '';
2736
		$outtva_tx = '';
2737
		$outqty = 1;
2738
		$outdiscount = 0;
2739
2740
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2741
2742
		$label = $objp->label;
2743
		if (!empty($objp->label_translated)) {
2744
			$label = $objp->label_translated;
2745
		}
2746
		if (!empty($filterkey) && $filterkey != '') {
2747
			$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2748
		}
2749
2750
		$outkey = $objp->rowid;
2751
		$outref = $objp->ref;
2752
		$outrefcust = empty($objp->custref) ? '' : $objp->custref;
2753
		$outlabel = $objp->label;
2754
		$outdesc = $objp->description;
2755
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2756
			$outlabel_translated = $objp->label_translated;
2757
			$outdesc_translated = $objp->description_translated;
2758
		}
2759
		$outbarcode = $objp->barcode;
2760
		$outorigin = $objp->fk_country;
2761
		$outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2762
2763
		$outtype = $objp->fk_product_type;
2764
		$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2765
		$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2766
2767
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2768
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2769
		}
2770
2771
		// Units
2772
		$outvalUnits = '';
2773
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2774
			if (!empty($objp->unit_short)) {
2775
				$outvalUnits .= ' - '.$objp->unit_short;
2776
			}
2777
		}
2778
		if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2779
			if (!empty($objp->weight) && $objp->weight_units !== null) {
2780
				$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2781
				$outvalUnits .= ' - '.$unitToShow;
2782
			}
2783
			if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2784
				$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2785
				$outvalUnits .= ' - '.$unitToShow;
2786
			}
2787
			if (!empty($objp->surface) && $objp->surface_units !== null) {
2788
				$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2789
				$outvalUnits .= ' - '.$unitToShow;
2790
			}
2791
			if (!empty($objp->volume) && $objp->volume_units !== null) {
2792
				$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2793
				$outvalUnits .= ' - '.$unitToShow;
2794
			}
2795
		}
2796
		if ($outdurationvalue && $outdurationunit) {
2797
			$da = array(
2798
				'h' => $langs->trans('Hour'),
2799
				'd' => $langs->trans('Day'),
2800
				'w' => $langs->trans('Week'),
2801
				'm' => $langs->trans('Month'),
2802
				'y' => $langs->trans('Year')
2803
			);
2804
			if (isset($da[$outdurationunit])) {
2805
				$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2806
			}
2807
		}
2808
2809
		$opt = '<option value="'.$objp->rowid.'"';
2810
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
2811
		if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2812
			$opt .= ' pbq="'.$objp->price_by_qty_rowid.'" data-pbq="'.$objp->price_by_qty_rowid.'" data-pbqup="'.$objp->price_by_qty_unitprice.'" data-pbqbase="'.$objp->price_by_qty_price_base_type.'" data-pbqqty="'.$objp->price_by_qty_quantity.'" data-pbqpercent="'.$objp->price_by_qty_remise_percent.'"';
2813
		}
2814
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2815
			if (!empty($user->rights->stock->lire)) {
2816
				if ($objp->stock > 0) {
2817
					$opt .= ' class="product_line_stock_ok"';
2818
				} elseif ($objp->stock <= 0) {
2819
					$opt .= ' class="product_line_stock_too_low"';
2820
				}
2821
			}
2822
		}
2823
		if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2824
			$opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2825
			$opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2826
		}
2827
		$opt .= '>';
2828
		$opt .= $objp->ref;
2829
		if (! empty($objp->custref)) {
2830
			$opt.= ' (' . $objp->custref . ')';
2831
		}
2832
		if ($outbarcode) {
2833
			$opt .= ' ('.$outbarcode.')';
2834
		}
2835
		$opt .= ' - '.dol_trunc($label, $maxlengtharticle);
2836
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2837
			$opt .= ' ('.getCountry($outorigin, 1).')';
2838
		}
2839
2840
		$objRef = $objp->ref;
2841
		if (! empty($objp->custref)) {
2842
			$objRef .= ' (' . $objp->custref . ')';
2843
		}
2844
		if (!empty($filterkey) && $filterkey != '') {
2845
			$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2846
		}
2847
		$outval .= $objRef;
2848
		if ($outbarcode) {
2849
			$outval .= ' ('.$outbarcode.')';
2850
		}
2851
		$outval .= ' - '.dol_trunc($label, $maxlengtharticle);
2852
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2853
			$outval .= ' ('.getCountry($outorigin, 1).')';
2854
		}
2855
2856
		// Units
2857
		$opt .= $outvalUnits;
2858
		$outval .= $outvalUnits;
2859
2860
		$found = 0;
2861
2862
		// Multiprice
2863
		// If we need a particular price level (from 1 to 6)
2864
		if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
2865
			$sql = "SELECT price, price_ttc, price_base_type, tva_tx";
2866
			$sql .= " FROM ".MAIN_DB_PREFIX."product_price";
2867
			$sql .= " WHERE fk_product = ".((int) $objp->rowid);
2868
			$sql .= " AND entity IN (".getEntity('productprice').")";
2869
			$sql .= " AND price_level = ".((int) $price_level);
2870
			$sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
2871
			$sql .= " LIMIT 1";
2872
2873
			dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
2874
			$result2 = $this->db->query($sql);
2875
			if ($result2) {
2876
				$objp2 = $this->db->fetch_object($result2);
2877
				if ($objp2) {
2878
					$found = 1;
2879
					if ($objp2->price_base_type == 'HT') {
2880
						$opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2881
						$outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2882
					} else {
2883
						$opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2884
						$outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2885
					}
2886
					$outprice_ht = price($objp2->price);
2887
					$outprice_ttc = price($objp2->price_ttc);
2888
					$outpricebasetype = $objp2->price_base_type;
2889
					$outtva_tx = $objp2->tva_tx;
2890
				}
2891
			} else {
2892
				dol_print_error($this->db);
2893
			}
2894
		}
2895
2896
		// Price by quantity
2897
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
2898
			$found = 1;
2899
			$outqty = $objp->quantity;
2900
			$outdiscount = $objp->remise_percent;
2901
			if ($objp->quantity == 1) {
2902
				$opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
2903
				$outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
2904
				$opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
2905
				$outval .= $langs->transnoentities("Unit");
2906
			} else {
2907
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2908
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2909
				$opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
2910
				$outval .= $langs->transnoentities("Units");
2911
			}
2912
2913
			$outprice_ht = price($objp->unitprice);
2914
			$outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
2915
			$outpricebasetype = $objp->price_base_type;
2916
			$outtva_tx = $objp->tva_tx;
2917
		}
2918
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
2919
			$opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2920
			$outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2921
		}
2922
		if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
2923
			$opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
2924
			$outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
2925
		}
2926
2927
		// Price by customer
2928
		if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
2929
			if (!empty($objp->idprodcustprice)) {
2930
				$found = 1;
2931
2932
				if ($objp->custprice_base_type == 'HT') {
2933
					$opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2934
					$outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2935
				} else {
2936
					$opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2937
					$outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2938
				}
2939
2940
				$outprice_ht = price($objp->custprice);
2941
				$outprice_ttc = price($objp->custprice_ttc);
2942
				$outpricebasetype = $objp->custprice_base_type;
2943
				$outtva_tx = $objp->custtva_tx;
2944
			}
2945
		}
2946
2947
		// If level no defined or multiprice not found, we used the default price
2948
		if (empty($hidepriceinlabel) && !$found) {
2949
			if ($objp->price_base_type == 'HT') {
2950
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2951
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2952
			} else {
2953
				$opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2954
				$outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2955
			}
2956
			$outprice_ht = price($objp->price);
2957
			$outprice_ttc = price($objp->price_ttc);
2958
			$outpricebasetype = $objp->price_base_type;
2959
			$outtva_tx = $objp->tva_tx;
2960
		}
2961
2962
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2963
			if (!empty($user->rights->stock->lire)) {
2964
				$opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
2965
2966
				if ($objp->stock > 0) {
2967
					$outval .= ' - <span class="product_line_stock_ok">';
2968
				} elseif ($objp->stock <= 0) {
2969
					$outval .= ' - <span class="product_line_stock_too_low">';
2970
				}
2971
				$outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
2972
				$outval .= '</span>';
2973
				if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
2974
					$langs->load("stocks");
2975
2976
					$tmpproduct = new Product($this->db);
2977
					$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
2978
					$tmpproduct->load_virtual_stock();
2979
					$virtualstock = $tmpproduct->stock_theorique;
2980
2981
					$opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
2982
2983
					$outval .= ' - '.$langs->transnoentities("VirtualStock").':';
2984
					if ($virtualstock > 0) {
2985
						$outval .= '<span class="product_line_stock_ok">';
2986
					} elseif ($virtualstock <= 0) {
2987
						$outval .= '<span class="product_line_stock_too_low">';
2988
					}
2989
					$outval .= $virtualstock;
2990
					$outval .= '</span>';
2991
2992
					unset($tmpproduct);
2993
				}
2994
			}
2995
		}
2996
2997
		$opt .= "</option>\n";
2998
		$optJson = array(
2999
			'key'=>$outkey,
3000
			'value'=>$outref,
3001
			'label'=>$outval,
3002
			'label2'=>$outlabel,
3003
			'desc'=>$outdesc,
3004
			'type'=>$outtype,
3005
			'price_ht'=>price2num($outprice_ht),
3006
			'price_ttc'=>price2num($outprice_ttc),
3007
			'pricebasetype'=>$outpricebasetype,
3008
			'tva_tx'=>$outtva_tx, 'qty'=>$outqty,
3009
			'discount'=>$outdiscount,
3010
			'duration_value'=>$outdurationvalue,
3011
			'duration_unit'=>$outdurationunit,
3012
			'pbq'=>$outpbq,
3013
			'labeltrans'=>$outlabel_translated,
3014
			'desctrans'=>$outdesc_translated,
3015
			'ref_customer'=>$outrefcust
3016
		);
3017
	}
3018
3019
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3020
	/**
3021
	 *	Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list)
3022
	 *
3023
	 *	@param	int		$socid			Id third party
3024
	 *	@param  string	$selected       Preselected product
3025
	 *	@param  string	$htmlname       Name of HTML Select
3026
	 *  @param	string	$filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
3027
	 *	@param  string	$filtre			For a SQL filter
3028
	 *	@param	array	$ajaxoptions	Options for ajax_autocompleter
3029
	 *  @param	int		$hidelabel		Hide label (0=no, 1=yes)
3030
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3031
	 *  @param	string	$morecss		More CSS
3032
	 *  @param	string	$placeholder	Placeholder
3033
	 *	@return	void
3034
	 */
3035
	public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3036
	{
3037
		// phpcs:enable
3038
		global $langs, $conf;
3039
		global $price_level, $status, $finished;
3040
3041
		if (!isset($status)) {
3042
			$status = 1;
3043
		}
3044
3045
		$selected_input_value = '';
3046
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3047
			if ($selected > 0) {
3048
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3049
				$producttmpselect = new Product($this->db);
3050
				$producttmpselect->fetch($selected);
3051
				$selected_input_value = $producttmpselect->ref;
3052
				unset($producttmpselect);
3053
			}
3054
3055
			// mode=2 means suppliers products
3056
			$urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3057
			print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3058
			print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3059
		} else {
3060
			print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3061
		}
3062
	}
3063
3064
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3065
	/**
3066
	 *	Return list of suppliers products
3067
	 *
3068
	 *	@param	int		$socid   			Id of supplier thirdparty (0 = no filter)
3069
	 *	@param  int		$selected       	Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD')
3070
	 *	@param  string	$htmlname       	Name of HTML select
3071
	 *  @param	string	$filtertype     	Filter on product type (''=nofilter, 0=product, 1=service)
3072
	 *	@param  string	$filtre         	Generic filter. Data must not come from user input.
3073
	 *	@param  string	$filterkey      	Filter of produdts
3074
	 *  @param  int		$statut         	-1=Return all products, 0=Products not on buy, 1=Products on buy
3075
	 *  @param  int		$outputmode     	0=HTML select string, 1=Array
3076
	 *  @param  int     $limit          	Limit of line number
3077
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3078
	 *  @param	string	$morecss			Add more CSS
3079
	 *  @param	int		$showstockinlist	Show stock information (slower).
3080
	 *  @param	string	$placeholder		Placeholder
3081
	 *  @return array           			Array of keys for json
3082
	 */
3083
	public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '', $showstockinlist = 0, $placeholder = '')
3084
	{
3085
		// phpcs:enable
3086
		global $langs, $conf, $db, $user;
3087
3088
		$out = '';
3089
		$outarray = array();
3090
3091
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3092
3093
		$langs->load('stocks');
3094
		// Units
3095
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3096
			$langs->load('other');
3097
		}
3098
3099
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock,";
3100
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3101
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name,";
3102
		$sql .= " pfp.supplier_reputation";
3103
		// if we use supplier description of the products
3104
		if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3105
			$sql .= " ,pfp.desc_fourn as description";
3106
		} else {
3107
			$sql .= " ,p.description";
3108
		}
3109
		// Units
3110
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3111
			$sql .= ", u.label as unit_long, u.short_label as unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units";
3112
		}
3113
		if (!empty($conf->barcode->enabled)) {
3114
			$sql .= ", pfp.barcode";
3115
		}
3116
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3117
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3118
		if ($socid > 0) {
3119
			$sql .= " AND pfp.fk_soc = ".((int) $socid);
3120
		}
3121
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3122
		// Units
3123
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3124
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
3125
		}
3126
		$sql .= " WHERE p.entity IN (".getEntity('product').")";
3127
		if ($statut != -1) {
3128
			$sql .= " AND p.tobuy = ".((int) $statut);
3129
		}
3130
		if (strval($filtertype) != '') {
3131
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
3132
		}
3133
		if (!empty($filtre)) {
3134
			$sql .= " ".$filtre;
3135
		}
3136
		// Add criteria on ref/label
3137
		if ($filterkey != '') {
3138
			$sql .= ' AND (';
3139
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3140
			// For natural search
3141
			$scrit = explode(' ', $filterkey);
3142
			$i = 0;
3143
			if (count($scrit) > 1) {
3144
				$sql .= "(";
3145
			}
3146
			foreach ($scrit as $crit) {
3147
				if ($i > 0) {
3148
					$sql .= " AND ";
3149
				}
3150
				$sql .= "(pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%' OR p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
3151
				if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3152
					$sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3153
				}
3154
				$sql .= ")";
3155
				$i++;
3156
			}
3157
			if (count($scrit) > 1) {
3158
				$sql .= ")";
3159
			}
3160
			if (!empty($conf->barcode->enabled)) {
3161
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3162
				$sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3163
			}
3164
			$sql .= ')';
3165
		}
3166
		$sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3167
		$sql .= $this->db->plimit($limit, 0);
3168
3169
		// Build output string
3170
3171
		dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3172
		$result = $this->db->query($sql);
3173
		if ($result) {
3174
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3175
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3176
3177
			$num = $this->db->num_rows($result);
3178
3179
			//$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';	// remove select to have id same with combo and ajax
3180
			$out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3181
			if (!$selected) {
3182
				$out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3183
			} else {
3184
				$out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3185
			}
3186
3187
			$i = 0;
3188
			while ($i < $num) {
3189
				$objp = $this->db->fetch_object($result);
3190
3191
				$outkey = $objp->idprodfournprice; // id in table of price
3192
				if (!$outkey && $alsoproductwithnosupplierprice) {
3193
					$outkey = 'idprod_'.$objp->rowid; // id of product
3194
				}
3195
3196
				$outref = $objp->ref;
3197
				$outval = '';
3198
				$outbarcode = $objp->barcode;
3199
				$outqty = 1;
3200
				$outdiscount = 0;
3201
				$outtype = $objp->fk_product_type;
3202
				$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3203
				$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3204
3205
				// Units
3206
				$outvalUnits = '';
3207
				if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3208
					if (!empty($objp->unit_short)) {
3209
						$outvalUnits .= ' - '.$objp->unit_short;
3210
					}
3211
					if (!empty($objp->weight) && $objp->weight_units !== null) {
3212
						$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3213
						$outvalUnits .= ' - '.$unitToShow;
3214
					}
3215
					if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3216
						$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3217
						$outvalUnits .= ' - '.$unitToShow;
3218
					}
3219
					if (!empty($objp->surface) && $objp->surface_units !== null) {
3220
						$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3221
						$outvalUnits .= ' - '.$unitToShow;
3222
					}
3223
					if (!empty($objp->volume) && $objp->volume_units !== null) {
3224
						$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3225
						$outvalUnits .= ' - '.$unitToShow;
3226
					}
3227
					if ($outdurationvalue && $outdurationunit) {
3228
						$da = array(
3229
							'h' => $langs->trans('Hour'),
3230
							'd' => $langs->trans('Day'),
3231
							'w' => $langs->trans('Week'),
3232
							'm' => $langs->trans('Month'),
3233
							'y' => $langs->trans('Year')
3234
						);
3235
						if (isset($da[$outdurationunit])) {
3236
							$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3237
						}
3238
					}
3239
				}
3240
3241
				$objRef = $objp->ref;
3242
				if ($filterkey && $filterkey != '') {
3243
					$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3244
				}
3245
				$objRefFourn = $objp->ref_fourn;
3246
				if ($filterkey && $filterkey != '') {
3247
					$objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3248
				}
3249
				$label = $objp->label;
3250
				if ($filterkey && $filterkey != '') {
3251
					$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3252
				}
3253
3254
				$optlabel = $objp->ref;
3255
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3256
					$optlabel .= ' <span class=\'opacitymedium\'>('.$objp->ref_fourn.')</span>';
3257
				}
3258
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3259
					$optlabel .= ' ('.$outbarcode.')';
3260
				}
3261
				$optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3262
3263
				$outvallabel = $objRef;
3264
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3265
					$outvallabel .= ' ('.$objRefFourn.')';
3266
				}
3267
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3268
					$outvallabel .= ' ('.$outbarcode.')';
3269
				}
3270
				$outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3271
3272
				// Units
3273
				$optlabel .= $outvalUnits;
3274
				$outvallabel .= $outvalUnits;
3275
3276
				if (!empty($objp->idprodfournprice)) {
3277
					$outqty = $objp->quantity;
3278
					$outdiscount = $objp->remise_percent;
3279
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3280
						$prod_supplier = new ProductFournisseur($this->db);
3281
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3282
						$prod_supplier->id = $objp->fk_product;
3283
						$prod_supplier->fourn_qty = $objp->quantity;
3284
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3285
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3286
						$priceparser = new PriceParser($this->db);
3287
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3288
						if ($price_result >= 0) {
3289
							$objp->fprice = $price_result;
3290
							if ($objp->quantity >= 1) {
3291
								$objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3292
							}
3293
						}
3294
					}
3295
					if ($objp->quantity == 1) {
3296
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3297
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3298
						$optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3299
						$outvallabel .= $langs->transnoentities("Unit");
3300
					} else {
3301
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3302
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3303
						$optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3304
						$outvallabel .= ' '.$langs->transnoentities("Units");
3305
					}
3306
3307
					if ($objp->quantity > 1) {
3308
						$optlabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3309
						$outvallabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3310
					}
3311
					if ($objp->remise_percent >= 1) {
3312
						$optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3313
						$outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3314
					}
3315
					if ($objp->duration) {
3316
						$optlabel .= " - ".$objp->duration;
3317
						$outvallabel .= " - ".$objp->duration;
3318
					}
3319
					if (!$socid) {
3320
						$optlabel .= " - ".dol_trunc($objp->name, 8);
3321
						$outvallabel .= " - ".dol_trunc($objp->name, 8);
3322
					}
3323
					if ($objp->supplier_reputation) {
3324
						//TODO dictionary
3325
						$reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3326
3327
						$optlabel .= " - ".$reputations[$objp->supplier_reputation];
3328
						$outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3329
					}
3330
				} else {
3331
					if (empty($alsoproductwithnosupplierprice)) {     // No supplier price defined for couple product/supplier
3332
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3333
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3334
					} else // No supplier price defined for product, even on other suppliers
3335
					{
3336
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3337
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3338
					}
3339
				}
3340
3341
				if (!empty($conf->stock->enabled) && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3342
					$novirtualstock = ($showstockinlist == 2);
3343
3344
					if (!empty($user->rights->stock->lire)) {
3345
						$outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3346
3347
						if ($objp->stock > 0) {
3348
							$optlabel .= ' - <span class="product_line_stock_ok">';
3349
						} elseif ($objp->stock <= 0) {
3350
							$optlabel .= ' - <span class="product_line_stock_too_low">';
3351
						}
3352
						$optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3353
						$optlabel .= '</span>';
3354
						if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
3355
							$langs->load("stocks");
3356
3357
							$tmpproduct = new Product($this->db);
3358
							$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3359
							$tmpproduct->load_virtual_stock();
3360
							$virtualstock = $tmpproduct->stock_theorique;
3361
3362
							$outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3363
3364
							$optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3365
							if ($virtualstock > 0) {
3366
								$optlabel .= '<span class="product_line_stock_ok">';
3367
							} elseif ($virtualstock <= 0) {
3368
								$optlabel .= '<span class="product_line_stock_too_low">';
3369
							}
3370
							$optlabel .= $virtualstock;
3371
							$optlabel .= '</span>';
3372
3373
							unset($tmpproduct);
3374
						}
3375
					}
3376
				}
3377
3378
				$opt = '<option value="'.$outkey.'"';
3379
				if ($selected && $selected == $objp->idprodfournprice) {
3380
					$opt .= ' selected';
3381
				}
3382
				if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3383
					$opt .= ' disabled';
3384
				}
3385
				if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3386
					$opt .= ' data-product-id="'.$objp->rowid.'" data-price-id="'.$objp->idprodfournprice.'" data-qty="'.$objp->quantity.'" data-up="'.$objp->unitprice.'" data-discount="'.$outdiscount.'"';
3387
				}
3388
				$opt .= ' data-description="'.dol_escape_htmltag($objp->description, 0, 1).'"';
3389
				$opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"';
3390
				$opt .= '>';
3391
3392
				$opt .= $optlabel;
3393
				$outval .= $outvallabel;
3394
3395
				$opt .= "</option>\n";
3396
3397
3398
				// Add new entry
3399
				// "key" value of json key array is used by jQuery automatically as selected value. Example: 'type' = product or service, 'price_ht' = unit price without tax
3400
				// "label" value of json key array is used by jQuery automatically as text for combo box
3401
				$out .= $opt;
3402
				array_push(
3403
					$outarray,
3404
					array('key'=>$outkey,
3405
						'value'=>$outref,
3406
						'label'=>$outval,
3407
						'qty'=>$outqty,
3408
						'price_ht'=>price2num($objp->unitprice, 'MT'),
3409
						'discount'=>$outdiscount,
3410
						'type'=>$outtype,
3411
						'duration_value'=>$outdurationvalue,
3412
						'duration_unit'=>$outdurationunit,
3413
						'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3414
						'description'=>$objp->description
3415
					)
3416
				);
3417
				// Exemple of var_dump $outarray
3418
				// array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3419
				//           ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3420
				//      	 ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3421
				//}
3422
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3423
				//$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3424
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3425
3426
				$i++;
3427
			}
3428
			$out .= '</select>';
3429
3430
			$this->db->free($result);
3431
3432
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3433
			$out .= ajax_combobox($htmlname);
3434
3435
			if (empty($outputmode)) {
3436
				return $out;
3437
			}
3438
			return $outarray;
3439
		} else {
3440
			dol_print_error($this->db);
3441
		}
3442
	}
3443
3444
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3445
	/**
3446
	 *	Return list of suppliers prices for a product
3447
	 *
3448
	 *  @param	    int		$productid       	Id of product
3449
	 *  @param      string	$htmlname        	Name of HTML field
3450
	 *  @param      int		$selected_supplier  Pre-selected supplier if more than 1 result
3451
	 *  @return	    string
3452
	 */
3453
	public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3454
	{
3455
		// phpcs:enable
3456
		global $langs, $conf;
3457
3458
		$langs->load('stocks');
3459
3460
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3461
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3462
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3463
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3464
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3465
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3466
		$sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3467
		$sql .= " AND p.tobuy = 1";
3468
		$sql .= " AND s.fournisseur = 1";
3469
		$sql .= " AND p.rowid = ".((int) $productid);
3470
		$sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3471
3472
		dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3473
		$result = $this->db->query($sql);
3474
3475
		if ($result) {
3476
			$num = $this->db->num_rows($result);
3477
3478
			$form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3479
3480
			if (!$num) {
3481
				$form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3482
			} else {
3483
				require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3484
				$form .= '<option value="0">&nbsp;</option>';
3485
3486
				$i = 0;
3487
				while ($i < $num) {
3488
					$objp = $this->db->fetch_object($result);
3489
3490
					$opt = '<option value="'.$objp->idprodfournprice.'"';
3491
					//if there is only one supplier, preselect it
3492
					if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier)) {
3493
						$opt .= ' selected';
3494
					}
3495
					$opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3496
3497
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3498
						$prod_supplier = new ProductFournisseur($this->db);
3499
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3500
						$prod_supplier->id = $productid;
3501
						$prod_supplier->fourn_qty = $objp->quantity;
3502
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3503
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3504
						$priceparser = new PriceParser($this->db);
3505
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3506
						if ($price_result >= 0) {
3507
							$objp->fprice = $price_result;
3508
							if ($objp->quantity >= 1) {
3509
								$objp->unitprice = $objp->fprice / $objp->quantity;
3510
							}
3511
						}
3512
					}
3513
					if ($objp->quantity == 1) {
3514
						$opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3515
					}
3516
3517
					$opt .= $objp->quantity.' ';
3518
3519
					if ($objp->quantity == 1) {
3520
						$opt .= $langs->trans("Unit");
3521
					} else {
3522
						$opt .= $langs->trans("Units");
3523
					}
3524
					if ($objp->quantity > 1) {
3525
						$opt .= " - ";
3526
						$opt .= price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit");
3527
					}
3528
					if ($objp->duration) {
3529
						$opt .= " - ".$objp->duration;
3530
					}
3531
					$opt .= "</option>\n";
3532
3533
					$form .= $opt;
3534
					$i++;
3535
				}
3536
			}
3537
3538
			$form .= '</select>';
3539
			$this->db->free($result);
3540
			return $form;
3541
		} else {
3542
			dol_print_error($this->db);
3543
		}
3544
	}
3545
3546
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3547
	/**
3548
	 *    Return list of delivery address
3549
	 *
3550
	 *    @param    string	$selected          	Id contact pre-selectionn
3551
	 *    @param    int		$socid				Id of company
3552
	 *    @param    string	$htmlname          	Name of HTML field
3553
	 *    @param    int		$showempty         	Add an empty field
3554
	 *    @return	integer|null
3555
	 */
3556
	public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3557
	{
3558
		// phpcs:enable
3559
		// looking for users
3560
		$sql = "SELECT a.rowid, a.label";
3561
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a";
3562
		$sql .= " WHERE a.fk_soc = ".((int) $socid);
3563
		$sql .= " ORDER BY a.label ASC";
3564
3565
		dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3566
		$resql = $this->db->query($sql);
3567
		if ($resql) {
3568
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3569
			if ($showempty) {
3570
				print '<option value="0">&nbsp;</option>';
3571
			}
3572
			$num = $this->db->num_rows($resql);
3573
			$i = 0;
3574
			if ($num) {
3575
				while ($i < $num) {
3576
					$obj = $this->db->fetch_object($resql);
3577
3578
					if ($selected && $selected == $obj->rowid) {
3579
						print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3580
					} else {
3581
						print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3582
					}
3583
					$i++;
3584
				}
3585
			}
3586
			print '</select>';
3587
			return $num;
3588
		} else {
3589
			dol_print_error($this->db);
3590
		}
3591
	}
3592
3593
3594
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3595
	/**
3596
	 *      Load into cache list of payment terms
3597
	 *
3598
	 *      @return     int             Nb of lines loaded, <0 if KO
3599
	 */
3600
	public function load_cache_conditions_paiements()
3601
	{
3602
		// phpcs:enable
3603
		global $langs;
3604
3605
		$num = count($this->cache_conditions_paiements);
3606
		if ($num > 0) {
3607
			return 0; // Cache already loaded
3608
		}
3609
3610
		dol_syslog(__METHOD__, LOG_DEBUG);
3611
3612
		$sql = "SELECT rowid, code, libelle as label";
3613
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term';
3614
		$sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3615
		$sql .= " AND active > 0";
3616
		$sql .= " ORDER BY sortorder";
3617
3618
		$resql = $this->db->query($sql);
3619
		if ($resql) {
3620
			$num = $this->db->num_rows($resql);
3621
			$i = 0;
3622
			while ($i < $num) {
3623
				$obj = $this->db->fetch_object($resql);
3624
3625
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3626
				$label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3627
				$this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3628
				$this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3629
				$i++;
3630
			}
3631
3632
			//$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1);		// We use the field sortorder of table
3633
3634
			return $num;
3635
		} else {
3636
			dol_print_error($this->db);
3637
			return -1;
3638
		}
3639
	}
3640
3641
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3642
	/**
3643
	 *      Charge dans cache la liste des délais de livraison possibles
3644
	 *
3645
	 *      @return     int             Nb of lines loaded, <0 if KO
3646
	 */
3647
	public function load_cache_availability()
3648
	{
3649
		// phpcs:enable
3650
		global $langs;
3651
3652
		$num = count($this->cache_availability);
3653
		if ($num > 0) {
3654
			return 0; // Cache already loaded
3655
		}
3656
3657
		dol_syslog(__METHOD__, LOG_DEBUG);
3658
3659
		$langs->load('propal');
3660
3661
		$sql = "SELECT rowid, code, label, position";
3662
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_availability';
3663
		$sql .= " WHERE active > 0";
3664
3665
		$resql = $this->db->query($sql);
3666
		if ($resql) {
3667
			$num = $this->db->num_rows($resql);
3668
			$i = 0;
3669
			while ($i < $num) {
3670
				$obj = $this->db->fetch_object($resql);
3671
3672
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3673
				$label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3674
				$this->cache_availability[$obj->rowid]['code'] = $obj->code;
3675
				$this->cache_availability[$obj->rowid]['label'] = $label;
3676
				$this->cache_availability[$obj->rowid]['position'] = $obj->position;
3677
				$i++;
3678
			}
3679
3680
			$this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3681
3682
			return $num;
3683
		} else {
3684
			dol_print_error($this->db);
3685
			return -1;
3686
		}
3687
	}
3688
3689
	/**
3690
	 *      Retourne la liste des types de delais de livraison possibles
3691
	 *
3692
	 *      @param	int		$selected       Id du type de delais pre-selectionne
3693
	 *      @param  string	$htmlname       Nom de la zone select
3694
	 *      @param  string	$filtertype     To add a filter
3695
	 *		@param	int		$addempty		Add empty entry
3696
	 * 		@param	string	$morecss		More CSS
3697
	 *		@return	void
3698
	 */
3699
	public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3700
	{
3701
		global $langs, $user;
3702
3703
		$this->load_cache_availability();
3704
3705
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3706
3707
		print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3708
		if ($addempty) {
3709
			print '<option value="0">&nbsp;</option>';
3710
		}
3711
		foreach ($this->cache_availability as $id => $arrayavailability) {
3712
			if ($selected == $id) {
3713
				print '<option value="'.$id.'" selected>';
3714
			} else {
3715
				print '<option value="'.$id.'">';
3716
			}
3717
			print dol_escape_htmltag($arrayavailability['label']);
3718
			print '</option>';
3719
		}
3720
		print '</select>';
3721
		if ($user->admin) {
3722
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3723
		}
3724
		print ajax_combobox($htmlname);
3725
	}
3726
3727
	/**
3728
	 *      Load into cache cache_demand_reason, array of input reasons
3729
	 *
3730
	 *      @return     int             Nb of lines loaded, <0 if KO
3731
	 */
3732
	public function loadCacheInputReason()
3733
	{
3734
		global $langs;
3735
3736
		$num = count($this->cache_demand_reason);
3737
		if ($num > 0) {
3738
			return 0; // Cache already loaded
3739
		}
3740
3741
		$sql = "SELECT rowid, code, label";
3742
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason';
3743
		$sql .= " WHERE active > 0";
3744
3745
		$resql = $this->db->query($sql);
3746
		if ($resql) {
3747
			$num = $this->db->num_rows($resql);
3748
			$i = 0;
3749
			$tmparray = array();
3750
			while ($i < $num) {
3751
				$obj = $this->db->fetch_object($resql);
3752
3753
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3754
				$label = ($obj->label != '-' ? $obj->label : '');
3755
				if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
3756
					$label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3757
				}
3758
				if ($langs->trans($obj->code) != $obj->code) {
3759
					$label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3760
				}
3761
3762
				$tmparray[$obj->rowid]['id']   = $obj->rowid;
3763
				$tmparray[$obj->rowid]['code'] = $obj->code;
3764
				$tmparray[$obj->rowid]['label'] = $label;
3765
				$i++;
3766
			}
3767
3768
			$this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
3769
3770
			unset($tmparray);
3771
			return $num;
3772
		} else {
3773
			dol_print_error($this->db);
3774
			return -1;
3775
		}
3776
	}
3777
3778
	/**
3779
	 *	Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
3780
	 *  List found into table c_input_reason loaded by loadCacheInputReason
3781
	 *
3782
	 *  @param	int		$selected        Id or code of type origin to select by default
3783
	 *  @param  string	$htmlname        Nom de la zone select
3784
	 *  @param  string	$exclude         To exclude a code value (Example: SRC_PROP)
3785
	 *	@param	int		$addempty		 Add an empty entry
3786
	 *  @param  string	$morecss		 Add more css to the HTML select component
3787
	 *  @param	int		$notooltip		 Do not show the tooltip for admin
3788
	 *	@return	void
3789
	 */
3790
	public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
3791
	{
3792
		global $langs, $user;
3793
3794
		$this->loadCacheInputReason();
3795
3796
		print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3797
		if ($addempty) {
3798
			print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
3799
		}
3800
		foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
3801
			if ($arraydemandreason['code'] == $exclude) {
3802
				continue;
3803
			}
3804
3805
			if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
3806
				print '<option value="'.$arraydemandreason['id'].'" selected>';
3807
			} else {
3808
				print '<option value="'.$arraydemandreason['id'].'">';
3809
			}
3810
			$label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
3811
			print $langs->trans($label);
3812
			print '</option>';
3813
		}
3814
		print '</select>';
3815
		if ($user->admin && empty($notooltip)) {
3816
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3817
		}
3818
		print ajax_combobox('select_'.$htmlname);
3819
	}
3820
3821
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3822
	/**
3823
	 *      Charge dans cache la liste des types de paiements possibles
3824
	 *
3825
	 *      @return     int                 Nb of lines loaded, <0 if KO
3826
	 */
3827
	public function load_cache_types_paiements()
3828
	{
3829
		// phpcs:enable
3830
		global $langs;
3831
3832
		$num = count($this->cache_types_paiements);
3833
		if ($num > 0) {
3834
			return $num; // Cache already loaded
3835
		}
3836
3837
		dol_syslog(__METHOD__, LOG_DEBUG);
3838
3839
		$this->cache_types_paiements = array();
3840
3841
		$sql = "SELECT id, code, libelle as label, type, active";
3842
		$sql .= " FROM ".MAIN_DB_PREFIX."c_paiement";
3843
		$sql .= " WHERE entity IN (".getEntity('c_paiement').")";
3844
3845
		$resql = $this->db->query($sql);
3846
		if ($resql) {
3847
			$num = $this->db->num_rows($resql);
3848
			$i = 0;
3849
			while ($i < $num) {
3850
				$obj = $this->db->fetch_object($resql);
3851
3852
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3853
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3854
				$this->cache_types_paiements[$obj->id]['id'] = $obj->id;
3855
				$this->cache_types_paiements[$obj->id]['code'] = $obj->code;
3856
				$this->cache_types_paiements[$obj->id]['label'] = $label;
3857
				$this->cache_types_paiements[$obj->id]['type'] = $obj->type;
3858
				$this->cache_types_paiements[$obj->id]['active'] = $obj->active;
3859
				$i++;
3860
			}
3861
3862
			$this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
3863
3864
			return $num;
3865
		} else {
3866
			dol_print_error($this->db);
3867
			return -1;
3868
		}
3869
	}
3870
3871
3872
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3873
	/**
3874
	 *      Return list of payment modes.
3875
	 *      Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want.
3876
	 *      See instead to force the default value by the caller.
3877
	 *
3878
	 *      @param	int		$selected		Id of payment term to preselect by default
3879
	 *      @param	string	$htmlname		Nom de la zone select
3880
	 *      @param	int		$filtertype		Not used
3881
	 *		@param	int		$addempty		Add an empty entry
3882
	 * 		@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
3883
	 * 		@param	string	$morecss			Add more CSS on select tag
3884
	 *		@return	void
3885
	 */
3886
	public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '')
3887
	{
3888
		// phpcs:enable
3889
		global $langs, $user, $conf;
3890
3891
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3892
3893
		$this->load_cache_conditions_paiements();
3894
3895
		// Set default value if not already set by caller
3896
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
3897
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
3898
		}
3899
3900
		print '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3901
		if ($addempty) {
3902
			print '<option value="0">&nbsp;</option>';
3903
		}
3904
		foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
3905
			if ($selected == $id) {
3906
				print '<option value="'.$id.'" selected>';
3907
			} else {
3908
				print '<option value="'.$id.'">';
3909
			}
3910
			print $arrayconditions['label'];
3911
			print '</option>';
3912
		}
3913
		print '</select>';
3914
		if ($user->admin && empty($noinfoadmin)) {
3915
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3916
		}
3917
		print ajax_combobox($htmlname);
3918
	}
3919
3920
3921
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3922
	/**
3923
	 *      Return list of payment methods
3924
	 *      Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want.
3925
	 *
3926
	 *      @param	string	$selected       Id or code or preselected payment mode
3927
	 *      @param  string	$htmlname       Name of select field
3928
	 *      @param  string	$filtertype     To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
3929
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
3930
	 *      @param  int		$empty			1=can be empty, 0 otherwise
3931
	 * 		@param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
3932
	 *      @param  int		$maxlength      Max length of label
3933
	 *      @param  int     $active         Active or not, -1 = all
3934
	 *      @param  string  $morecss        Add more CSS on select tag
3935
	 *      @param	int		$nooutput		1=Return string, do not send to output
3936
	 * 		@return	void
3937
	 */
3938
	public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
3939
	{
3940
		// phpcs:enable
3941
		global $langs, $user, $conf;
3942
3943
		$out = '';
3944
3945
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
3946
3947
		$filterarray = array();
3948
		if ($filtertype == 'CRDT') {
3949
			$filterarray = array(0, 2, 3);
3950
		} elseif ($filtertype == 'DBIT') {
3951
			$filterarray = array(1, 2, 3);
3952
		} elseif ($filtertype != '' && $filtertype != '-1') {
3953
			$filterarray = explode(',', $filtertype);
3954
		}
3955
3956
		$this->load_cache_types_paiements();
3957
3958
		// Set default value if not already set by caller
3959
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
3960
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
3961
		}
3962
3963
		$out .= '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3964
		if ($empty) {
3965
			$out .= '<option value="">&nbsp;</option>';
3966
		}
3967
		foreach ($this->cache_types_paiements as $id => $arraytypes) {
3968
			// If not good status
3969
			if ($active >= 0 && $arraytypes['active'] != $active) {
3970
				continue;
3971
			}
3972
3973
			// On passe si on a demande de filtrer sur des modes de paiments particuliers
3974
			if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
3975
				continue;
3976
			}
3977
3978
			// We discard empty line if showempty is on because an empty line has already been output.
3979
			if ($empty && empty($arraytypes['code'])) {
3980
				continue;
3981
			}
3982
3983
			if ($format == 0) {
3984
				$out .= '<option value="'.$id.'"';
3985
			} elseif ($format == 1) {
3986
				$out .= '<option value="'.$arraytypes['code'].'"';
3987
			} elseif ($format == 2) {
3988
				$out .= '<option value="'.$arraytypes['code'].'"';
3989
			} elseif ($format == 3) {
3990
				$out .= '<option value="'.$id.'"';
3991
			}
3992
			// Print attribute selected or not
3993
			if ($format == 1 || $format == 2) {
3994
				if ($selected == $arraytypes['code']) {
3995
					$out .= ' selected';
3996
				}
3997
			} else {
3998
				if ($selected == $id) {
3999
					$out .= ' selected';
4000
				}
4001
			}
4002
			$out .= '>';
4003
			if ($format == 0) {
4004
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4005
			} elseif ($format == 1) {
4006
				$value = $arraytypes['code'];
4007
			} elseif ($format == 2) {
4008
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4009
			} elseif ($format == 3) {
4010
				$value = $arraytypes['code'];
4011
			}
4012
			$out .= $value ? $value : '&nbsp;';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
4013
			$out .= '</option>';
4014
		}
4015
		$out .= '</select>';
4016
		if ($user->admin && !$noadmininfo) {
4017
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4018
		}
4019
		$out .= ajax_combobox('select'.$htmlname);
4020
4021
		if (empty($nooutput)) {
4022
			print $out;
4023
		} else {
4024
			return $out;
4025
		}
4026
	}
4027
4028
4029
	/**
4030
	 *  Selection HT or TTC
4031
	 *
4032
	 *  @param	string	$selected       Id pre-selectionne
4033
	 *  @param  string	$htmlname       Nom de la zone select
4034
	 *  @param	string	$addjscombo		Add js combo
4035
	 * 	@return	string					Code of HTML select to chose tax or not
4036
	 */
4037
	public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4038
	{
4039
		global $langs;
4040
4041
		$return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4042
		$options = array(
4043
			'HT'=>$langs->trans("HT"),
4044
			'TTC'=>$langs->trans("TTC")
4045
		);
4046
		foreach ($options as $id => $value) {
4047
			if ($selected == $id) {
4048
				$return .= '<option value="'.$id.'" selected>'.$value;
4049
			} else {
4050
				$return .= '<option value="'.$id.'">'.$value;
4051
			}
4052
			$return .= '</option>';
4053
		}
4054
		$return .= '</select>';
4055
		if ($addjscombo) {
4056
			$return .= ajax_combobox('select_'.$htmlname);
4057
		}
4058
4059
		return $return;
4060
	}
4061
4062
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4063
	/**
4064
	 *      Load in cache list of transport mode
4065
	 *
4066
	 *      @return     int                 Nb of lines loaded, <0 if KO
4067
	 */
4068
	public function load_cache_transport_mode()
4069
	{
4070
		// phpcs:enable
4071
		global $langs;
4072
4073
		$num = count($this->cache_transport_mode);
4074
		if ($num > 0) {
4075
			return $num; // Cache already loaded
4076
		}
4077
4078
		dol_syslog(__METHOD__, LOG_DEBUG);
4079
4080
		$this->cache_transport_mode = array();
4081
4082
		$sql = "SELECT rowid, code, label, active";
4083
		$sql .= " FROM ".MAIN_DB_PREFIX."c_transport_mode";
4084
		$sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4085
4086
		$resql = $this->db->query($sql);
4087
		if ($resql) {
4088
			$num = $this->db->num_rows($resql);
4089
			$i = 0;
4090
			while ($i < $num) {
4091
				$obj = $this->db->fetch_object($resql);
4092
4093
				// If traduction exist, we use it else we take the default label
4094
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4095
				$this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4096
				$this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4097
				$this->cache_transport_mode[$obj->rowid]['label'] = $label;
4098
				$this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4099
				$i++;
4100
			}
4101
4102
			$this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4103
4104
			return $num;
4105
		} else {
4106
			dol_print_error($this->db);
4107
			return -1;
4108
		}
4109
	}
4110
4111
	/**
4112
	 *      Return list of transport mode for intracomm report
4113
	 *
4114
	 *      @param	string	$selected       Id of the transport mode pre-selected
4115
	 *      @param  string	$htmlname       Name of the select field
4116
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
4117
	 *      @param  int		$empty			1=can be empty, 0 else
4118
	 *      @param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
4119
	 *      @param  int		$maxlength      Max length of label
4120
	 *      @param  int     $active         Active or not, -1 = all
4121
	 *      @param  string  $morecss        Add more CSS on select tag
4122
	 * 		@return	void
4123
	 */
4124
	public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4125
	{
4126
		global $langs, $user;
4127
4128
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4129
4130
		$this->load_cache_transport_mode();
4131
4132
		print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4133
		if ($empty) {
4134
			print '<option value="">&nbsp;</option>';
4135
		}
4136
		foreach ($this->cache_transport_mode as $id => $arraytypes) {
4137
			// If not good status
4138
			if ($active >= 0 && $arraytypes['active'] != $active) {
4139
				continue;
4140
			}
4141
4142
			// We discard empty line if showempty is on because an empty line has already been output.
4143
			if ($empty && empty($arraytypes['code'])) {
4144
				continue;
4145
			}
4146
4147
			if ($format == 0) {
4148
				print '<option value="'.$id.'"';
4149
			} elseif ($format == 1) {
4150
				print '<option value="'.$arraytypes['code'].'"';
4151
			} elseif ($format == 2) {
4152
				print '<option value="'.$arraytypes['code'].'"';
4153
			} elseif ($format == 3) {
4154
				print '<option value="'.$id.'"';
4155
			}
4156
			// If text is selected, we compare with code, else with id
4157
			if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4158
				print ' selected';
4159
			} elseif ($selected == $id) {
4160
				print ' selected';
4161
			}
4162
			print '>';
4163
			if ($format == 0) {
4164
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4165
			} elseif ($format == 1) {
4166
				$value = $arraytypes['code'];
4167
			} elseif ($format == 2) {
4168
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4169
			} elseif ($format == 3) {
4170
				$value = $arraytypes['code'];
4171
			}
4172
			print $value ? $value : '&nbsp;';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
4173
			print '</option>';
4174
		}
4175
		print '</select>';
4176
		if ($user->admin && !$noadmininfo) {
4177
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4178
		}
4179
	}
4180
4181
	/**
4182
	 *  Return a HTML select list of shipping mode
4183
	 *
4184
	 *  @param	string	$selected           Id shipping mode pre-selected
4185
	 *  @param  string	$htmlname           Name of select zone
4186
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4187
	 *  @param  int		$useempty           1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4188
	 *  @param  string	$moreattrib         To add more attribute on select
4189
	 *	@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
4190
	 *  @param	string	$morecss			More CSS
4191
	 * 	@return	void
4192
	 */
4193
	public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4194
	{
4195
		global $langs, $conf, $user;
4196
4197
		$langs->load("admin");
4198
		$langs->load("deliveries");
4199
4200
		$sql = "SELECT rowid, code, libelle as label";
4201
		$sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode";
4202
		$sql .= " WHERE active > 0";
4203
		if ($filtre) {
4204
			$sql .= " AND ".$filtre;
4205
		}
4206
		$sql .= " ORDER BY libelle ASC";
4207
4208
		dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4209
		$result = $this->db->query($sql);
4210
		if ($result) {
4211
			$num = $this->db->num_rows($result);
4212
			$i = 0;
4213
			if ($num) {
4214
				print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4215
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4216
					print '<option value="-1">&nbsp;</option>';
4217
				}
4218
				while ($i < $num) {
4219
					$obj = $this->db->fetch_object($result);
4220
					if ($selected == $obj->rowid) {
4221
						print '<option value="'.$obj->rowid.'" selected>';
4222
					} else {
4223
						print '<option value="'.$obj->rowid.'">';
4224
					}
4225
					print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4226
					print '</option>';
4227
					$i++;
4228
				}
4229
				print "</select>";
4230
				if ($user->admin  && empty($noinfoadmin)) {
4231
					print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4232
				}
4233
4234
				print ajax_combobox('select'.$htmlname);
4235
			} else {
4236
				print $langs->trans("NoShippingMethodDefined");
4237
			}
4238
		} else {
4239
			dol_print_error($this->db);
4240
		}
4241
	}
4242
4243
	/**
4244
	 *    Display form to select shipping mode
4245
	 *
4246
	 *    @param	string	$page        Page
4247
	 *    @param    int		$selected    Id of shipping mode
4248
	 *    @param    string	$htmlname    Name of select html field
4249
	 *    @param    int		$addempty    1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4250
	 *    @return	void
4251
	 */
4252
	public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4253
	{
4254
		global $langs, $db;
4255
4256
		$langs->load("deliveries");
4257
4258
		if ($htmlname != "none") {
4259
			print '<form method="POST" action="'.$page.'">';
4260
			print '<input type="hidden" name="action" value="setshippingmethod">';
4261
			print '<input type="hidden" name="token" value="'.newToken().'">';
4262
			$this->selectShippingMethod($selected, $htmlname, '', $addempty);
4263
			print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4264
			print '</form>';
4265
		} else {
4266
			if ($selected) {
4267
				$code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code');
4268
				print $langs->trans("SendingMethod".strtoupper($code));
4269
			} else {
4270
				print "&nbsp;";
4271
			}
4272
		}
4273
	}
4274
4275
	/**
4276
	 * Creates HTML last in cycle situation invoices selector
4277
	 *
4278
	 * @param     string  $selected   		Preselected ID
4279
	 * @param     int     $socid      		Company ID
4280
	 *
4281
	 * @return    string                     HTML select
4282
	 */
4283
	public function selectSituationInvoices($selected = '', $socid = 0)
4284
	{
4285
		global $langs;
4286
4287
		$langs->load('bills');
4288
4289
		$opt = '<option value ="" selected></option>';
4290
		$sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc';
4291
		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture';
4292
		$sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4293
		$sql .= ' AND situation_counter >= 1';
4294
		$sql .= ' AND fk_soc = '.(int) $socid;
4295
		$sql .= ' AND type <> 2';
4296
		$sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4297
		$resql = $this->db->query($sql);
4298
4299
		if ($resql && $this->db->num_rows($resql) > 0) {
4300
			// Last seen cycle
4301
			$ref = 0;
4302
			while ($obj = $this->db->fetch_object($resql)) {
4303
				//Same cycle ?
4304
				if ($obj->situation_cycle_ref != $ref) {
4305
					// Just seen this cycle
4306
					$ref = $obj->situation_cycle_ref;
4307
					//not final ?
4308
					if ($obj->situation_final != 1) {
4309
						//Not prov?
4310
						if (substr($obj->ref, 1, 4) != 'PROV') {
4311
							if ($selected == $obj->rowid) {
4312
								$opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4313
							} else {
4314
								$opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4315
							}
4316
						}
4317
					}
4318
				}
4319
			}
4320
		} else {
4321
				dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4322
		}
4323
		if ($opt == '<option value ="" selected></option>') {
4324
			$opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4325
		}
4326
		return $opt;
4327
	}
4328
4329
	/**
4330
	 *      Creates HTML units selector (code => label)
4331
	 *
4332
	 *      @param	string	$selected       Preselected Unit ID
4333
	 *      @param  string	$htmlname       Select name
4334
	 *      @param	int		$showempty		Add a nempty line
4335
	 *      @param  string  $unit_type      Restrict to one given unit type
4336
	 * 		@return	string                  HTML select
4337
	 */
4338
	public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4339
	{
4340
		global $langs;
4341
4342
		$langs->load('products');
4343
4344
		$return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4345
4346
		$sql = 'SELECT rowid, label, code from '.MAIN_DB_PREFIX.'c_units';
4347
		$sql .= ' WHERE active > 0';
4348
		if (!empty($unit_type)) {
4349
			$sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4350
		}
4351
4352
		$resql = $this->db->query($sql);
4353
		if ($resql && $this->db->num_rows($resql) > 0) {
4354
			if ($showempty) {
4355
				$return .= '<option value="none"></option>';
4356
			}
4357
4358
			while ($res = $this->db->fetch_object($resql)) {
4359
				$unitLabel = $res->label;
4360
				if (!empty($langs->tab_translate['unit'.$res->code])) {	// check if Translation is available before
4361
					$unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4362
				}
4363
4364
				if ($selected == $res->rowid) {
4365
					$return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4366
				} else {
4367
					$return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4368
				}
4369
			}
4370
			$return .= '</select>';
4371
		}
4372
		return $return;
4373
	}
4374
4375
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4376
	/**
4377
	 *  Return a HTML select list of bank accounts
4378
	 *
4379
	 *  @param	string	$selected           Id account pre-selected
4380
	 *  @param  string	$htmlname           Name of select zone
4381
	 *  @param  int		$status             Status of searched accounts (0=open, 1=closed, 2=both)
4382
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4383
	 *  @param  int		$useempty           1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4384
	 *  @param  string	$moreattrib         To add more attribute on select
4385
	 *  @param	int		$showcurrency		Show currency in label
4386
	 *  @param	string	$morecss			More CSS
4387
	 *  @param	int		$nooutput			1=Return string, do not send to output
4388
	 * 	@return	int							<0 if error, Num of bank account found if OK (0, 1, 2, ...)
4389
	 */
4390
	public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4391
	{
4392
		// phpcs:enable
4393
		global $langs, $conf;
4394
4395
		$out = '';
4396
4397
		$langs->load("admin");
4398
		$num = 0;
4399
4400
		$sql = "SELECT rowid, label, bank, clos as status, currency_code";
4401
		$sql .= " FROM ".MAIN_DB_PREFIX."bank_account";
4402
		$sql .= " WHERE entity IN (".getEntity('bank_account').")";
4403
		if ($status != 2) {
4404
			$sql .= " AND clos = ".(int) $status;
4405
		}
4406
		if ($filtre) {
4407
			$sql .= " AND ".$filtre;
4408
		}
4409
		$sql .= " ORDER BY label";
4410
4411
		dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4412
		$result = $this->db->query($sql);
4413
		if ($result) {
4414
			$num = $this->db->num_rows($result);
4415
			$i = 0;
4416
			if ($num) {
4417
				$out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4418
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4419
					$out .= '<option value="-1">&nbsp;</option>';
4420
				}
4421
4422
				while ($i < $num) {
4423
					$obj = $this->db->fetch_object($result);
4424
					if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4425
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4426
					} else {
4427
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4428
					}
4429
					$out .= trim($obj->label);
4430
					if ($showcurrency) {
4431
						$out .= ' ('.$obj->currency_code.')';
4432
					}
4433
					if ($status == 2 && $obj->status == 1) {
4434
						$out .= ' ('.$langs->trans("Closed").')';
4435
					}
4436
					$out .= '</option>';
4437
					$i++;
4438
				}
4439
				$out .= "</select>";
4440
				$out .= ajax_combobox('select'.$htmlname);
4441
			} else {
4442
				if ($status == 0) {
4443
					$out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4444
				} else {
4445
					$out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4446
				}
4447
			}
4448
		} else {
4449
			dol_print_error($this->db);
4450
		}
4451
4452
		// Output or return
4453
		if (empty($nooutput)) {
4454
			print $out;
4455
		} else {
4456
			return $out;
4457
		}
4458
4459
		return $num;
4460
	}
4461
4462
	/**
4463
	 *  Return a HTML select list of establishment
4464
	 *
4465
	 *  @param	string	$selected           Id establishment pre-selected
4466
	 *  @param  string	$htmlname           Name of select zone
4467
	 *  @param  int		$status             Status of searched establishment (0=open, 1=closed, 2=both)
4468
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4469
	 *  @param  int		$useempty           1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4470
	 *  @param  string	$moreattrib         To add more attribute on select
4471
	 * 	@return	int							<0 if error, Num of establishment found if OK (0, 1, 2, ...)
4472
	 */
4473
	public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4474
	{
4475
		global $langs, $conf;
4476
4477
		$langs->load("admin");
4478
		$num = 0;
4479
4480
		$sql = "SELECT rowid, name, fk_country, status, entity";
4481
		$sql .= " FROM ".MAIN_DB_PREFIX."establishment";
4482
		$sql .= " WHERE 1=1";
4483
		if ($status != 2) {
4484
			$sql .= " AND status = ".(int) $status;
4485
		}
4486
		if ($filtre) {
4487
			$sql .= " AND ".$filtre;
4488
		}
4489
		$sql .= " ORDER BY name";
4490
4491
		dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4492
		$result = $this->db->query($sql);
4493
		if ($result) {
4494
			$num = $this->db->num_rows($result);
4495
			$i = 0;
4496
			if ($num) {
4497
				print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4498
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4499
					print '<option value="-1">&nbsp;</option>';
4500
				}
4501
4502
				while ($i < $num) {
4503
					$obj = $this->db->fetch_object($result);
4504
					if ($selected == $obj->rowid) {
4505
						print '<option value="'.$obj->rowid.'" selected>';
4506
					} else {
4507
						print '<option value="'.$obj->rowid.'">';
4508
					}
4509
					print trim($obj->name);
4510
					if ($status == 2 && $obj->status == 1) {
4511
						print ' ('.$langs->trans("Closed").')';
4512
					}
4513
					print '</option>';
4514
					$i++;
4515
				}
4516
				print "</select>";
4517
			} else {
4518
				if ($status == 0) {
4519
					print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4520
				} else {
4521
					print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4522
				}
4523
			}
4524
		} else {
4525
			dol_print_error($this->db);
4526
		}
4527
	}
4528
4529
	/**
4530
	 *    Display form to select bank account
4531
	 *
4532
	 *    @param	string	$page        Page
4533
	 *    @param    int		$selected    Id of bank account
4534
	 *    @param    string	$htmlname    Name of select html field
4535
	 *    @param    int		$addempty    1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
4536
	 *    @return	void
4537
	 */
4538
	public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4539
	{
4540
		global $langs;
4541
		if ($htmlname != "none") {
4542
			print '<form method="POST" action="'.$page.'">';
4543
			print '<input type="hidden" name="action" value="setbankaccount">';
4544
			print '<input type="hidden" name="token" value="'.newToken().'">';
4545
			print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4546
			$nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4547
			if ($nbaccountfound > 0) {
4548
				print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4549
			}
4550
			print '</form>';
4551
		} else {
4552
			$langs->load('banks');
4553
4554
			if ($selected) {
4555
				require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4556
				$bankstatic = new Account($this->db);
4557
				$result = $bankstatic->fetch($selected);
4558
				if ($result) {
4559
					print $bankstatic->getNomUrl(1);
4560
				}
4561
			} else {
4562
				print "&nbsp;";
4563
			}
4564
		}
4565
	}
4566
4567
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4568
	/**
4569
	 *    Return list of categories having choosed type
4570
	 *
4571
	 *    @param	string|int	            $type				Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
4572
	 *    @param    string		            $selected    		Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1.
4573
	 *    @param    string		            $htmlname			HTML field name
4574
	 *    @param    int			            $maxlength      	Maximum length for labels
4575
	 *    @param    int|string|array    	$markafterid        Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id.
4576
	 *                                                          $markafterid can be an :
4577
	 *                                                          - int (id of category)
4578
	 *                                                          - string (categories ids seprated by comma)
4579
	 *                                                          - array (list of categories ids)
4580
	 *    @param	int			            $outputmode			0=HTML select string, 1=Array
4581
	 *    @param	int			            $include			[=0] Removed or 1=Keep only
4582
	 *    @param	string					$morecss			More CSS
4583
	 *    @return	string
4584
	 *    @see select_categories()
4585
	 */
4586
	public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4587
	{
4588
		// phpcs:enable
4589
		global $conf, $langs;
4590
		$langs->load("categories");
4591
4592
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4593
4594
		// For backward compatibility
4595
		if (is_numeric($type)) {
4596
			dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4597
		}
4598
4599
		if ($type === Categorie::TYPE_BANK_LINE) {
4600
			// TODO Move this into common category feature
4601
			$cate_arbo = array();
4602
			$sql = "SELECT c.label, c.rowid";
4603
			$sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c";
4604
			$sql .= " WHERE entity = ".$conf->entity;
4605
			$sql .= " ORDER BY c.label";
4606
			$result = $this->db->query($sql);
4607
			if ($result) {
4608
				$num = $this->db->num_rows($result);
4609
				$i = 0;
4610
				while ($i < $num) {
4611
					$objp = $this->db->fetch_object($result);
4612
					if ($objp) {
4613
						$cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label);
4614
					}
4615
					$i++;
4616
				}
4617
				$this->db->free($result);
4618
			} else {
4619
				dol_print_error($this->db);
4620
			}
4621
		} else {
4622
			$cat = new Categorie($this->db);
4623
			$cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4624
		}
4625
4626
		$output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4627
		$outarray = array();
4628
		if (is_array($cate_arbo)) {
4629
			if (!count($cate_arbo)) {
4630
				$output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4631
			} else {
4632
				$output .= '<option value="-1">&nbsp;</option>';
4633
				foreach ($cate_arbo as $key => $value) {
4634
					if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4635
						$add = 'selected ';
4636
					} else {
4637
						$add = '';
4638
					}
4639
					$output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'">'.dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle').'</option>';
4640
4641
					$outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4642
				}
4643
			}
4644
		}
4645
		$output .= '</select>';
4646
		$output .= "\n";
4647
4648
		if ($outputmode) {
4649
			return $outarray;
4650
		}
4651
		return $output;
4652
	}
4653
4654
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4655
	/**
4656
	 *     Show a confirmation HTML form or AJAX popup
4657
	 *
4658
	 *     @param	string		$page        	   	Url of page to call if confirmation is OK
4659
	 *     @param	string		$title       	   	Title
4660
	 *     @param	string		$question    	   	Question
4661
	 *     @param 	string		$action      	   	Action
4662
	 *	   @param	array		$formquestion	   	An array with forms complementary inputs
4663
	 * 	   @param	string		$selectedchoice		"" or "no" or "yes"
4664
	 * 	   @param	int			$useajax		   	0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=preoutput confirm box with div id=dialog-confirm-xxx
4665
	 *     @param	int			$height          	Force height of box
4666
	 *     @param	int			$width				Force width of box
4667
	 *     @return 	void
4668
	 *     @deprecated
4669
	 *     @see formconfirm()
4670
	 */
4671
	public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4672
	{
4673
		// phpcs:enable
4674
		dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4675
		print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4676
	}
4677
4678
	/**
4679
	 *     Show a confirmation HTML form or AJAX popup.
4680
	 *     Easiest way to use this is with useajax=1.
4681
	 *     If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters)
4682
	 *     just after calling this method. For example:
4683
	 *       print '<script type="text/javascript">'."\n";
4684
	 *       print 'jQuery(document).ready(function() {'."\n";
4685
	 *       print 'jQuery(".xxxlink").click(function(e) { jQuery("#aparamid").val(jQuery(this).attr("rel")); jQuery("#dialog-confirm-xxx").dialog("open"); return false; });'."\n";
4686
	 *       print '});'."\n";
4687
	 *       print '</script>'."\n";
4688
	 *
4689
	 *     @param  	string			$page        	   	Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated)
4690
	 *     @param	string			$title       	   	Title
4691
	 *     @param	string			$question    	   	Question
4692
	 *     @param 	string			$action      	   	Action
4693
	 *	   @param  	array|string	$formquestion	   	An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>))
4694
	 *													type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', 'other' or 'onecolumn'...
4695
	 * 	   @param  	string			$selectedchoice  	'' or 'no', or 'yes' or '1' or '0'
4696
	 * 	   @param  	int|string		$useajax		   	0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx
4697
	 *     @param  	int|string		$height          	Force height of box (0 = auto)
4698
	 *     @param	int				$width				Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones.
4699
	 *     @param	int				$disableformtag		1=Disable form tag. Can be used if we are already inside a <form> section.
4700
	 *     @return 	string      		    			HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form
4701
	 */
4702
	public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0)
4703
	{
4704
		global $langs, $conf;
4705
4706
		$more = '<!-- formconfirm before calling page='.dol_escape_htmltag($page).' -->';
4707
		$formconfirm = '';
4708
		$inputok = array();
4709
		$inputko = array();
4710
4711
		// Clean parameters
4712
		$newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
4713
		if ($conf->browser->layout == 'phone') {
4714
			$width = '95%';
4715
		}
4716
4717
		// Set height automatically if not defined
4718
		if (empty($height)) {
4719
			$height = 220;
4720
			if (is_array($formquestion) && count($formquestion) > 2) {
4721
				$height += ((count($formquestion) - 2) * 24);
4722
			}
4723
		}
4724
4725
		if (is_array($formquestion) && !empty($formquestion)) {
4726
			// First add hidden fields and value
4727
			foreach ($formquestion as $key => $input) {
4728
				if (is_array($input) && !empty($input)) {
4729
					if ($input['type'] == 'hidden') {
4730
						$more .= '<input type="hidden" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'" value="'.dol_escape_htmltag($input['value']).'">'."\n";
4731
					}
4732
				}
4733
			}
4734
4735
			// Now add questions
4736
			$moreonecolumn = '';
4737
			$more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
4738
			foreach ($formquestion as $key => $input) {
4739
				if (is_array($input) && !empty($input)) {
4740
					$size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : '');	// deprecated. Use morecss instead.
4741
					$moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4742
					$morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4743
4744
					if ($input['type'] == 'text') {
4745
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd"><input type="text" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$size.' value="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4746
					} elseif ($input['type'] == 'password')	{
4747
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd"><input type="password" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$size.' value="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4748
					} elseif ($input['type'] == 'textarea') {
4749
						/*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
4750
						$more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
4751
						$more .= $input['value'];
4752
						$more .= '</textarea>';
4753
						$more .= '</div></div>'."\n";*/
4754
						$moreonecolumn .= '<div class="margintoponly">';
4755
						$moreonecolumn .= $input['label'].'<br>';
4756
						$moreonecolumn .= '<textarea name="'.dol_escape_htmltag($input['name']).'" id="'.dol_escape_htmltag($input['name']).'" class="'.$morecss.'"'.$moreattr.'>';
4757
						$moreonecolumn .= $input['value'];
4758
						$moreonecolumn .= '</textarea>';
4759
						$moreonecolumn .= '</div>';
4760
					} elseif ($input['type'] == 'select') {
4761
						if (empty($morecss)) {
4762
							$morecss = 'minwidth100';
4763
						}
4764
4765
						$show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
4766
						$key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
4767
						$value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
4768
						$translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
4769
						$maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
4770
						$disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
4771
						$sort = isset($input['select_sort']) ? $input['select_sort'] : '';
4772
4773
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4774
						if (!empty($input['label'])) {
4775
							$more .= $input['label'].'</div><div class="tagtd left">';
4776
						}
4777
						$more .= $this->selectarray($input['name'], $input['values'], $input['default'], $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
4778
						$more .= '</div></div>'."\n";
4779
					} elseif ($input['type'] == 'checkbox') {
4780
						$more .= '<div class="tagtr">';
4781
						$more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
4782
						$more .= '<input type="checkbox" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$moreattr;
4783
						if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0') {
4784
							$more .= ' checked';
4785
						}
4786
						if (is_bool($input['value']) && $input['value']) {
4787
							$more .= ' checked';
4788
						}
4789
						if (isset($input['disabled'])) {
4790
							$more .= ' disabled';
4791
						}
4792
						$more .= ' /></div>';
4793
						$more .= '</div>'."\n";
4794
					} elseif ($input['type'] == 'radio') {
4795
						$i = 0;
4796
						foreach ($input['values'] as $selkey => $selval) {
4797
							$more .= '<div class="tagtr">';
4798
							if ($i == 0) {
4799
								$more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
4800
							} else {
4801
								$more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
4802
							}
4803
							$more .= '<div class="tagtd'.($i == 0 ? ' tdtop' : '').'"><input type="radio" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name'].$selkey).'" name="'.dol_escape_htmltag($input['name']).'" value="'.$selkey.'"'.$moreattr;
4804
							if ($input['disabled']) {
4805
								$more .= ' disabled';
4806
							}
4807
							if (isset($input['default']) && $input['default'] === $selkey) {
4808
								$more .= ' checked="checked"';
4809
							}
4810
							$more .= ' /> ';
4811
							$more .= '<label for="'.dol_escape_htmltag($input['name'].$selkey).'">'.$selval.'</label>';
4812
							$more .= '</div></div>'."\n";
4813
							$i++;
4814
						}
4815
					} elseif ($input['type'] == 'date') {
4816
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
4817
						$more .= '<div class="tagtd">';
4818
						$addnowlink = (empty($input['datenow']) ? 0 : 1);
4819
						$more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, $addnowlink);
4820
						$more .= '</div></div>'."\n";
4821
						$formquestion[] = array('name'=>$input['name'].'day');
4822
						$formquestion[] = array('name'=>$input['name'].'month');
4823
						$formquestion[] = array('name'=>$input['name'].'year');
4824
						$formquestion[] = array('name'=>$input['name'].'hour');
4825
						$formquestion[] = array('name'=>$input['name'].'min');
4826
					} elseif ($input['type'] == 'other') {
4827
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4828
						if (!empty($input['label'])) {
4829
							$more .= $input['label'].'</div><div class="tagtd">';
4830
						}
4831
						$more .= $input['value'];
4832
						$more .= '</div></div>'."\n";
4833
					} elseif ($input['type'] == 'onecolumn') {
4834
						$moreonecolumn .= '<div class="margintoponly">';
4835
						$moreonecolumn .= $input['value'];
4836
						$moreonecolumn .= '</div>'."\n";
4837
					} elseif ($input['type'] == 'hidden') {
4838
						// Do nothing more, already added by a previous loop
4839
					} else {
4840
						$more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
4841
					}
4842
				}
4843
			}
4844
			$more .= '</div>'."\n";
4845
			$more .= $moreonecolumn;
4846
		}
4847
4848
		// JQUI method dialog is broken with jmobile, we use standard HTML.
4849
		// Note: When using dol_use_jmobile or no js, you must also check code for button use a GET url with action=xxx and check that you also output the confirm code when action=xxx
4850
		// See page product/card.php for example
4851
		if (!empty($conf->dol_use_jmobile)) {
4852
			$useajax = 0;
4853
		}
4854
		if (empty($conf->use_javascript_ajax)) {
4855
			$useajax = 0;
4856
		}
4857
4858
		if ($useajax) {
4859
			$autoOpen = true;
4860
			$dialogconfirm = 'dialog-confirm';
4861
			$button = '';
4862
			if (!is_numeric($useajax)) {
4863
				$button = $useajax;
4864
				$useajax = 1;
4865
				$autoOpen = false;
4866
				$dialogconfirm .= '-'.$button;
4867
			}
4868
			$pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes';
4869
			$pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : '');
4870
4871
			// Add input fields into list of fields to read during submit (inputok and inputko)
4872
			if (is_array($formquestion)) {
4873
				foreach ($formquestion as $key => $input) {
4874
					//print "xx ".$key." rr ".is_array($input)."<br>\n";
4875
					// Add name of fields to propagate with the GET when submitting the form with button OK.
4876
					if (is_array($input) && isset($input['name'])) {
4877
						if (strpos($input['name'], ',') > 0) {
4878
							$inputok = array_merge($inputok, explode(',', $input['name']));
4879
						} else {
4880
							array_push($inputok, $input['name']);
4881
						}
4882
					}
4883
					// Add name of fields to propagate with the GET when submitting the form with button KO.
4884
					if (isset($input['inputko']) && $input['inputko'] == 1) {
4885
						array_push($inputko, $input['name']);
4886
					}
4887
				}
4888
			}
4889
4890
			// Show JQuery confirm box.
4891
			$formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
4892
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4893
				$formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
4894
			}
4895
			if (!empty($more)) {
4896
				$formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
4897
			}
4898
			$formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
4899
			$formconfirm .= '</div>'."\n";
4900
4901
			$formconfirm .= "\n<!-- begin ajax formconfirm page=".$page." -->\n";
4902
			$formconfirm .= '<script type="text/javascript">'."\n";
4903
			$formconfirm .= 'jQuery(document).ready(function() {
4904
            $(function() {
4905
            	$( "#'.$dialogconfirm.'" ).dialog(
4906
            	{
4907
                    autoOpen: '.($autoOpen ? "true" : "false").',';
4908
			if ($newselectedchoice == 'no') {
4909
				$formconfirm .= '
4910
						open: function() {
4911
            				$(this).parent().find("button.ui-button:eq(2)").focus();
4912
						},';
4913
			}
4914
			$formconfirm .= '
4915
                    resizable: false,
4916
                    height: "'.$height.'",
4917
                    width: "'.$width.'",
4918
                    modal: true,
4919
                    closeOnEscape: false,
4920
                    buttons: {
4921
                        "'.dol_escape_js($langs->transnoentities("Yes")).'": function() {
4922
                        	var options = "&token='.urlencode(newToken()).'";
4923
                        	var inputok = '.json_encode($inputok).';	/* List of fields into form */
4924
                         	var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
4925
                         	if (inputok.length>0) {
4926
                         		$.each(inputok, function(i, inputname) {
4927
                         			var more = "";
4928
									var inputvalue;
4929
                         			if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
4930
										inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
4931
									} else {
4932
                         		    	if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4933
                         				inputvalue = $("#" + inputname + more).val();
4934
									}
4935
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4936
									console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
4937
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4938
                         		});
4939
                         	}
4940
                         	var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "") + options;
4941
            				if (pageyes.length > 0) { location.href = urljump; }
4942
                            $(this).dialog("close");
4943
                        },
4944
                        "'.dol_escape_js($langs->transnoentities("No")).'": function() {
4945
                        	var options = "&token='.urlencode(newToken()).'";
4946
                         	var inputko = '.json_encode($inputko).';	/* List of fields into form */
4947
                         	var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
4948
                         	if (inputko.length>0) {
4949
                         		$.each(inputko, function(i, inputname) {
4950
                         			var more = "";
4951
                         			if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4952
                         			var inputvalue = $("#" + inputname + more).val();
4953
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4954
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4955
                         		});
4956
                         	}
4957
                         	var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "") + options;
4958
                         	//alert(urljump);
4959
            				if (pageno.length > 0) { location.href = urljump; }
4960
                            $(this).dialog("close");
4961
                        }
4962
                    }
4963
                }
4964
                );
4965
4966
            	var button = "'.$button.'";
4967
            	if (button.length > 0) {
4968
                	$( "#" + button ).click(function() {
4969
                		$("#'.$dialogconfirm.'").dialog("open");
4970
        			});
4971
                }
4972
            });
4973
            });
4974
            </script>';
4975
			$formconfirm .= "<!-- end ajax formconfirm -->\n";
4976
		} else {
4977
			$formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
4978
4979
			if (empty($disableformtag)) {
4980
				$formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
4981
			}
4982
4983
			$formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
4984
			$formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
4985
4986
			$formconfirm .= '<table class="valid centpercent">'."\n";
4987
4988
			// Line title
4989
			$formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
4990
			$formconfirm .= img_picto('', 'recent').' '.$title;
4991
			$formconfirm .= '</td></tr>'."\n";
4992
4993
			// Line text
4994
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4995
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
4996
			}
4997
4998
			// Line form fields
4999
			if ($more) {
5000
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
5001
				$formconfirm .= $more;
5002
				$formconfirm .= '</td></tr>'."\n";
5003
			}
5004
5005
			// Line with question
5006
			$formconfirm .= '<tr class="valid">';
5007
			$formconfirm .= '<td class="valid">'.$question.'</td>';
5008
			$formconfirm .= '<td class="valid center">';
5009
			$formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly');
5010
			$formconfirm .= '<input class="button valignmiddle confirmvalidatebutton" type="submit" value="'.$langs->trans("Validate").'">';
5011
			$formconfirm .= '</td>';
5012
			$formconfirm .= '</tr>'."\n";
5013
5014
			$formconfirm .= '</table>'."\n";
5015
5016
			if (empty($disableformtag)) {
5017
				$formconfirm .= "</form>\n";
5018
			}
5019
			$formconfirm .= '<br>';
5020
5021
			if (empty($conf->use_javascript_ajax)) {
5022
				$formconfirm .= '<!-- code to disable button to avoid double clic -->';
5023
				$formconfirm .= '<script type="text/javascript">'."\n";
5024
				$formconfirm .= '
5025
				$(document).ready(function () {
5026
					$(".confirmvalidatebutton").on("click", function() {
5027
						console.log("We click on button");
5028
						$(this).attr("disabled", "disabled");
5029
						setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5030
						//console.log($(this).closest("form"));
5031
						$(this).closest("form").submit();
5032
					});
5033
				});
5034
				';
5035
				$formconfirm .= '</script>'."\n";
5036
			}
5037
5038
			$formconfirm .= "<!-- end formconfirm -->\n";
5039
		}
5040
5041
		return $formconfirm;
5042
	}
5043
5044
5045
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5046
	/**
5047
	 *    Show a form to select a project
5048
	 *
5049
	 *    @param	int		$page        		Page
5050
	 *    @param	int		$socid       		Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id)
5051
	 *    @param    int		$selected    		Id pre-selected project
5052
	 *    @param    string	$htmlname    		Name of select field
5053
	 *    @param	int		$discard_closed		Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable)
5054
	 *    @param	int		$maxlength			Max length
5055
	 *    @param	int		$forcefocus			Force focus on field (works with javascript only)
5056
	 *    @param    int     $nooutput           No print is done. String is returned.
5057
	 *    @return	string                      Return html content
5058
	 */
5059
	public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0)
5060
	{
5061
		// phpcs:enable
5062
		global $langs;
5063
5064
		require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5065
		require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5066
5067
		$out = '';
5068
5069
		$formproject = new FormProjets($this->db);
5070
5071
		$langs->load("project");
5072
		if ($htmlname != "none") {
5073
			$out .= "\n";
5074
			$out .= '<form method="post" action="'.$page.'">';
5075
			$out .= '<input type="hidden" name="action" value="classin">';
5076
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5077
			$out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1);
5078
			$out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5079
			$out .= '</form>';
5080
		} else {
5081
			if ($selected) {
5082
				$projet = new Project($this->db);
5083
				$projet->fetch($selected);
5084
				//print '<a href="'.DOL_URL_ROOT.'/projet/card.php?id='.$selected.'">'.$projet->title.'</a>';
5085
				$out .= $projet->getNomUrl(0, '', 1);
5086
			} else {
5087
				$out .= "&nbsp;";
5088
			}
5089
		}
5090
5091
		if (empty($nooutput)) {
5092
			print $out;
5093
			return '';
5094
		}
5095
		return $out;
5096
	}
5097
5098
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5099
	/**
5100
	 *	Show a form to select payment conditions
5101
	 *
5102
	 *  @param	int		$page        	Page
5103
	 *  @param  string	$selected    	Id condition pre-selectionne
5104
	 *  @param  string	$htmlname    	Name of select html field
5105
	 *	@param	int		$addempty		Add empty entry
5106
	 *  @return	void
5107
	 */
5108
	public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0)
5109
	{
5110
		// phpcs:enable
5111
		global $langs;
5112
		if ($htmlname != "none") {
5113
			print '<form method="post" action="'.$page.'">';
5114
			print '<input type="hidden" name="action" value="setconditions">';
5115
			print '<input type="hidden" name="token" value="'.newToken().'">';
5116
			$this->select_conditions_paiements($selected, $htmlname, -1, $addempty);
5117
			print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5118
			print '</form>';
5119
		} else {
5120
			if ($selected) {
5121
				$this->load_cache_conditions_paiements();
5122
				if (isset($this->cache_conditions_paiements[$selected])) {
5123
					print $this->cache_conditions_paiements[$selected]['label'];
5124
				} else {
5125
					$langs->load('errors');
5126
					print $langs->trans('ErrorNotInDictionaryPaymentConditions');
5127
				}
5128
			} else {
5129
				print "&nbsp;";
5130
			}
5131
		}
5132
	}
5133
5134
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5135
	/**
5136
	 *  Show a form to select a delivery delay
5137
	 *
5138
	 *  @param  int		$page        	Page
5139
	 *  @param  string	$selected    	Id condition pre-selectionne
5140
	 *  @param  string	$htmlname    	Name of select html field
5141
	 *	@param	int		$addempty		Ajoute entree vide
5142
	 *  @return	void
5143
	 */
5144
	public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5145
	{
5146
		// phpcs:enable
5147
		global $langs;
5148
		if ($htmlname != "none") {
5149
			print '<form method="post" action="'.$page.'">';
5150
			print '<input type="hidden" name="action" value="setavailability">';
5151
			print '<input type="hidden" name="token" value="'.newToken().'">';
5152
			$this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5153
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5154
			print '</form>';
5155
		} else {
5156
			if ($selected) {
5157
				$this->load_cache_availability();
5158
				print $this->cache_availability[$selected]['label'];
5159
			} else {
5160
				print "&nbsp;";
5161
			}
5162
		}
5163
	}
5164
5165
	/**
5166
	 *  Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
5167
	 *  List found into table c_input_reason loaded by loadCacheInputReason
5168
	 *
5169
	 *  @param  string	$page        	Page
5170
	 *  @param  string	$selected    	Id condition pre-selectionne
5171
	 *  @param  string	$htmlname    	Name of select html field
5172
	 *  @param	int		$addempty		Add empty entry
5173
	 *  @return	void
5174
	 */
5175
	public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5176
	{
5177
		global $langs;
5178
		if ($htmlname != "none") {
5179
			print '<form method="post" action="'.$page.'">';
5180
			print '<input type="hidden" name="action" value="setdemandreason">';
5181
			print '<input type="hidden" name="token" value="'.newToken().'">';
5182
			$this->selectInputReason($selected, $htmlname, -1, $addempty);
5183
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5184
			print '</form>';
5185
		} else {
5186
			if ($selected) {
5187
				$this->loadCacheInputReason();
5188
				foreach ($this->cache_demand_reason as $key => $val) {
5189
					if ($val['id'] == $selected) {
5190
						print $val['label'];
5191
						break;
5192
					}
5193
				}
5194
			} else {
5195
				print "&nbsp;";
5196
			}
5197
		}
5198
	}
5199
5200
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5201
	/**
5202
	 *    Show a form + html select a date
5203
	 *
5204
	 *    @param	string		$page        	Page
5205
	 *    @param	string		$selected    	Date preselected
5206
	 *    @param    string		$htmlname    	Html name of date input fields or 'none'
5207
	 *    @param    int			$displayhour 	Display hour selector
5208
	 *    @param    int			$displaymin		Display minutes selector
5209
	 *    @param	int			$nooutput		1=No print output, return string
5210
	 *    @return	string
5211
	 *    @see		selectDate()
5212
	 */
5213
	public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0)
5214
	{
5215
		// phpcs:enable
5216
		global $langs;
5217
5218
		$ret = '';
5219
5220
		if ($htmlname != "none") {
5221
			$ret .= '<form method="post" action="'.$page.'" name="form'.$htmlname.'">';
5222
			$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5223
			$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5224
			$ret .= '<table class="nobordernopadding">';
5225
			$ret .= '<tr><td>';
5226
			$ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5227
			$ret .= '</td>';
5228
			$ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5229
			$ret .= '</tr></table></form>';
5230
		} else {
5231
			if ($displayhour) {
5232
				$ret .= dol_print_date($selected, 'dayhour');
5233
			} else {
5234
				$ret .= dol_print_date($selected, 'day');
5235
			}
5236
		}
5237
5238
		if (empty($nooutput)) {
5239
			print $ret;
5240
		}
5241
		return $ret;
5242
	}
5243
5244
5245
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5246
	/**
5247
	 *  Show a select form to choose a user
5248
	 *
5249
	 *  @param	string	$page        	Page
5250
	 *  @param  string	$selected    	Id of user preselected
5251
	 *  @param  string	$htmlname    	Name of input html field. If 'none', we just output the user link.
5252
	 *  @param  array	$exclude		List of users id to exclude
5253
	 *  @param  array	$include        List of users id to include
5254
	 *  @return	void
5255
	 */
5256
	public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5257
	{
5258
		// phpcs:enable
5259
		global $langs;
5260
5261
		if ($htmlname != "none") {
5262
			print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5263
			print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5264
			print '<input type="hidden" name="token" value="'.newToken().'">';
5265
			print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5266
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5267
			print '</form>';
5268
		} else {
5269
			if ($selected) {
5270
				require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5271
				$theuser = new User($this->db);
5272
				$theuser->fetch($selected);
5273
				print $theuser->getNomUrl(1);
5274
			} else {
5275
				print "&nbsp;";
5276
			}
5277
		}
5278
	}
5279
5280
5281
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5282
	/**
5283
	 *    Show form with payment mode
5284
	 *
5285
	 *    @param	string	$page        	Page
5286
	 *    @param    int		$selected    	Id mode pre-selectionne
5287
	 *    @param    string	$htmlname    	Name of select html field
5288
	 *    @param  	string	$filtertype		To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz))
5289
	 *    @param    int     $active         Active or not, -1 = all
5290
	 *    @param   int     $addempty       1=Add empty entry
5291
	 *    @return	void
5292
	 */
5293
	public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0)
5294
	{
5295
		// phpcs:enable
5296
		global $langs;
5297
		if ($htmlname != "none") {
5298
			print '<form method="POST" action="'.$page.'">';
5299
			print '<input type="hidden" name="action" value="setmode">';
5300
			print '<input type="hidden" name="token" value="'.newToken().'">';
5301
			$this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active);
5302
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5303
			print '</form>';
5304
		} else {
5305
			if ($selected) {
5306
				$this->load_cache_types_paiements();
5307
				print $this->cache_types_paiements[$selected]['label'];
5308
			} else {
5309
				print "&nbsp;";
5310
			}
5311
		}
5312
	}
5313
5314
	/**
5315
	 *    Show form with transport mode
5316
	 *
5317
	 *    @param	string	$page        	Page
5318
	 *    @param    int		$selected    	Id mode pre-select
5319
	 *    @param    string	$htmlname    	Name of select html field
5320
	 *    @param    int     $active         Active or not, -1 = all
5321
	 *    @param    int     $addempty       1=Add empty entry
5322
	 *    @return	void
5323
	 */
5324
	public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5325
	{
5326
		global $langs;
5327
		if ($htmlname != "none") {
5328
			print '<form method="POST" action="'.$page.'">';
5329
			print '<input type="hidden" name="action" value="settransportmode">';
5330
			print '<input type="hidden" name="token" value="'.newToken().'">';
5331
			$this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5332
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5333
			print '</form>';
5334
		} else {
5335
			if ($selected) {
5336
				$this->load_cache_transport_mode();
5337
				print $this->cache_transport_mode[$selected]['label'];
5338
			} else {
5339
				print "&nbsp;";
5340
			}
5341
		}
5342
	}
5343
5344
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5345
	/**
5346
	 *    Show form with multicurrency code
5347
	 *
5348
	 *    @param	string	$page        	Page
5349
	 *    @param    string	$selected    	code pre-selectionne
5350
	 *    @param    string	$htmlname    	Name of select html field
5351
	 *    @return	void
5352
	 */
5353
	public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5354
	{
5355
		// phpcs:enable
5356
		global $langs;
5357
		if ($htmlname != "none") {
5358
			print '<form method="POST" action="'.$page.'">';
5359
			print '<input type="hidden" name="action" value="setmulticurrencycode">';
5360
			print '<input type="hidden" name="token" value="'.newToken().'">';
5361
			print $this->selectMultiCurrency($selected, $htmlname, 0);
5362
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5363
			print '</form>';
5364
		} else {
5365
			dol_include_once('/core/lib/company.lib.php');
5366
			print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5367
		}
5368
	}
5369
5370
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5371
	/**
5372
	 *    Show form with multicurrency rate
5373
	 *
5374
	 *    @param	string	$page        	Page
5375
	 *    @param    double	$rate	    	Current rate
5376
	 *    @param    string	$htmlname    	Name of select html field
5377
	 *    @param    string  $currency       Currency code to explain the rate
5378
	 *    @return	void
5379
	 */
5380
	public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5381
	{
5382
		// phpcs:enable
5383
		global $langs, $mysoc, $conf;
5384
5385
		if ($htmlname != "none") {
5386
			print '<form method="POST" action="'.$page.'">';
5387
			print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5388
			print '<input type="hidden" name="token" value="'.newToken().'">';
5389
			print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5390
			print '<select name="calculation_mode">';
5391
			print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5392
			print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5393
			print '</select> ';
5394
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5395
			print '</form>';
5396
		} else {
5397
			if (!empty($rate)) {
5398
				print price($rate, 1, $langs, 1, 0);
5399
				if ($currency && $rate != 1) {
5400
					print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5401
				}
5402
			} else {
5403
				print 1;
5404
			}
5405
		}
5406
	}
5407
5408
5409
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5410
	/**
5411
	 *	Show a select box with available absolute discounts
5412
	 *
5413
	 *  @param  string	$page        	Page URL where form is shown
5414
	 *  @param  int		$selected    	Value pre-selected
5415
	 *	@param  string	$htmlname    	Name of SELECT component. If 'none', not changeable. Example 'remise_id'.
5416
	 *	@param	int		$socid			Third party id
5417
	 * 	@param	float	$amount			Total amount available
5418
	 * 	@param	string	$filter			SQL filter on discounts
5419
	 * 	@param	int		$maxvalue		Max value for lines that can be selected
5420
	 *  @param  string	$more           More string to add
5421
	 *  @param  int     $hidelist       1=Hide list
5422
	 *  @param	int		$discount_type	0 => customer discount, 1 => supplier discount
5423
	 *  @return	void
5424
	 */
5425
	public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5426
	{
5427
		// phpcs:enable
5428
		global $conf, $langs;
5429
		if ($htmlname != "none") {
5430
			print '<form method="post" action="'.$page.'">';
5431
			print '<input type="hidden" name="action" value="setabsolutediscount">';
5432
			print '<input type="hidden" name="token" value="'.newToken().'">';
5433
			print '<div class="inline-block">';
5434
			if (!empty($discount_type)) {
5435
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5436
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5437
						$translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5438
					} else {
5439
						$translationKey = 'HasCreditNoteFromSupplier';
5440
					}
5441
				} else {
5442
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5443
						$translationKey = 'HasAbsoluteDiscountFromSupplier';
5444
					} else {
5445
						$translationKey = 'HasCreditNoteFromSupplier';
5446
					}
5447
				}
5448
			} else {
5449
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5450
					if (!$filter || $filter == "fk_facture_source IS NULL") {
5451
						$translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5452
					} else {
5453
						$translationKey = 'CompanyHasCreditNote';
5454
					}
5455
				} else {
5456
					if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5457
						$translationKey = 'CompanyHasAbsoluteDiscount';
5458
					} else {
5459
						$translationKey = 'CompanyHasCreditNote';
5460
					}
5461
				}
5462
			}
5463
			print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5464
			if (empty($hidelist)) {
5465
				print ' ';
5466
			}
5467
			print '</div>';
5468
			if (empty($hidelist)) {
5469
				print '<div class="inline-block" style="padding-right: 10px">';
5470
				$newfilter = 'discount_type='.intval($discount_type);
5471
				if (!empty($discount_type)) {
5472
					$newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5473
				} else {
5474
					$newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5475
				}
5476
				if ($filter) {
5477
					$newfilter .= ' AND ('.$filter.')';
5478
				}
5479
				$nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5480
				if ($nbqualifiedlines > 0) {
5481
					print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5482
					if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5483
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5484
					}
5485
					if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5486
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5487
					}
5488
5489
					print '>';
5490
				}
5491
				print '</div>';
5492
			}
5493
			if ($more) {
5494
				print '<div class="inline-block">';
5495
				print $more;
5496
				print '</div>';
5497
			}
5498
			print '</form>';
5499
		} else {
5500
			if ($selected) {
5501
				print $selected;
5502
			} else {
5503
				print "0";
5504
			}
5505
		}
5506
	}
5507
5508
5509
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5510
	/**
5511
	 *  Show forms to select a contact
5512
	 *
5513
	 *  @param	string		$page        	Page
5514
	 *  @param	Societe		$societe		Filter on third party
5515
	 *  @param    int			$selected    	Id contact pre-selectionne
5516
	 *  @param    string		$htmlname    	Name of HTML select. If 'none', we just show contact link.
5517
	 *  @return	void
5518
	 */
5519
	public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5520
	{
5521
		// phpcs:enable
5522
		global $langs, $conf;
5523
5524
		if ($htmlname != "none") {
5525
			print '<form method="post" action="'.$page.'">';
5526
			print '<input type="hidden" name="action" value="set_contact">';
5527
			print '<input type="hidden" name="token" value="'.newToken().'">';
5528
			print '<table class="nobordernopadding">';
5529
			print '<tr><td>';
5530
			print $this->selectcontacts($societe->id, $selected, $htmlname);
5531
			$num = $this->num;
5532
			if ($num == 0) {
5533
				$addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5534
				print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5535
			}
5536
			print '</td>';
5537
			print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5538
			print '</tr></table></form>';
5539
		} else {
5540
			if ($selected) {
5541
				require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5542
				$contact = new Contact($this->db);
5543
				$contact->fetch($selected);
5544
				print $contact->getFullName($langs);
5545
			} else {
5546
				print "&nbsp;";
5547
			}
5548
		}
5549
	}
5550
5551
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5552
	/**
5553
	 *  Output html select to select thirdparty
5554
	 *
5555
	 *  @param	string	$page       	Page
5556
	 *  @param  string	$selected   	Id preselected
5557
	 *  @param  string	$htmlname		Name of HTML select
5558
	 *  @param  string	$filter         Optional filters criteras. Do not use a filter coming from input of users.
5559
	 *	@param	int		$showempty		Add an empty field
5560
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
5561
	 * 	@param	int		$forcecombo		Force to use combo box
5562
	 *  @param	array	$events			Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
5563
	 *  @param  int     $nooutput       No print output. Return it only.
5564
	 *  @param	array	$excludeids		Exclude IDs from the select combo
5565
	 *  @return	void|string
5566
	 */
5567
	public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array())
5568
	{
5569
		// phpcs:enable
5570
		global $langs;
5571
5572
		$out = '';
5573
		if ($htmlname != "none") {
5574
			$out .= '<form method="post" action="'.$page.'">';
5575
			$out .= '<input type="hidden" name="action" value="set_thirdparty">';
5576
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5577
			$out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
5578
			$out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5579
			$out .= '</form>';
5580
		} else {
5581
			if ($selected) {
5582
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5583
				$soc = new Societe($this->db);
5584
				$soc->fetch($selected);
5585
				$out .= $soc->getNomUrl($langs);
5586
			} else {
5587
				$out .= "&nbsp;";
5588
			}
5589
		}
5590
5591
		if ($nooutput) {
5592
			return $out;
5593
		} else {
5594
			print $out;
5595
		}
5596
	}
5597
5598
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5599
	/**
5600
	 *    Retourne la liste des devises, dans la langue de l'utilisateur
5601
	 *
5602
	 *    @param	string	$selected    preselected currency code
5603
	 *    @param    string	$htmlname    name of HTML select list
5604
	 *    @deprecated
5605
	 *    @return	void
5606
	 */
5607
	public function select_currency($selected = '', $htmlname = 'currency_id')
5608
	{
5609
		// phpcs:enable
5610
		print $this->selectCurrency($selected, $htmlname);
5611
	}
5612
5613
	/**
5614
	 *  Retourne la liste des devises, dans la langue de l'utilisateur
5615
	 *
5616
	 *  @param	string	$selected    preselected currency code
5617
	 *  @param  string	$htmlname    name of HTML select list
5618
	 *  @param  string  $mode        0 = Add currency symbol into label, 1 = Add 3 letter iso code
5619
	 * 	@return	string
5620
	 */
5621
	public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0)
5622
	{
5623
		global $conf, $langs, $user;
5624
5625
		$langs->loadCacheCurrencies('');
5626
5627
		$out = '';
5628
5629
		if ($selected == 'euro' || $selected == 'euros') {
5630
			$selected = 'EUR'; // Pour compatibilite
5631
		}
5632
5633
		$out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
5634
		foreach ($langs->cache_currencies as $code_iso => $currency) {
5635
			$labeltoshow = $currency['label'];
5636
			if ($mode == 1) {
5637
				$labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
5638
			} else {
5639
				$labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
5640
			}
5641
5642
			if ($selected && $selected == $code_iso) {
5643
				$out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
5644
			} else {
5645
				$out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
5646
			}
5647
			$out .= $labeltoshow;
5648
			$out .= '</option>';
5649
		}
5650
		$out .= '</select>';
5651
		if ($user->admin) {
5652
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5653
		}
5654
5655
		// Make select dynamic
5656
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5657
		$out .= ajax_combobox($htmlname);
5658
5659
		return $out;
5660
	}
5661
5662
	/**
5663
	 *	Return array of currencies in user language
5664
	 *
5665
	 *  @param	string	$selected    preselected currency code
5666
	 *  @param  string	$htmlname    name of HTML select list
5667
	 *  @param  integer	$useempty    1=Add empty line
5668
	 *  @param string $filter Optional filters criteras (example: 'code <> x', ' in (1,3)')
5669
	 *  @param bool $excludeConfCurrency false  = If company current currency not in table, we add it into list. Should always be available.  true = we are in currency_rate update , we don't want to see conf->currency in select
5670
	 * 	@return	string
5671
	 */
5672
	public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false)
5673
	{
5674
		global $db, $conf, $langs, $user;
5675
5676
		$langs->loadCacheCurrencies(''); // Load ->cache_currencies
5677
5678
		$TCurrency = array();
5679
5680
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency';
5681
		$sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
5682
		if ($filter) {
5683
			$sql .= " AND ".$filter;
5684
		}
5685
		$resql = $this->db->query($sql);
5686
		if ($resql) {
5687
			while ($obj = $this->db->fetch_object($resql)) {
5688
				$TCurrency[$obj->code] = $obj->code;
5689
			}
5690
		}
5691
5692
		$out = '';
5693
		$out .= '<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">';
5694
		if ($useempty) {
5695
			$out .= '<option value="">&nbsp;</option>';
5696
		}
5697
		// If company current currency not in table, we add it into list. Should always be available.
5698
		if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
5699
			$TCurrency[$conf->currency] = $conf->currency;
5700
		}
5701
		if (count($TCurrency) > 0) {
5702
			foreach ($langs->cache_currencies as $code_iso => $currency) {
5703
				if (isset($TCurrency[$code_iso])) {
5704
					if (!empty($selected) && $selected == $code_iso) {
5705
						$out .= '<option value="'.$code_iso.'" selected="selected">';
5706
					} else {
5707
						$out .= '<option value="'.$code_iso.'">';
5708
					}
5709
5710
					$out .= $currency['label'];
5711
					$out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
5712
					$out .= '</option>';
5713
				}
5714
			}
5715
		}
5716
5717
		$out .= '</select>';
5718
		// Make select dynamic
5719
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5720
		$out .= ajax_combobox($htmlname);
5721
5722
		return $out;
5723
	}
5724
5725
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5726
	/**
5727
	 *  Load into the cache vat rates of a country
5728
	 *
5729
	 *  @param	string	$country_code		Country code with quotes ("'CA'", or "'CA,IN,...'")
5730
	 *  @return	int							Nb of loaded lines, 0 if already loaded, <0 if KO
5731
	 */
5732
	public function load_cache_vatrates($country_code)
5733
	{
5734
		// phpcs:enable
5735
		global $langs;
5736
5737
		$num = count($this->cache_vatrates);
5738
		if ($num > 0) {
5739
			return $num; // Cache already loaded
5740
		}
5741
5742
		dol_syslog(__METHOD__, LOG_DEBUG);
5743
5744
		$sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
5745
		$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5746
		$sql .= " WHERE t.fk_pays = c.rowid";
5747
		$sql .= " AND t.active > 0";
5748
		$sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
5749
		$sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
5750
5751
		$resql = $this->db->query($sql);
5752
		if ($resql) {
5753
			$num = $this->db->num_rows($resql);
5754
			if ($num) {
5755
				for ($i = 0; $i < $num; $i++) {
5756
					$obj = $this->db->fetch_object($resql);
5757
					$this->cache_vatrates[$i]['rowid']	= $obj->rowid;
5758
					$this->cache_vatrates[$i]['code'] = $obj->code;
5759
					$this->cache_vatrates[$i]['txtva']	= $obj->taux;
5760
					$this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
5761
					$this->cache_vatrates[$i]['localtax1']	    = $obj->localtax1;
5762
					$this->cache_vatrates[$i]['localtax1_type']	= $obj->localtax1_type;
5763
					$this->cache_vatrates[$i]['localtax2']	    = $obj->localtax2;
5764
					$this->cache_vatrates[$i]['localtax2_type']	= $obj->localtax1_type;
5765
5766
					$this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
5767
					$this->cache_vatrates[$i]['labelallrates'] = $obj->taux.'/'.($obj->localtax1 ? $obj->localtax1 : '0').'/'.($obj->localtax2 ? $obj->localtax2 : '0').($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
5768
					$positiverates = '';
5769
					if ($obj->taux) {
5770
						$positiverates .= ($positiverates ? '/' : '').$obj->taux;
5771
					}
5772
					if ($obj->localtax1) {
5773
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
5774
					}
5775
					if ($obj->localtax2) {
5776
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
5777
					}
5778
					if (empty($positiverates)) {
5779
						$positiverates = '0';
5780
					}
5781
					$this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
5782
				}
5783
5784
				return $num;
5785
			} else {
5786
				$this->error = '<font class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</font>';
5787
				return -1;
5788
			}
5789
		} else {
5790
			$this->error = '<font class="error">'.$this->db->error().'</font>';
5791
			return -2;
5792
		}
5793
	}
5794
5795
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5796
	/**
5797
	 *  Output an HTML select vat rate.
5798
	 *  The name of this function should be selectVat. We keep bad name for compatibility purpose.
5799
	 *
5800
	 *  @param	string	      $htmlname           Name of HTML select field
5801
	 *  @param  float|string  $selectedrate       Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing.
5802
	 *  @param  Societe	      $societe_vendeuse   Thirdparty seller
5803
	 *  @param  Societe	      $societe_acheteuse  Thirdparty buyer
5804
	 *  @param  int		      $idprod             Id product. O if unknown of NA.
5805
	 *  @param  int		      $info_bits          Miscellaneous information on line (1 for NPR)
5806
	 *  @param  int|string    $type               ''=Unknown, 0=Product, 1=Service (Used if idprod not defined)
5807
	 *                  		                  Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5808
	 *                  					      Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5809
	 *                  					      Si (vendeur et acheteur dans Communaute europeenne) et bien vendu = moyen de transports neuf (auto, bateau, avion), TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle.
5810
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle.
5811
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle.
5812
	 *                  					      Sinon la TVA proposee par defaut=0. Fin de regle.
5813
	 *  @param	bool	     $options_only		  Return HTML options lines only (for ajax treatment)
5814
	 *  @param  int          $mode                0=Use vat rate as key in combo list, 1=Add VAT code after vat rate into key, -1=Use id of vat line as key
5815
	 *  @return	string
5816
	 */
5817
	public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
5818
	{
5819
		// phpcs:enable
5820
		global $langs, $conf, $mysoc;
5821
5822
		$langs->load('errors');
5823
5824
		$return = '';
5825
5826
		// Define defaultnpr, defaultttx and defaultcode
5827
		$defaultnpr = ($info_bits & 0x01);
5828
		$defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
5829
		$defaulttx = str_replace('*', '', $selectedrate);
5830
		$defaultcode = '';
5831
		$reg = array();
5832
		if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5833
			$defaultcode = $reg[1];
5834
			$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5835
		}
5836
		//var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
5837
5838
		// Check parameters
5839
		if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
5840
			if ($societe_vendeuse->id == $mysoc->id) {
5841
				$return .= '<font class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</font>';
5842
			} else {
5843
				$return .= '<font class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</font>';
5844
			}
5845
			return $return;
5846
		}
5847
5848
		//var_dump($societe_acheteuse);
5849
		//print "name=$name, selectedrate=$selectedrate, seller=".$societe_vendeuse->country_code." buyer=".$societe_acheteuse->country_code." buyer is company=".$societe_acheteuse->isACompany()." idprod=$idprod, info_bits=$info_bits type=$type";
5850
		//exit;
5851
5852
		// Define list of countries to use to search VAT rates to show
5853
		// First we defined code_country to use to find list
5854
		if (is_object($societe_vendeuse)) {
5855
			$code_country = "'".$societe_vendeuse->country_code."'";
5856
		} else {
5857
			$code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
5858
		}
5859
		if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {    // If option to have vat for end customer for services is on
5860
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
5861
			if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
5862
				// We also add the buyer
5863
				if (is_numeric($type)) {
5864
					if ($type == 1) { // We know product is a service
5865
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5866
					}
5867
				} elseif (!$idprod) {  // We don't know type of product
5868
					$code_country .= ",'".$societe_acheteuse->country_code."'";
5869
				} else {
5870
					$prodstatic = new Product($this->db);
5871
					$prodstatic->fetch($idprod);
5872
					if ($prodstatic->type == Product::TYPE_SERVICE) {   // We know product is a service
5873
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5874
					}
5875
				}
5876
			}
5877
		}
5878
5879
		// Now we get list
5880
		$num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
5881
5882
		if ($num > 0) {
5883
			// Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
5884
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
5885
				$tmpthirdparty = new Societe($this->db);
5886
				$defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5887
				$defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5888
				if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5889
					$defaultcode = $reg[1];
5890
					$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5891
				}
5892
				if (empty($defaulttx)) {
5893
					$defaultnpr = 0;
5894
				}
5895
			}
5896
5897
			// Si taux par defaut n'a pu etre determine, on prend dernier de la liste.
5898
			// Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant
5899
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
5900
				if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
5901
					$defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
5902
				} else {
5903
					$defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS);
5904
				}
5905
			}
5906
5907
			// Disabled if seller is not subject to VAT
5908
			$disabled = false;
5909
			$title = '';
5910
			if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
5911
				// Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
5912
				// of using supplier invoices (this is a very bad idea !)
5913
				if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
5914
					$title = ' title="'.$langs->trans('VATIsNotUsed').'"';
5915
					$disabled = true;
5916
				}
5917
			}
5918
5919
			if (!$options_only) {
5920
				$return .= '<select class="flat minwidth75imp" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
5921
			}
5922
5923
			$selectedfound = false;
5924
			foreach ($this->cache_vatrates as $rate) {
5925
				// Keep only 0 if seller is not subject to VAT
5926
				if ($disabled && $rate['txtva'] != 0) {
5927
					continue;
5928
				}
5929
5930
				// Define key to use into select list
5931
				$key = $rate['txtva'];
5932
				$key .= $rate['nprtva'] ? '*' : '';
5933
				if ($mode > 0 && $rate['code']) {
5934
					$key .= ' ('.$rate['code'].')';
5935
				}
5936
				if ($mode < 0) {
5937
					$key = $rate['rowid'];
5938
				}
5939
5940
				$return .= '<option value="'.$key.'"';
5941
				if (!$selectedfound) {
5942
					if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
5943
						if ($defaultcode == $rate['code']) {
5944
							$return .= ' selected';
5945
							$selectedfound = true;
5946
						}
5947
					} elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
5948
						$return .= ' selected';
5949
						$selectedfound = true;
5950
					}
5951
				}
5952
				$return .= '>';
5953
				//if (! empty($conf->global->MAIN_VAT_SHOW_POSITIVE_RATES))
5954
				if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
5955
					$return .= $rate['labelpositiverates'];
5956
				} else {
5957
					$return .= vatrate($rate['label']);
5958
				}
5959
				//$return.=($rate['code']?' '.$rate['code']:'');
5960
				$return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the *  (old behaviour only if new vat code is not used)
5961
5962
				$return .= '</option>';
5963
			}
5964
5965
			if (!$options_only) {
5966
				$return .= '</select>';
5967
			}
5968
		} else {
5969
			$return .= $this->error;
5970
		}
5971
5972
		$this->num = $num;
5973
		return $return;
5974
	}
5975
5976
5977
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5978
	/**
5979
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5980
	 *  Fields are preselected with :
5981
	 *            	- set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5982
	 *            	- local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5983
	 *            	- Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5984
	 *
5985
	 *	@param	integer	    $set_time 		Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
5986
	 *	@param	string		$prefix			Prefix for fields name
5987
	 *	@param	int			$h				1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty
5988
	 *	@param	int			$m				1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty
5989
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5990
	 *	@param	string		$form_name 		Not used
5991
	 *	@param	int			$d				1=Show days, month, years
5992
	 * 	@param	int			$addnowlink		Add a link "Now"
5993
	 * 	@param	int			$nooutput		Do not output html string but return it
5994
	 * 	@param 	int			$disabled		Disable input fields
5995
	 *  @param  int			$fullday        When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59
5996
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another select_date field.
5997
	 *  @param  datetime    $adddateof      Add a link "Date of invoice" using the following date.
5998
	 *  @return	string|void					Nothing or string if nooutput is 1
5999
	 *  @deprecated
6000
	 *  @see    selectDate(), form_date(), select_month(), select_year(), select_dayofweek()
6001
	 */
6002
	public function select_date($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $nooutput = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '')
6003
	{
6004
		// phpcs:enable
6005
		$retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6006
		if (!empty($nooutput)) {
6007
			return $retstring;
6008
		}
6009
		print $retstring;
6010
		return;
6011
	}
6012
6013
	/**
6014
	 *  Show 2 HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6015
	 *  Fields are preselected with :
6016
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6017
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6018
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6019
	 *
6020
	 *  @param  integer     $set_time       Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
6021
	 *  @param  integer     $set_time_end       Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
6022
	 *  @param	string		$prefix			Prefix for fields name
6023
	 *  @param	string		$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6024
	 * 	@return string                      Html for selectDate
6025
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6026
	 */
6027
	public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0)
6028
	{
6029
		global $langs;
6030
6031
		$ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6032
		$ret .= '<br>';
6033
		$ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6034
		return $ret;
6035
	}
6036
6037
	/**
6038
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6039
	 *  Fields are preselected with :
6040
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6041
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6042
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6043
	 *
6044
	 *  @param  integer     $set_time       Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2).
6045
	 *  @param	string		$prefix			Prefix for fields name
6046
	 *  @param	int			$h				1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty
6047
	 *	@param	int			$m				1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty
6048
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6049
	 *	@param	string		$form_name 		Not used
6050
	 *	@param	int			$d				1=Show days, month, years
6051
	 * 	@param	int			$addnowlink		Add a link "Now", 1 with server time, 2 with local computer time
6052
	 * 	@param 	int			$disabled		Disable input fields
6053
	 *  @param  int			$fullday        When a checkbox with id #fullday is checked, hours are set with 00:00 (if value if 'fulldaystart') or 23:59 (if value is 'fulldayend')
6054
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another selectDate field.
6055
	 *  @param  datetime    $adddateof      Add a link "Date of ..." using the following date. See also $labeladddateof for the label used.
6056
	 *  @param  string      $openinghours   Specify hour start and hour end for the select ex 8,20
6057
	 *  @param  int         $stepminutes    Specify step for minutes between 1 and 30
6058
	 *  @param	string		$labeladddateof Label to use for the $adddateof parameter.
6059
	 *  @param	string 		$placeholder    Placeholder
6060
	 *  @param	mixed		$gm				'auto' (for backward compatibility, avoid this), 'gmt' or 'tzserver' or 'tzuserrel'
6061
	 * 	@return string                      Html for selectDate
6062
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6063
	 */
6064
	public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '', $placeholder = '', $gm = 'auto')
6065
	{
6066
		global $conf, $langs;
6067
6068
		if ($gm === 'auto') {
6069
			$gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6070
		}
6071
6072
		$retstring = '';
6073
6074
		if ($prefix == '') {
6075
			$prefix = 're';
6076
		}
6077
		if ($h == '') {
6078
			$h = 0;
6079
		}
6080
		if ($m == '') {
6081
			$m = 0;
6082
		}
6083
		$emptydate = 0;
6084
		$emptyhours = 0;
6085
		if ($stepminutes <= 0 || $stepminutes > 30) {
6086
			$stepminutes = 1;
6087
		}
6088
		if ($empty == 1) {
6089
			$emptydate = 1;
6090
			$emptyhours = 1;
6091
		}
6092
		if ($empty == 2) {
6093
			$emptydate = 0;
6094
			$emptyhours = 1;
6095
		}
6096
		$orig_set_time = $set_time;
6097
6098
		if ($set_time === '' && $emptydate == 0) {
6099
			include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6100
			if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6101
				$set_time = dol_now($gm);
6102
			} else {
6103
				$set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6104
			}
6105
		}
6106
6107
		// Analysis of the pre-selection date
6108
		$reg = array();
6109
		if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) {	// deprecated usage
6110
			// Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6111
			$syear	= (!empty($reg[1]) ? $reg[1] : '');
6112
			$smonth = (!empty($reg[2]) ? $reg[2] : '');
6113
			$sday	= (!empty($reg[3]) ? $reg[3] : '');
6114
			$shour	= (!empty($reg[4]) ? $reg[4] : '');
6115
			$smin	= (!empty($reg[5]) ? $reg[5] : '');
6116
		} elseif (strval($set_time) != '' && $set_time != -1) {
6117
			// set_time est un timestamps (0 possible)
6118
			$syear = dol_print_date($set_time, "%Y", $gm);
6119
			$smonth = dol_print_date($set_time, "%m", $gm);
6120
			$sday = dol_print_date($set_time, "%d", $gm);
6121
			if ($orig_set_time != '') {
6122
				$shour = dol_print_date($set_time, "%H", $gm);
6123
				$smin = dol_print_date($set_time, "%M", $gm);
6124
				$ssec = dol_print_date($set_time, "%S", $gm);
6125
			} else {
6126
				$shour = '';
6127
				$smin = '';
6128
				$ssec = '';
6129
			}
6130
		} else {
6131
			// Date est '' ou vaut -1
6132
			$syear = '';
6133
			$smonth = '';
6134
			$sday = '';
6135
			$shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6136
			$smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6137
			$ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6138
		}
6139
		if ($h == 3) {
6140
			$shour = '';
6141
		}
6142
		if ($m == 3) {
6143
			$smin = '';
6144
		}
6145
6146
		$nowgmt = dol_now('gmt');
6147
		//var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6148
6149
		// You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6150
		$usecalendar = 'combo';
6151
		if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6152
			$usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6153
		}
6154
6155
		if ($d) {
6156
			// Show date with popup
6157
			if ($usecalendar != 'combo') {
6158
				$formated_date = '';
6159
				//print "e".$set_time." t ".$conf->format_date_short;
6160
				if (strval($set_time) != '' && $set_time != -1) {
6161
					//$formated_date=dol_print_date($set_time,$conf->format_date_short);
6162
					$formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6163
				}
6164
6165
				// Calendrier popup version eldy
6166
				if ($usecalendar == "eldy") {
6167
					// Input area to enter date manually
6168
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6169
					$retstring .= ($disabled ? ' disabled' : '');
6170
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6171
					$retstring .= '>';
6172
6173
					// Icon calendar
6174
					$retstringbuttom = '';
6175
					if (!$disabled) {
6176
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
6177
						$base = DOL_URL_ROOT.'/core/';
6178
						$retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
6179
						$retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
6180
					} else {
6181
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6182
					}
6183
					$retstring = $retstringbuttom.$retstring;
6184
6185
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6186
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6187
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6188
				} elseif ($usecalendar == 'jquery') {
6189
					if (!$disabled) {
6190
						// Output javascript for datepicker
6191
						$retstring .= "<script type='text/javascript'>";
6192
						$retstring .= "$(function(){ $('#".$prefix."').datepicker({
6193
							dateFormat: '".$langs->trans("FormatDateShortJQueryInput")."',
6194
							autoclose: true,
6195
							todayHighlight: true,";
6196
						if (!empty($conf->dol_use_jmobile)) {
6197
							$retstring .= "
6198
								beforeShow: function (input, datePicker) {
6199
									input.disabled = true;
6200
								},
6201
								onClose: function (dateText, datePicker) {
6202
									this.disabled = false;
6203
								},
6204
								";
6205
						}
6206
						// Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
6207
						if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS)) {
6208
							$retstring .= "
6209
								showOn: 'button',	/* both has problem with autocompletion */
6210
								buttonImage: '".DOL_URL_ROOT."/theme/".$conf->theme."/img/object_calendarday.png',
6211
								buttonImageOnly: true";
6212
						}
6213
						$retstring .= "
6214
							}) });";
6215
						$retstring .= "</script>";
6216
					}
6217
6218
					// Zone de saisie manuelle de la date
6219
					$retstring .= '<div class="nowrap inline-block divfordateinput">';
6220
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6221
					$retstring .= ($disabled ? ' disabled' : '');
6222
					$retstring .= ($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '');
6223
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6224
					$retstring .= '>';
6225
6226
					// Icone calendrier
6227
					if (!$disabled) {
6228
						/* Not required. Managed by option buttonImage of jquery
6229
						$retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
6230
						$retstring.="<script type='text/javascript'>";
6231
						$retstring.="jQuery(document).ready(function() {";
6232
						$retstring.='	jQuery("#'.$prefix.'id").click(function() {';
6233
						$retstring.="    	jQuery('#".$prefix."').focus();";
6234
						$retstring.='    });';
6235
						$retstring.='});';
6236
						$retstring.="</script>";*/
6237
					} else {
6238
						$retstringbutton = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6239
						$retsring = $retstringbutton.$retstring;
6240
					}
6241
6242
					$retstring .= '</div>';
6243
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6244
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6245
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6246
				} else {
6247
					$retstring .= "Bad value of MAIN_POPUP_CALENDAR";
6248
				}
6249
			} else {
6250
				// Show date with combo selects
6251
				// Day
6252
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
6253
6254
				if ($emptydate || $set_time == -1) {
6255
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6256
				}
6257
6258
				for ($day = 1; $day <= 31; $day++) {
6259
					$retstring .= '<option value="'.$day.'"'.($day == $sday ? ' selected' : '').'>'.$day.'</option>';
6260
				}
6261
6262
				$retstring .= "</select>";
6263
6264
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
6265
				if ($emptydate || $set_time == -1) {
6266
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6267
				}
6268
6269
				// Month
6270
				for ($month = 1; $month <= 12; $month++) {
6271
					$retstring .= '<option value="'.$month.'"'.($month == $smonth ? ' selected' : '').'>';
6272
					$retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
6273
					$retstring .= "</option>";
6274
				}
6275
				$retstring .= "</select>";
6276
6277
				// Year
6278
				if ($emptydate || $set_time == -1) {
6279
					$retstring .= '<input'.($disabled ? ' disabled' : '').' placeholder="'.dol_escape_htmltag($langs->trans("Year")).'" class="flat maxwidth50imp valignmiddle" type="number" min="0" max="3000" maxlength="4" id="'.$prefix.'year" name="'.$prefix.'year" value="'.$syear.'">';
6280
				} else {
6281
					$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
6282
6283
					for ($year = $syear - 10; $year < $syear + 10; $year++) {
6284
						$retstring .= '<option value="'.$year.'"'.($year == $syear ? ' selected' : '').'>'.$year.'</option>';
6285
					}
6286
					$retstring .= "</select>\n";
6287
				}
6288
			}
6289
		}
6290
6291
		if ($d && $h) {
6292
			$retstring .= ($h == 2 ? '<br>' : ' ');
6293
			$retstring .= '<span class="nowraponall">';
6294
		}
6295
6296
		if ($h) {
6297
			$hourstart = 0;
6298
			$hourend = 24;
6299
			if ($openinghours != '') {
6300
				$openinghours = explode(',', $openinghours);
6301
				$hourstart = $openinghours[0];
6302
				$hourend = $openinghours[1];
6303
				if ($hourend < $hourstart) {
6304
					$hourend = $hourstart;
6305
				}
6306
			}
6307
			// Show hour
6308
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'hour' : '').'" id="'.$prefix.'hour" name="'.$prefix.'hour">';
6309
			if ($emptyhours) {
6310
				$retstring .= '<option value="-1">&nbsp;</option>';
6311
			}
6312
			for ($hour = $hourstart; $hour < $hourend; $hour++) {
6313
				if (strlen($hour) < 2) {
6314
					$hour = "0".$hour;
6315
				}
6316
				$retstring .= '<option value="'.$hour.'"'.(($hour == $shour) ? ' selected' : '').'>'.$hour;
6317
				//$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
6318
				$retstring .= '</option>';
6319
			}
6320
			$retstring .= '</select>';
6321
			//if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
6322
			if ($m) {
6323
				$retstring .= ":";
6324
			}
6325
		}
6326
6327
		if ($m) {
6328
			// Show minutes
6329
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'min' : '').'" id="'.$prefix.'min" name="'.$prefix.'min">';
6330
			if ($emptyhours) {
6331
				$retstring .= '<option value="-1">&nbsp;</option>';
6332
			}
6333
			for ($min = 0; $min < 60; $min += $stepminutes) {
6334
				if (strlen($min) < 2) {
6335
					$min = "0".$min;
6336
				}
6337
				$retstring .= '<option value="'.$min.'"'.(($min == $smin) ? ' selected' : '').'>'.$min.(empty($conf->dol_optimize_smallscreen) ? '' : '').'</option>';
6338
			}
6339
			$retstring .= '</select>';
6340
6341
			$retstring .= '<input type="hidden" name="'.$prefix.'sec" value="'.$ssec.'">';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ssec does not seem to be defined for all execution paths leading up to this point.
Loading history...
6342
		}
6343
6344
		if ($d && $h) {
6345
			$retstring .= '</span>';
6346
		}
6347
6348
		// Add a "Now" link
6349
		if ($conf->use_javascript_ajax && $addnowlink) {
6350
			// Script which will be inserted in the onClick of the "Now" link
6351
			$reset_scripts = "";
6352
			if ($addnowlink == 2) { // local computer time
6353
				// pad add leading 0 on numbers
6354
				$reset_scripts .= "Number.prototype.pad = function(size) {
6355
                        var s = String(this);
6356
                        while (s.length < (size || 2)) {s = '0' + s;}
6357
                        return s;
6358
                    };
6359
                    var d = new Date();";
6360
			}
6361
6362
			// Generate the date part, depending on the use or not of the javascript calendar
6363
			if ($addnowlink == 1) { // server time expressed in user time setup
6364
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6365
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6366
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6367
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6368
			} elseif ($addnowlink == 2) {
6369
				/* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
6370
				 * This break application for foreign languages.
6371
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
6372
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
6373
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
6374
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
6375
				*/
6376
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6377
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6378
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6379
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6380
			}
6381
			/*if ($usecalendar == "eldy")
6382
			{
6383
				$base=DOL_URL_ROOT.'/core/';
6384
				$reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
6385
			}
6386
			else
6387
			{
6388
				$reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
6389
				$reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
6390
				$reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
6391
			}*/
6392
			// Update the hour part
6393
			if ($h) {
6394
				if ($fullday) {
6395
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6396
				}
6397
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
6398
				if ($addnowlink == 1) {
6399
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6400
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6401
				} elseif ($addnowlink == 2) {
6402
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());';
6403
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6404
				}
6405
6406
				if ($fullday) {
6407
					$reset_scripts .= ' } ';
6408
				}
6409
			}
6410
			// Update the minute part
6411
			if ($m) {
6412
				if ($fullday) {
6413
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6414
				}
6415
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
6416
				if ($addnowlink == 1) {
6417
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6418
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6419
				} elseif ($addnowlink == 2) {
6420
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());';
6421
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6422
				}
6423
				if ($fullday) {
6424
					$reset_scripts .= ' } ';
6425
				}
6426
			}
6427
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6428
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6429
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonNow" type="button" name="_useless" value="now" onClick="'.$reset_scripts.'">';
6430
				$retstring .= $langs->trans("Now");
6431
				$retstring .= '</button> ';
6432
			}
6433
		}
6434
6435
		// Add a "Plus one hour" link
6436
		if ($conf->use_javascript_ajax && $addplusone) {
6437
			// Script which will be inserted in the onClick of the "Add plusone" link
6438
			$reset_scripts = "";
6439
6440
			// Generate the date part, depending on the use or not of the javascript calendar
6441
			$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel').'\');';
6442
			$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6443
			$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6444
			$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6445
			// Update the hour part
6446
			if ($h) {
6447
				if ($fullday) {
6448
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6449
				}
6450
				$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6451
				if ($fullday) {
6452
					$reset_scripts .= ' } ';
6453
				}
6454
			}
6455
			// Update the minute part
6456
			if ($m) {
6457
				if ($fullday) {
6458
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6459
				}
6460
				$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6461
				if ($fullday) {
6462
					$reset_scripts .= ' } ';
6463
				}
6464
			}
6465
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6466
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6467
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="'.$reset_scripts.'">';
6468
				$retstring .= $langs->trans("DateStartPlusOne");
6469
				$retstring .= '</button> ';
6470
			}
6471
		}
6472
6473
		// Add a link to set data
6474
		if ($conf->use_javascript_ajax && $adddateof) {
6475
			$tmparray = dol_getdate($adddateof);
6476
			if (empty($labeladddateof)) {
6477
				$labeladddateof = $langs->trans("DateInvoice");
6478
			}
6479
			$retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="console.log(\'Click on now link\'); jQuery(\'#re\').val(\''.dol_print_date($adddateof, 'dayinputnoreduce').'\');jQuery(\'#reday\').val(\''.$tmparray['mday'].'\');jQuery(\'#remonth\').val(\''.$tmparray['mon'].'\');jQuery(\'#reyear\').val(\''.$tmparray['year'].'\');">'.$labeladddateof.'</a>';
6480
		}
6481
6482
		return $retstring;
6483
	}
6484
6485
	/**
6486
	 * selectTypeDuration
6487
	 *
6488
	 * @param   string   	$prefix     	Prefix
6489
	 * @param   string   	$selected   	Selected duration type
6490
	 * @param	array		$excludetypes	Array of duration types to exclude. Example array('y', 'm')
6491
	 * @return  string      	         	HTML select string
6492
	 */
6493
	public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
6494
	{
6495
		global $langs;
6496
6497
		$TDurationTypes = array(
6498
			'y'=>$langs->trans('Years'),
6499
			'm'=>$langs->trans('Month'),
6500
			'w'=>$langs->trans('Weeks'),
6501
			'd'=>$langs->trans('Days'),
6502
			'h'=>$langs->trans('Hours'),
6503
			'i'=>$langs->trans('Minutes')
6504
		);
6505
6506
		// Removed undesired duration types
6507
		foreach ($excludetypes as $value) {
6508
			unset($TDurationTypes[$value]);
6509
		}
6510
6511
		$retstring = '<select class="flat" id="select_'.$prefix.'type_duration" name="'.$prefix.'type_duration">';
6512
		foreach ($TDurationTypes as $key => $typeduration) {
6513
			$retstring .= '<option value="'.$key.'"';
6514
			if ($key == $selected) {
6515
				$retstring .= " selected";
6516
			}
6517
			$retstring .= ">".$typeduration."</option>";
6518
		}
6519
		$retstring .= "</select>";
6520
6521
		$retstring .= ajax_combobox('select_'.$prefix.'type_duration');
6522
6523
		return $retstring;
6524
	}
6525
6526
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6527
	/**
6528
	 *  Function to show a form to select a duration on a page
6529
	 *
6530
	 *	@param	string		$prefix   		Prefix for input fields
6531
	 *	@param  int			$iSecond  		Default preselected duration (number of seconds or '')
6532
	 * 	@param	int			$disabled       Disable the combo box
6533
	 * 	@param	string		$typehour		If 'select' then input hour and input min is a combo,
6534
	 *						            	If 'text' input hour is in text and input min is a text,
6535
	 *						            	If 'textselect' input hour is in text and input min is a combo
6536
	 *  @param	integer		$minunderhours	If 1, show minutes selection under the hours
6537
	 * 	@param	int			$nooutput		Do not output html string but return it
6538
	 *  @return	string|void
6539
	 */
6540
	public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
6541
	{
6542
		// phpcs:enable
6543
		global $langs;
6544
6545
		$retstring = '';
6546
6547
		$hourSelected = 0;
6548
		$minSelected = 0;
6549
6550
		// Hours
6551
		if ($iSecond != '') {
6552
			require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6553
6554
			$hourSelected = convertSecondToTime($iSecond, 'allhour');
6555
			$minSelected = convertSecondToTime($iSecond, 'min');
6556
		}
6557
6558
		if ($typehour == 'select') {
6559
			$retstring .= '<select class="flat" id="select_'.$prefix.'hour" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').'>';
6560
			for ($hour = 0; $hour < 25; $hour++) {	// For a duration, we allow 24 hours
6561
				$retstring .= '<option value="'.$hour.'"';
6562
				if ($hourSelected == $hour) {
6563
					$retstring .= " selected";
6564
				}
6565
				$retstring .= ">".$hour."</option>";
6566
			}
6567
			$retstring .= "</select>";
6568
		} elseif ($typehour == 'text' || $typehour == 'textselect') {
6569
			$retstring .= '<input placeholder="'.$langs->trans('HourShort').'" type="number" min="0" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputhour" value="'.(($hourSelected != '') ? ((int) $hourSelected) : '').'">';
6570
		} else {
6571
			return 'BadValueForParameterTypeHour';
6572
		}
6573
6574
		if ($typehour != 'text') {
6575
			$retstring .= ' '.$langs->trans('HourShort');
6576
		} else {
6577
			$retstring .= '<span class="hideonsmartphone">:</span>';
6578
		}
6579
6580
		// Minutes
6581
		if ($minunderhours) {
6582
			$retstring .= '<br>';
6583
		} else {
6584
			$retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
6585
		}
6586
6587
		if ($typehour == 'select' || $typehour == 'textselect') {
6588
			$retstring .= '<select class="flat" id="select_'.$prefix.'min" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').'>';
6589
			for ($min = 0; $min <= 55; $min = $min + 5) {
6590
				$retstring .= '<option value="'.$min.'"';
6591
				if ($minSelected == $min) {
6592
					$retstring .= ' selected';
6593
				}
6594
				$retstring .= '>'.$min.'</option>';
6595
			}
6596
			$retstring .= "</select>";
6597
		} elseif ($typehour == 'text') {
6598
			$retstring .= '<input placeholder="'.$langs->trans('MinuteShort').'" type="number" min="0" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputminute" value="'.(($minSelected != '') ? ((int) $minSelected) : '').'">';
6599
		}
6600
6601
		if ($typehour != 'text') {
6602
			$retstring .= ' '.$langs->trans('MinuteShort');
6603
		}
6604
6605
		//$retstring.="&nbsp;";
6606
6607
		if (!empty($nooutput)) {
6608
			return $retstring;
6609
		}
6610
6611
		print $retstring;
6612
		return;
6613
	}
6614
6615
	/**
6616
	 *  Return list of tickets in Ajax if Ajax activated or go to selectTicketsList
6617
	 *
6618
	 *  @param		int			$selected				Preselected tickets
6619
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
6620
	 *  @param  	string		$filtertype     		To add a filter
6621
	 *  @param		int			$limit					Limit on number of returned lines
6622
	 *  @param		int			$status					Ticket status
6623
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
6624
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
6625
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
6626
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
6627
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6628
	 * 	@param		int			$forcecombo				Force to use combo box
6629
	 *  @param      string      $morecss                Add more css on select
6630
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
6631
	 *  @param		string		$nooutput				No print, return the output into a string
6632
	 *  @return		void|string
6633
	 */
6634
	public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
6635
	{
6636
		global $langs, $conf;
6637
6638
		$out = '';
6639
6640
		// check parameters
6641
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
6642
6643
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6644
			$placeholder = '';
6645
6646
			if ($selected && empty($selected_input_value)) {
6647
				require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6648
				$tickettmpselect = new Ticket($this->db);
6649
				$tickettmpselect->fetch($selected);
6650
				$selected_input_value = $tickettmpselect->ref;
6651
				unset($tickettmpselect);
6652
			}
6653
6654
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $urloption seems to be never defined.
Loading history...
6655
6656
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
6657
			elseif ($hidelabel > 1) {
6658
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
6659
				if ($hidelabel == 2) {
6660
					$out .= img_picto($langs->trans("Search"), 'search');
6661
				}
6662
			}
6663
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
6664
			if ($hidelabel == 3) {
6665
				$out .= img_picto($langs->trans("Search"), 'search');
6666
			}
6667
		} else {
6668
			$out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
6669
		}
6670
6671
		if (empty($nooutput)) print $out;
6672
		else return $out;
6673
	}
6674
6675
6676
	/**
6677
	 *	Return list of tickets.
6678
	 *  Called by selectTickets.
6679
	 *
6680
	 *	@param      int		$selected           Preselected ticket
6681
	 *	@param      string	$htmlname           Name of select html
6682
	 *  @param		string	$filtertype         Filter on ticket type
6683
	 *	@param      int		$limit              Limit on number of returned lines
6684
	 * 	@param      string	$filterkey          Filter on ticket ref or subject
6685
	 *	@param		int		$status             Ticket status
6686
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
6687
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6688
	 * 	@param		int		$forcecombo		    Force to use combo box
6689
	 *  @param      string  $morecss            Add more css on select
6690
	 *  @return     array    				    Array of keys for json
6691
	 */
6692
	public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
6693
	{
6694
		global $langs, $conf, $user, $db;
6695
6696
		$out = '';
6697
		$outarray = array();
6698
6699
		$selectFields = " p.rowid, p.ref, p.message";
6700
6701
		$sql = "SELECT ";
6702
		$sql .= $selectFields;
6703
		$sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
6704
		$sql .= ' WHERE p.entity IN ('.getEntity('ticket').')';
6705
6706
		// Add criteria on ref/label
6707
		if ($filterkey != '') {
6708
			$sql .= ' AND (';
6709
			$prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
6710
			// For natural search
6711
			$scrit = explode(' ', $filterkey);
6712
			$i = 0;
6713
			if (count($scrit) > 1) $sql .= "(";
6714
			foreach ($scrit as $crit) {
6715
				if ($i > 0) $sql .= " AND ";
6716
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.subject LIKE '".$this->db->escape($prefix.$crit)."%'";
6717
				$sql .= ")";
6718
				$i++;
6719
			}
6720
			if (count($scrit) > 1) $sql .= ")";
6721
			$sql .= ')';
6722
		}
6723
6724
		$sql .= $this->db->plimit($limit, 0);
6725
6726
		// Build output string
6727
		dol_syslog(get_class($this)."::selectTicketsList search tickets", LOG_DEBUG);
6728
		$result = $this->db->query($sql);
6729
		if ($result) {
6730
			require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6731
			require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
6732
6733
			$num = $this->db->num_rows($result);
6734
6735
			$events = null;
6736
6737
			if (!$forcecombo) {
6738
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6739
				$out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT);
6740
			}
6741
6742
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
6743
6744
			$textifempty = '';
6745
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
6746
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
6747
			if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6748
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6749
				else $textifempty .= $langs->trans("All");
6750
			} else {
6751
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6752
			}
6753
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
6754
6755
			$i = 0;
6756
			while ($num && $i < $num) {
6757
				$opt = '';
6758
				$optJson = array();
6759
				$objp = $this->db->fetch_object($result);
6760
6761
				$this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
6762
				// Add new entry
6763
				// "key" value of json key array is used by jQuery automatically as selected value
6764
				// "label" value of json key array is used by jQuery automatically as text for combo box
6765
				$out .= $opt;
6766
				array_push($outarray, $optJson);
6767
6768
				$i++;
6769
			}
6770
6771
			$out .= '</select>';
6772
6773
			$this->db->free($result);
6774
6775
			if (empty($outputmode)) return $out;
6776
			return $outarray;
6777
		} else {
6778
			dol_print_error($db);
6779
		}
6780
	}
6781
6782
	/**
6783
	 * constructTicketListOption.
6784
	 * This define value for &$opt and &$optJson.
6785
	 *
6786
	 * @param 	resource	$objp			    Result set of fetch
6787
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
6788
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
6789
	 * @param 	string		$selected		    Preselected value
6790
	 * @param   string      $filterkey          Filter key to highlight
6791
	 * @return	void
6792
	 */
6793
	protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
6794
	{
6795
		global $langs, $conf, $user, $db;
6796
6797
		$outkey = '';
6798
		$outval = '';
6799
		$outref = '';
6800
		$outlabel = '';
6801
		$outtype = '';
6802
6803
		$label = $objp->label;
6804
6805
		$outkey = $objp->rowid;
6806
		$outref = $objp->ref;
6807
		$outlabel = $objp->label;
6808
		$outtype = $objp->fk_product_type;
6809
6810
		$opt = '<option value="'.$objp->rowid.'"';
6811
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
6812
		$opt .= '>';
6813
		$opt .= $objp->ref;
6814
		$objRef = $objp->ref;
6815
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
6816
		$outval .= $objRef;
6817
6818
		$opt .= "</option>\n";
6819
		$optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtypem);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $outtypem does not exist. Did you maybe mean $outtype?
Loading history...
6820
	}
6821
6822
6823
	/**
6824
	 * Generic method to select a component from a combo list.
6825
	 * Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.
6826
	 * This is the generic method that will replace all specific existing methods.
6827
	 *
6828
	 * @param 	string			$objectdesc			ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]
6829
	 * @param	string			$htmlname			Name of HTML select component
6830
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6831
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6832
	 * @param	string			$searchkey			Search criteria
6833
	 * @param	string			$placeholder		Place holder
6834
	 * @param	string			$morecss			More CSS
6835
	 * @param	string			$moreparams			More params provided to ajax call
6836
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6837
	 * @param	int				$disabled			1=Html component is disabled
6838
	 * @param	string	        $selected_input_value	Value of preselected input text (for use with ajax)
6839
	 * @return	string								Return HTML string
6840
	 * @see selectForFormsList() select_thirdparty_list()
6841
	 */
6842
	public function selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '')
6843
	{
6844
		global $conf, $user;
6845
6846
		$objecttmp = null;
6847
6848
		$InfoFieldList = explode(":", $objectdesc);
6849
		$classname = $InfoFieldList[0];
6850
		$classpath = $InfoFieldList[1];
6851
		$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
6852
		$filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
6853
		$sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
6854
6855
		if (!empty($classpath)) {
6856
			dol_include_once($classpath);
6857
6858
			if ($classname && class_exists($classname)) {
6859
				$objecttmp = new $classname($this->db);
6860
				// Make some replacement
6861
				$sharedentities = getEntity(strtolower($classname));
6862
				$objecttmp->filter = str_replace(
6863
					array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
6864
					array($conf->entity, $sharedentities, $user->id),
6865
					$filter
6866
				);
6867
			}
6868
		}
6869
		if (!is_object($objecttmp)) {
6870
			dol_syslog('Error bad setup of type for field '.$InfoFieldList, LOG_WARNING);
6871
			return 'Error bad setup of type for field '.join(',', $InfoFieldList);
6872
		}
6873
6874
		//var_dump($objecttmp->filter);
6875
		$prefixforautocompletemode = $objecttmp->element;
6876
		if ($prefixforautocompletemode == 'societe') {
6877
			$prefixforautocompletemode = 'company';
6878
		}
6879
		if ($prefixforautocompletemode == 'product') {
6880
			$prefixforautocompletemode = 'produit';
6881
		}
6882
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6883
6884
		dol_syslog(get_class($this)."::selectForForms object->filter=".$objecttmp->filter, LOG_DEBUG);
6885
		$out = '';
6886
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->$confkeyforautocompletemode) && !$forcecombo) {
6887
			// No immediate load of all database
6888
			$placeholder = '';
6889
			if ($preselectedvalue && empty($selected_input_value)) {
6890
				$objecttmp->fetch($preselectedvalue);
6891
				$selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
6892
				//unset($objecttmp);
6893
			}
6894
6895
			$objectdesc = $classname.':'.$classpath.':'.$addcreatebuttonornot.':'.$filter;
6896
			$urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php';
6897
6898
			// No immediate load of all database
6899
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&objectdesc='.urlencode($objectdesc).'&filter='.urlencode($objecttmp->filter).($sortfield ? '&sortfield='.urlencode($sortfield) : '');
6900
			// Activate the auto complete using ajax call.
6901
			$out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
6902
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
6903
			$out .= '<input type="text" class="'.$morecss.'"'.($disabled ? ' disabled="disabled"' : '').' name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '') .' />';
6904
		} else {
6905
			// Immediate load of table record. Note: filter is inside $objecttmp->filter
6906
			$out .= $this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield);
6907
		}
6908
6909
		return $out;
6910
	}
6911
6912
	/**
6913
	 * Function to forge a SQL criteria
6914
	 *
6915
	 * @param  array    $matches       Array of found string by regex search. Example: "t.ref:like:'SO-%'" or "t.date_creation:<:'20160101'" or "t.nature:is:NULL"
6916
	 * @return string                  Forged criteria. Example: "t.field like 'abc%'"
6917
	 */
6918
	protected static function forgeCriteriaCallback($matches)
6919
	{
6920
		global $db;
6921
6922
		//dol_syslog("Convert matches ".$matches[1]);
6923
		if (empty($matches[1])) {
6924
			return '';
6925
		}
6926
		$tmp = explode(':', $matches[1]);
6927
		if (count($tmp) < 3) {
6928
			return '';
6929
		}
6930
6931
		$tmpescaped = $tmp[2];
6932
		$regbis = array();
6933
		if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
6934
			$tmpescaped = "'".$db->escape($regbis[1])."'";
6935
		} else {
6936
			$tmpescaped = $db->escape($tmpescaped);
6937
		}
6938
		return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped;
6939
	}
6940
6941
	/**
6942
	 * Output html form to select an object.
6943
	 * Note, this function is called by selectForForms or by ajax selectobject.php
6944
	 *
6945
	 * @param 	Object			$objecttmp			Object to knwo the table to scan for combo.
6946
	 * @param	string			$htmlname			Name of HTML select component
6947
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6948
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6949
	 * @param	string			$searchkey			Search value
6950
	 * @param	string			$placeholder		Place holder
6951
	 * @param	string			$morecss			More CSS
6952
	 * @param	string			$moreparams			More params provided to ajax call
6953
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6954
	 * @param	int				$outputmode			0=HTML select string, 1=Array
6955
	 * @param	int				$disabled			1=Html component is disabled
6956
	 * @param	string			$sortfield			Sort field
6957
	 * @return	string|array						Return HTML string
6958
	 * @see selectForForms()
6959
	 */
6960
	public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '')
6961
	{
6962
		global $conf, $langs, $user, $hookmanager;
6963
6964
		//print "$objecttmp->filter, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled";
6965
6966
		$prefixforautocompletemode = $objecttmp->element;
6967
		if ($prefixforautocompletemode == 'societe') {
6968
			$prefixforautocompletemode = 'company';
6969
		}
6970
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6971
6972
		if (!empty($objecttmp->fields)) {	// For object that declare it, it is better to use declared fields (like societe, contact, ...)
6973
			$tmpfieldstoshow = '';
6974
			foreach ($objecttmp->fields as $key => $val) {
6975
				if (!dol_eval($val['enabled'], 1, 1)) {
6976
					continue;
6977
				}
6978
				if (!empty($val['showoncombobox'])) {
6979
					$tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '').'t.'.$key;
6980
				}
6981
			}
6982
			if ($tmpfieldstoshow) {
6983
				$fieldstoshow = $tmpfieldstoshow;
6984
			}
6985
		} else {
6986
			// For backward compatibility
6987
			$objecttmp->fields['ref'] = array('type'=>'varchar(30)', 'label'=>'Ref', 'showoncombobox'=>1);
6988
		}
6989
6990
		if (empty($fieldstoshow)) {
6991
			if (isset($objecttmp->fields['ref'])) {
6992
				$fieldstoshow = 't.ref';
6993
			} else {
6994
				$langs->load("errors");
6995
				$this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
6996
				return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
6997
			}
6998
		}
6999
7000
		$out = '';
7001
		$outarray = array();
7002
7003
		$num = 0;
7004
7005
		// Search data
7006
		$sql = "SELECT t.rowid, ".$fieldstoshow." FROM ".MAIN_DB_PREFIX.$objecttmp->table_element." as t";
7007
		if (isset($objecttmp->ismultientitymanaged)) {
7008
			if (!is_numeric($objecttmp->ismultientitymanaged)) {
7009
				$tmparray = explode('@', $objecttmp->ismultientitymanaged);
7010
				$sql .= " INNER JOIN ".MAIN_DB_PREFIX.$tmparray[1]." as parenttable ON parenttable.rowid = t.".$tmparray[0];
7011
			}
7012
			if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
7013
				if (!$user->rights->societe->client->voir && !$user->socid) {
7014
					$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
7015
				}
7016
			}
7017
		}
7018
7019
		// Add where from hooks
7020
		$parameters = array();
7021
		$reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
7022
		if (!empty($hookmanager->resPrint)) {
7023
			$sql .= $hookmanager->resPrint;
7024
		} else {
7025
			$sql .= " WHERE 1=1";
7026
			if (isset($objecttmp->ismultientitymanaged)) {
7027
				if ($objecttmp->ismultientitymanaged == 1) {
7028
					$sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
7029
				}
7030
				if (!is_numeric($objecttmp->ismultientitymanaged)) {
7031
					$sql .= " AND parenttable.entity = t.".$tmparray[0];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tmparray does not seem to be defined for all execution paths leading up to this point.
Loading history...
7032
				}
7033
				if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
7034
					if ($objecttmp->element == 'societe') {
7035
						$sql .= " AND t.rowid = ".((int) $user->socid);
7036
					} else {
7037
						$sql .= " AND t.fk_soc = ".((int) $user->socid);
7038
					}
7039
				}
7040
				if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
7041
					if (!$user->rights->societe->client->voir && !$user->socid) {
7042
						$sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
7043
					}
7044
				}
7045
			}
7046
			if ($searchkey != '') {
7047
				$sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
7048
			}
7049
			if ($objecttmp->filter) {	 // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
7050
				/*if (! DolibarrApi::_checkFilters($objecttmp->filter))
7051
				{
7052
					throw new RestException(503, 'Error when validating parameter sqlfilters '.$objecttmp->filter);
7053
				}*/
7054
				$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
7055
				$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'Form::forgeCriteriaCallback', $objecttmp->filter).")";
7056
			}
7057
		}
7058
		$sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, "ASC");
7059
		//$sql.=$this->db->plimit($limit, 0);
7060
		//print $sql;
7061
7062
		// Build output string
7063
		$resql = $this->db->query($sql);
7064
		if ($resql) {
7065
			// Construct $out and $outarray
7066
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').($moreparams ? ' '.$moreparams : '').' name="'.$htmlname.'">'."\n";
7067
7068
			// Warning: Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'. Seems it is no more true with selec2 v4
7069
			$textifempty = '&nbsp;';
7070
7071
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7072
			if (!empty($conf->global->$confkeyforautocompletemode)) {
7073
				if ($showempty && !is_numeric($showempty)) {
7074
					$textifempty = $langs->trans($showempty);
7075
				} else {
7076
					$textifempty .= $langs->trans("All");
7077
				}
7078
			}
7079
			if ($showempty) {
7080
				$out .= '<option value="-1">'.$textifempty.'</option>'."\n";
7081
			}
7082
7083
			$num = $this->db->num_rows($resql);
7084
			$i = 0;
7085
			if ($num) {
7086
				while ($i < $num) {
7087
					$obj = $this->db->fetch_object($resql);
7088
					$label = '';
7089
					$tmparray = explode(',', $fieldstoshow);
7090
					$oldvalueforshowoncombobox = 0;
7091
					foreach ($tmparray as $key => $val) {
7092
						$val = preg_replace('/t\./', '', $val);
7093
						$label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
7094
						$label .= $obj->$val;
7095
						$oldvalueforshowoncombobox = $objecttmp->fields[$val]['showoncombobox'];
7096
					}
7097
					if (empty($outputmode)) {
7098
						if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
7099
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
7100
						} else {
7101
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
7102
						}
7103
					} else {
7104
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
7105
					}
7106
7107
					$i++;
7108
					if (($i % 10) == 0) {
7109
						$out .= "\n";
7110
					}
7111
				}
7112
			}
7113
7114
			$out .= '</select>'."\n";
7115
7116
			if (!$forcecombo) {
7117
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7118
				$out .= ajax_combobox($htmlname, null, (!empty($conf->global->$confkeyforautocompletemode) ? $conf->global->$confkeyforautocompletemode : 0));
7119
			}
7120
		} else {
7121
			dol_print_error($this->db);
7122
		}
7123
7124
		$this->result = array('nbofelement'=>$num);
7125
7126
		if ($outputmode) {
7127
			return $outarray;
7128
		}
7129
		return $out;
7130
	}
7131
7132
7133
	/**
7134
	 *	Return a HTML select string, built from an array of key+value.
7135
	 *  Note: Do not apply langs->trans function on returned content, content may be entity encoded twice.
7136
	 *
7137
	 *	@param	string			$htmlname			Name of html select area. Must start with "multi" if this is a multiselect
7138
	 *	@param	array			$array				Array like array(key => value) or array(key=>array('label'=>..., 'data-...'=>..., 'disabled'=>..., 'css'=>...))
7139
	 *	@param	string|string[]	$id					Preselected key or preselected keys for multiselect
7140
	 *	@param	int|string		$show_empty			0 no empty value allowed, 1 or string to add an empty value into list (If 1: key is -1 and value is '' or '&nbsp;', If placeholder string: key is -1 and value is the string), <0 to add an empty value with key that is this value.
7141
	 *	@param	int				$key_in_label		1 to show key into label with format "[key] value"
7142
	 *	@param	int				$value_as_key		1 to use value as key
7143
	 *	@param  string			$moreparam			Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
7144
	 *	@param  int				$translate			1=Translate and encode value
7145
	 * 	@param	int				$maxlen				Length maximum for labels
7146
	 * 	@param	int				$disabled			Html select box is disabled
7147
	 *  @param	string			$sort				'ASC' or 'DESC' = Sort on label, '' or 'NONE' or 'POS' = Do not sort, we keep original order
7148
	 *  @param	string			$morecss			Add more class to css styles
7149
	 *  @param	int				$addjscombo			Add js combo
7150
	 *  @param  string          $moreparamonempty	Add more param on the empty option line. Not used if show_empty not set
7151
	 *  @param  int             $disablebademail	1=Check if a not valid email, 2=Check string '---', and if found into value, disable and colorize entry
7152
	 *  @param  int             $nohtmlescape		No html escaping.
7153
	 * 	@return	string								HTML select string.
7154
	 *  @see multiselectarray(), selectArrayAjax(), selectArrayFilter()
7155
	 */
7156
	public static function selectarray($htmlname, $array, $id = '', $show_empty = 0, $key_in_label = 0, $value_as_key = 0, $moreparam = '', $translate = 0, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '', $addjscombo = 1, $moreparamonempty = '', $disablebademail = 0, $nohtmlescape = 0)
7157
	{
7158
		global $conf, $langs;
7159
7160
		// Do we want a multiselect ?
7161
		//$jsbeautify = 0;
7162
		//if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
7163
		$jsbeautify = 1;
7164
7165
		if ($value_as_key) {
7166
			$array = array_combine($array, $array);
7167
		}
7168
7169
		$out = '';
7170
7171
		if ($addjscombo < 0) {
7172
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7173
				$addjscombo = 1;
7174
			} else {
7175
				$addjscombo = 0;
7176
			}
7177
		}
7178
7179
		// Add code for jquery to use multiselect
7180
		if ($addjscombo && $jsbeautify) {
7181
			// Enhance with select2
7182
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7183
			$out .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', $show_empty < 0 ? (string) $show_empty : '-1');
7184
		}
7185
7186
		$out .= '<select id="'.preg_replace('/^\./', '', $htmlname).'" '.($disabled ? 'disabled="disabled" ' : '').'class="flat '.(preg_replace('/^\./', '', $htmlname)).($morecss ? ' '.$morecss : '').'"';
7187
		$out .= ' name="'.preg_replace('/^\./', '', $htmlname).'" '.($moreparam ? $moreparam : '');
7188
		$out .= '>';
7189
7190
		if ($show_empty) {
7191
			$textforempty = ' ';
7192
			if (!empty($conf->use_javascript_ajax)) {
7193
				$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
7194
			}
7195
			if (!is_numeric($show_empty)) {
7196
				$textforempty = $show_empty;
7197
			}
7198
			$out .= '<option class="optiongrey" '.($moreparamonempty ? $moreparamonempty.' ' : '').'value="'.($show_empty < 0 ? $show_empty : -1).'"'.($id == $show_empty ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
7199
		}
7200
7201
		if (is_array($array)) {
7202
			// Translate
7203
			if ($translate) {
7204
				foreach ($array as $key => $value) {
7205
					if (!is_array($value)) {
7206
						$array[$key] = $langs->trans($value);
7207
					} else {
7208
						$array[$key]['label'] = $langs->trans($value['label']);
7209
					}
7210
				}
7211
			}
7212
7213
			// Sort
7214
			if ($sort == 'ASC') {
7215
				asort($array);
7216
			} elseif ($sort == 'DESC') {
7217
				arsort($array);
7218
			}
7219
7220
			foreach ($array as $key => $tmpvalue) {
7221
				if (is_array($tmpvalue)) {
7222
					$value = $tmpvalue['label'];
7223
					$disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
7224
					$style = empty($tmpvalue['css']) ? ' class="'.$tmpvalue['css'].'"' : '';
7225
				} else {
7226
					$value = $tmpvalue;
7227
					$disabled = '';
7228
					$style = '';
7229
				}
7230
				if (!empty($disablebademail)) {
7231
					if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
7232
						|| ($disablebademail == 2 && preg_match('/---/', $value))) {
7233
						$disabled = ' disabled';
7234
						$style = ' class="warning"';
7235
					}
7236
				}
7237
7238
				if ($key_in_label) {
7239
					if (empty($nohtmlescape)) {
7240
						$selectOptionValue = dol_escape_htmltag($key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value));
7241
					} else {
7242
						$selectOptionValue = $key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value);
7243
					}
7244
				} else {
7245
					if (empty($nohtmlescape)) {
7246
						$selectOptionValue = dol_escape_htmltag($maxlen ?dol_trunc($value, $maxlen) : $value);
7247
					} else {
7248
						$selectOptionValue = $maxlen ?dol_trunc($value, $maxlen) : $value;
7249
					}
7250
					if ($value == '' || $value == '-') {
7251
						$selectOptionValue = '&nbsp;';
7252
					}
7253
				}
7254
7255
				$out .= '<option value="'.$key.'"';
7256
				$out .= $style.$disabled;
7257
				if (is_array($id)) {
7258
					if (in_array($key, $id) && !$disabled) {
7259
						$out .= ' selected'; // To preselect a value
7260
					}
7261
				} else {
7262
					$id = (string) $id; // if $id = 0, then $id = '0'
7263
					if ($id != '' && $id == $key && !$disabled) {
7264
						$out .= ' selected'; // To preselect a value
7265
					}
7266
				}
7267
				if ($nohtmlescape) {
7268
					$out .= ' data-html="'.dol_escape_htmltag($selectOptionValue).'"';
7269
				}
7270
				if (is_array($tmpvalue)) {
7271
					foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
7272
						if (preg_match('/^data-/', $keyforvalue)) {
7273
							$out .= ' '.$keyforvalue.'="'.$valueforvalue.'"';
7274
						}
7275
					}
7276
				}
7277
				$out .= '>';
7278
				//var_dump($selectOptionValue);
7279
				$out .= $selectOptionValue;
7280
				$out .= "</option>\n";
7281
			}
7282
		}
7283
7284
		$out .= "</select>";
7285
		return $out;
7286
	}
7287
7288
7289
	/**
7290
	 *	Return a HTML select string, built from an array of key+value, but content returned into select come from an Ajax call of an URL.
7291
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7292
	 *
7293
	 *	@param	string	$htmlname       		Name of html select area
7294
	 *	@param	string	$url					Url. Must return a json_encode of array(key=>array('text'=>'A text', 'url'=>'An url'), ...)
7295
	 *	@param	string	$id             		Preselected key
7296
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7297
	 *	@param  string	$moreparamtourl 		Add more parameters onto the Ajax called URL
7298
	 * 	@param	int		$disabled				Html select box is disabled
7299
	 *  @param	int		$minimumInputLength		Minimum Input Length
7300
	 *  @param	string	$morecss				Add more class to css styles
7301
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7302
	 *  @param  string  $placeholder            String to use as placeholder
7303
	 *  @param  integer $acceptdelayedhtml      1 = caller is requesting to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
7304
	 * 	@return	string   						HTML select string
7305
	 *  @see selectArrayFilter(), ajax_combobox() in ajax.lib.php
7306
	 */
7307
	public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7308
	{
7309
		global $conf, $langs;
7310
		global $delayedhtmlcontent;	// Will be used later outside of this function
7311
7312
		// TODO Use an internal dolibarr component instead of select2
7313
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7314
			return '';
7315
		}
7316
7317
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"></select>';
7318
7319
		$outdelayed = '';
7320
		if (!empty($conf->use_javascript_ajax)) {
7321
			$tmpplugin = 'select2';
7322
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7323
		    	<script>
7324
		    	$(document).ready(function () {
7325
7326
	    	        '.($callurlonselect ? 'var saveRemoteData = [];' : '').'
7327
7328
	                $(".'.$htmlname.'").select2({
7329
				    	ajax: {
7330
					    	dir: "ltr",
7331
					    	url: "'.$url.'",
7332
					    	dataType: \'json\',
7333
					    	delay: 250,
7334
					    	data: function (params) {
7335
					    		return {
7336
							    	q: params.term, 	// search term
7337
					    			page: params.page
7338
					    		};
7339
				    		},
7340
				    		processResults: function (data) {
7341
				    			// parse the results into the format expected by Select2.
7342
				    			// since we are using custom formatting functions we do not need to alter the remote JSON data
7343
				    			//console.log(data);
7344
								saveRemoteData = data;
7345
					    	    /* format json result for select2 */
7346
					    	    result = []
7347
					    	    $.each( data, function( key, value ) {
7348
					    	       result.push({id: key, text: value.text});
7349
	                            });
7350
				    			//return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
7351
				    			//console.log(result);
7352
				    			return {results: result, more: false}
7353
				    		},
7354
				    		cache: true
7355
				    	},
7356
		 				language: select2arrayoflanguage,
7357
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7358
					    placeholder: "'.dol_escape_js($placeholder).'",
7359
				    	escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7360
				    	minimumInputLength: '.$minimumInputLength.',
7361
				        formatResult: function(result, container, query, escapeMarkup) {
7362
	                        return escapeMarkup(result.text);
7363
	                    },
7364
				    });
7365
7366
	                '.($callurlonselect ? '
7367
	                /* Code to execute a GET when we select a value */
7368
	                $(".'.$htmlname.'").change(function() {
7369
				    	var selected = $(".'.$htmlname.'").val();
7370
	                	console.log("We select in selectArrayAjax the entry "+selected)
7371
				        $(".'.$htmlname.'").val("");  /* reset visible combo value */
7372
	    			    $.each( saveRemoteData, function( key, value ) {
7373
	    				        if (key == selected)
7374
	    			            {
7375
	    			                 console.log("selectArrayAjax - Do a redirect to "+value.url)
7376
	    			                 location.assign(value.url);
7377
	    			            }
7378
	                    });
7379
	    			});' : '').'
7380
7381
	    	   });
7382
		       </script>';
7383
		}
7384
7385
		if ($acceptdelayedhtml) {
7386
			$delayedhtmlcontent .= $outdelayed;
7387
		} else {
7388
			$out .= $outdelayed;
7389
		}
7390
		return $out;
7391
	}
7392
7393
	/**
7394
	 *  Return a HTML select string, built from an array of key+value, but content returned into select is defined into $array parameter.
7395
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7396
	 *
7397
	 *  @param  string	$htmlname               Name of html select area
7398
	 *	@param	array	$array					Array (key=>array('text'=>'A text', 'url'=>'An url'), ...)
7399
	 *	@param	string	$id             		Preselected key
7400
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7401
	 *	@param	int		$disableFiltering		If set to 1, results are not filtered with searched string
7402
	 * 	@param	int		$disabled				Html select box is disabled
7403
	 *  @param	int		$minimumInputLength		Minimum Input Length
7404
	 *  @param	string	$morecss				Add more class to css styles
7405
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7406
	 *  @param  string  $placeholder            String to use as placeholder
7407
	 *  @param  integer $acceptdelayedhtml      1 = caller is requesting to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
7408
	 *  @return	string   						HTML select string
7409
	 *  @see selectArrayAjax(), ajax_combobox() in ajax.lib.php
7410
	 */
7411
	public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7412
	{
7413
		global $conf, $langs;
7414
		global $delayedhtmlcontent;	// Will be used later outside of this function
7415
7416
		// TODO Use an internal dolibarr component instead of select2
7417
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7418
			return '';
7419
		}
7420
7421
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"><option></option></select>';
7422
7423
		$formattedarrayresult = array();
7424
7425
		foreach ($array as $key => $value) {
7426
			$o = new stdClass();
7427
			$o->id = $key;
7428
			$o->text = $value['text'];
7429
			$o->url = $value['url'];
7430
			$formattedarrayresult[] = $o;
7431
		}
7432
7433
		$outdelayed = '';
7434
		if (!empty($conf->use_javascript_ajax)) {
7435
			$tmpplugin = 'select2';
7436
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7437
				<script>
7438
				$(document).ready(function () {
7439
					var data = '.json_encode($formattedarrayresult).';
7440
7441
					'.($callurlonselect ? 'var saveRemoteData = '.json_encode($array).';' : '').'
7442
7443
					$(".'.$htmlname.'").select2({
7444
						data: data,
7445
						language: select2arrayoflanguage,
7446
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7447
						placeholder: "'.dol_escape_js($placeholder).'",
7448
						escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7449
						minimumInputLength: '.$minimumInputLength.',
7450
						formatResult: function(result, container, query, escapeMarkup) {
7451
							return escapeMarkup(result.text);
7452
						},
7453
						matcher: function (params, data) {
7454
7455
							if(! data.id) return null;';
7456
7457
			if ($callurlonselect) {
7458
				$outdelayed .= '
7459
7460
							var urlBase = data.url;
7461
							var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
7462
							/* console.log("params.term="+params.term); */
7463
							/* console.log("params.term encoded="+encodeURIComponent(params.term)); */
7464
							saveRemoteData[data.id].url = urlBase + separ + "sall=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
7465
			}
7466
7467
			if (!$disableFiltering) {
7468
				$outdelayed .= '
7469
7470
							if(data.text.match(new RegExp(params.term))) {
7471
								return data;
7472
							}
7473
7474
							return null;';
7475
			} else {
7476
				$outdelayed .= '
7477
7478
							return data;';
7479
			}
7480
7481
			$outdelayed .= '
7482
						}
7483
					});
7484
7485
					'.($callurlonselect ? '
7486
					/* Code to execute a GET when we select a value */
7487
					$(".'.$htmlname.'").change(function() {
7488
						var selected = $(".'.$htmlname.'").val();
7489
						console.log("We select "+selected)
7490
7491
						$(".'.$htmlname.'").val("");  /* reset visible combo value */
7492
						$.each( saveRemoteData, function( key, value ) {
7493
							if (key == selected)
7494
							{
7495
								console.log("selectArrayFilter - Do a redirect to "+value.url)
7496
								location.assign(value.url);
7497
							}
7498
						});
7499
					});' : '').'
7500
7501
				});
7502
				</script>';
7503
		}
7504
7505
		if ($acceptdelayedhtml) {
7506
			$delayedhtmlcontent .= $outdelayed;
7507
		} else {
7508
			$out .= $outdelayed;
7509
		}
7510
		return $out;
7511
	}
7512
7513
	/**
7514
	 *	Show a multiselect form from an array. WARNING: Use this only for short lists.
7515
	 *
7516
	 *	@param	string	$htmlname		Name of select
7517
	 *	@param	array	$array			Array with key+value
7518
	 *	@param	array	$selected		Array with key+value preselected
7519
	 *	@param	int		$key_in_label   1 to show key like in "[key] value"
7520
	 *	@param	int		$value_as_key   1 to use value as key
7521
	 *	@param  string	$morecss        Add more css style
7522
	 *	@param  int		$translate		Translate and encode value
7523
	 *  @param	int		$width			Force width of select box. May be used only when using jquery couch. Example: 250, 95%
7524
	 *  @param	string	$moreattrib		Add more options on select component. Example: 'disabled'
7525
	 *  @param	string	$elemtype		Type of element we show ('category', ...). Will execute a formating function on it. To use in readonly mode if js component support HTML formatting.
7526
	 *  @param	string	$placeholder	String to use as placeholder
7527
	 *  @param	int		$addjscombo		Add js combo
7528
	 *	@return	string					HTML multiselect string
7529
	 *  @see selectarray(), selectArrayAjax(), selectArrayFilter()
7530
	 */
7531
	public static function multiselectarray($htmlname, $array, $selected = array(), $key_in_label = 0, $value_as_key = 0, $morecss = '', $translate = 0, $width = 0, $moreattrib = '', $elemtype = '', $placeholder = '', $addjscombo = -1)
7532
	{
7533
		global $conf, $langs;
7534
7535
		$out = '';
7536
7537
		if ($addjscombo < 0) {
7538
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7539
				$addjscombo = 1;
7540
			} else {
7541
				$addjscombo = 0;
7542
			}
7543
		}
7544
7545
		// Add code for jquery to use multiselect
7546
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
7547
			$out .= "\n".'<!-- JS CODE TO ENABLE select for id '.$htmlname.', addjscombo='.$addjscombo.' -->';
7548
			$out .= "\n".'<script>'."\n";
7549
			if ($addjscombo == 1) {
7550
				$tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
7551
				$out .= 'function formatResult(record) {'."\n";
7552
				if ($elemtype == 'category') {
7553
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
7554
				} else {
7555
					$out .= 'return record.text;';
7556
				}
7557
				$out .= '};'."\n";
7558
				$out .= 'function formatSelection(record) {'."\n";
7559
				if ($elemtype == 'category') {
7560
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
7561
				} else {
7562
					$out .= 'return record.text;';
7563
				}
7564
				$out .= '};'."\n";
7565
				$out .= '$(document).ready(function () {
7566
							$(\'#'.$htmlname.'\').'.$tmpplugin.'({
7567
								dir: \'ltr\',
7568
								// Specify format function for dropdown item
7569
								formatResult: formatResult,
7570
							 	templateResult: formatResult,		/* For 4.0 */
7571
								// Specify format function for selected item
7572
								formatSelection: formatSelection,
7573
							 	templateSelection: formatSelection		/* For 4.0 */
7574
							});
7575
7576
							/* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
7577
								 the size only if component is not hidden by default on load */
7578
							$(\'#'.$htmlname.' + .select2\').addClass(\''.$morecss.'\');
7579
						});'."\n";
7580
			} elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
7581
				// Add other js lib
7582
				// TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
7583
				// ...
7584
				$out .= 'console.log(\'addjscombo=2 for htmlname='.$htmlname.'\');';
7585
				$out .= '$(document).ready(function () {
7586
							$(\'#'.$htmlname.'\').multiSelect({
7587
								containerHTML: \'<div class="multi-select-container">\',
7588
								menuHTML: \'<div class="multi-select-menu">\',
7589
								buttonHTML: \'<span class="multi-select-button '.$morecss.'">\',
7590
								menuItemHTML: \'<label class="multi-select-menuitem">\',
7591
								activeClass: \'multi-select-container--open\',
7592
								noneText: \''.$placeholder.'\'
7593
							});
7594
						})';
7595
			}
7596
			$out .= '</script>';
7597
		}
7598
7599
		// Try also magic suggest
7600
		$out .= '<select id="'.$htmlname.'" class="multiselect'.($morecss ? ' '.$morecss : '').'" multiple name="'.$htmlname.'[]"'.($moreattrib ? ' '.$moreattrib : '').($width ? ' style="width: '.(preg_match('/%/', $width) ? $width : $width.'px').'"' : '').'>'."\n";
7601
		if (is_array($array) && !empty($array)) {
7602
			if ($value_as_key) {
7603
				$array = array_combine($array, $array);
7604
			}
7605
7606
			if (!empty($array)) {
7607
				foreach ($array as $key => $value) {
7608
					$newval = ($translate ? $langs->trans($value) : $value);
7609
					$newval = ($key_in_label ? $key.' - '.$newval : $newval);
7610
7611
					$out .= '<option value="'.$key.'"';
7612
					if (is_array($selected) && !empty($selected) && in_array((string) $key, $selected) && ((string) $key != '')) {
7613
						$out .= ' selected';
7614
					}
7615
					$out .= ' data-html="'.dol_escape_htmltag($newval).'"';
7616
					$out .= '>';
7617
					$out .= dol_htmlentitiesbr($newval);
7618
					$out .= '</option>'."\n";
7619
				}
7620
			}
7621
		}
7622
		$out .= '</select>'."\n";
7623
7624
		return $out;
7625
	}
7626
7627
7628
	/**
7629
	 *	Show a multiselect dropbox from an array. If a saved selection of fields exists for user (into $user->conf->MAIN_SELECTEDFIELDS_contextofpage), we use this one instead of default.
7630
	 *
7631
	 *	@param	string	$htmlname		Name of HTML field
7632
	 *	@param	array	$array			Array with array of fields we could show. This array may be modified according to setup of user.
7633
	 *  @param  string  $varpage        Id of context for page. Can be set by caller with $varpage=(empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage);
7634
	 *	@return	string					HTML multiselect string
7635
	 *  @see selectarray()
7636
	 */
7637
	public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage)
7638
	{
7639
		global $conf, $langs, $user, $extrafields;
7640
7641
		if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7642
			return '';
7643
		}
7644
7645
		$tmpvar = "MAIN_SELECTEDFIELDS_".$varpage; // To get list of saved selected fields to show
7646
7647
		if (!empty($user->conf->$tmpvar)) {		// A list of fields was already customized for user
7648
			$tmparray = explode(',', $user->conf->$tmpvar);
7649
			foreach ($array as $key => $val) {
7650
				//var_dump($key);
7651
				//var_dump($tmparray);
7652
				if (in_array($key, $tmparray)) {
7653
					$array[$key]['checked'] = 1;
7654
				} else {
7655
					$array[$key]['checked'] = 0;
7656
				}
7657
			}
7658
		} else {								// There is no list of fields already customized for user
7659
			foreach ($array as $key => $val) {
7660
				if ($array[$key]['checked'] < 0) {
7661
					$array[$key]['checked'] = 0;
7662
				}
7663
			}
7664
		}
7665
7666
		$listoffieldsforselection = '';
7667
		$listcheckedstring = '';
7668
7669
		foreach ($array as $key => $val) {
7670
			/* var_dump($val);
7671
			var_dump(array_key_exists('enabled', $val));
7672
			var_dump(!$val['enabled']);*/
7673
			if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
7674
				unset($array[$key]); // We don't want this field
7675
				continue;
7676
			}
7677
			if (!empty($val['type']) && $val['type'] == 'separate') {
7678
				// Field remains in array but we don't add it into $listoffieldsforselection
7679
				//$listoffieldsforselection .= '<li>-----</li>';
7680
				continue;
7681
			}
7682
			if ($val['label']) {
7683
				if (!empty($val['langfile']) && is_object($langs)) {
7684
					$langs->load($val['langfile']);
7685
				}
7686
7687
				// Note: $val['checked'] <> 0 means we must show the field into the combo list
7688
				$listoffieldsforselection .= '<li><input type="checkbox" id="checkbox'.$key.'" value="'.$key.'"'.((empty($val['checked']) && $val['checked'] != '-1') ? '' : ' checked="checked"').'/><label for="checkbox'.$key.'">'.dol_escape_htmltag($langs->trans($val['label'])).'</label></li>';
7689
				$listcheckedstring .= (empty($val['checked']) ? '' : $key.',');
7690
			}
7691
		}
7692
7693
		$out = '<!-- Component multiSelectArrayWithCheckbox '.$htmlname.' -->
7694
7695
        <dl class="dropdown">
7696
            <dt>
7697
            <a href="#'.$htmlname.'">
7698
              '.img_picto('', 'list').'
7699
            </a>
7700
            <input type="hidden" class="'.$htmlname.'" name="'.$htmlname.'" value="'.$listcheckedstring.'">
7701
            </dt>
7702
            <dd class="dropdowndd">
7703
                <div class="multiselectcheckbox'.$htmlname.'">
7704
                    <ul class="ul'.$htmlname.'">
7705
                    '.$listoffieldsforselection.'
7706
                    </ul>
7707
                </div>
7708
            </dd>
7709
        </dl>
7710
7711
        <script type="text/javascript">
7712
          jQuery(document).ready(function () {
7713
              $(\'.multiselectcheckbox'.$htmlname.' input[type="checkbox"]\').on(\'click\', function () {
7714
                  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
7715
7716
                  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\');	// Update field so we know we changed something on selected fields after POST
7717
7718
                  var title = $(this).val() + ",";
7719
                  if ($(this).is(\':checked\')) {
7720
                      $(\'.'.$htmlname.'\').val(title + $(\'.'.$htmlname.'\').val());
7721
                  }
7722
                  else {
7723
                      $(\'.'.$htmlname.'\').val( $(\'.'.$htmlname.'\').val().replace(title, \'\') )
7724
                  }
7725
                  // Now, we submit page
7726
                  //$(this).parents(\'form:first\').submit();
7727
              });
7728
7729
7730
           });
7731
        </script>
7732
7733
        ';
7734
		return $out;
7735
	}
7736
7737
	/**
7738
	 * 	Render list of categories linked to object with id $id and type $type
7739
	 *
7740
	 * 	@param		int		$id				Id of object
7741
	 * 	@param		string	$type			Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
7742
	 *  @param		int		$rendermode		0=Default, use multiselect. 1=Emulate multiselect (recommended)
7743
	 *  @param		int		$nolink			1=Do not add html links
7744
	 * 	@return		string					String with categories
7745
	 */
7746
	public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
7747
	{
7748
		global $db;
7749
7750
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7751
7752
		$cat = new Categorie($db);
7753
		$categories = $cat->containing($id, $type);
7754
7755
		if ($rendermode == 1) {
7756
			$toprint = array();
7757
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7758
				$ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text
7759
				foreach ($ways as $way) {
7760
					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.$way.'</li>';
7761
				}
7762
			}
7763
			return '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7764
		}
7765
7766
		if ($rendermode == 0) {
7767
			$arrayselected = array();
7768
			$cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 1);
7769
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7770
				$arrayselected[] = $c->id;
7771
			}
7772
7773
			return $this->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, '', 0, '100%', 'disabled', 'category');
7774
		}
7775
7776
		return 'ErrorBadValueForParameterRenderMode'; // Should not happened
7777
	}
7778
7779
	/**
7780
	 *  Show linked object block.
7781
	 *
7782
	 *  @param	CommonObject	$object		      Object we want to show links to
7783
	 *  @param  string          $morehtmlright    More html to show on right of title
7784
	 *  @param  array           $compatibleImportElementsList  Array of compatibles elements object for "import from" action
7785
	 *  @return	int							      <0 if KO, >=0 if OK
7786
	 */
7787
	public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = false)
7788
	{
7789
		global $conf, $langs, $hookmanager;
7790
		global $bc, $action;
7791
7792
		$object->fetchObjectLinked();
7793
7794
		// Bypass the default method
7795
		$hookmanager->initHooks(array('commonobject'));
7796
		$parameters = array(
7797
			'morehtmlright' => $morehtmlright,
7798
			'compatibleImportElementsList' => &$compatibleImportElementsList,
7799
		);
7800
		$reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
7801
7802
		if (empty($reshook)) {
7803
			$nbofdifferenttypes = count($object->linkedObjects);
7804
7805
			print '<!-- showLinkedObjectBlock -->';
7806
			print load_fiche_titre($langs->trans('RelatedObjects'), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
7807
7808
7809
			print '<div class="div-table-responsive-no-min">';
7810
			print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="'.$object->element.'"  data-elementid="'.$object->id.'"   >';
7811
7812
			print '<tr class="liste_titre">';
7813
			print '<td>'.$langs->trans("Type").'</td>';
7814
			print '<td>'.$langs->trans("Ref").'</td>';
7815
			print '<td class="center"></td>';
7816
			print '<td class="center">'.$langs->trans("Date").'</td>';
7817
			print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
7818
			print '<td class="right">'.$langs->trans("Status").'</td>';
7819
			print '<td></td>';
7820
			print '</tr>';
7821
7822
			$nboftypesoutput = 0;
7823
7824
			foreach ($object->linkedObjects as $objecttype => $objects) {
7825
				$tplpath = $element = $subelement = $objecttype;
7826
7827
				// to display inport button on tpl
7828
				$showImportButton = false;
7829
				if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
7830
					$showImportButton = true;
7831
				}
7832
7833
				$regs = array();
7834
				if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
7835
					$element = $regs[1];
7836
					$subelement = $regs[2];
7837
					$tplpath = $element.'/'.$subelement;
7838
				}
7839
				$tplname = 'linkedobjectblock';
7840
7841
				// To work with non standard path
7842
				if ($objecttype == 'facture') {
7843
					$tplpath = 'compta/'.$element;
7844
					if (empty($conf->facture->enabled)) {
7845
						continue; // Do not show if module disabled
7846
					}
7847
				} elseif ($objecttype == 'facturerec') {
7848
					$tplpath = 'compta/facture';
7849
					$tplname = 'linkedobjectblockForRec';
7850
					if (empty($conf->facture->enabled)) {
7851
						continue; // Do not show if module disabled
7852
					}
7853
				} elseif ($objecttype == 'propal') {
7854
					$tplpath = 'comm/'.$element;
7855
					if (empty($conf->propal->enabled)) {
7856
						continue; // Do not show if module disabled
7857
					}
7858
				} elseif ($objecttype == 'supplier_proposal') {
7859
					if (empty($conf->supplier_proposal->enabled)) {
7860
						continue; // Do not show if module disabled
7861
					}
7862
				} elseif ($objecttype == 'shipping' || $objecttype == 'shipment') {
7863
					$tplpath = 'expedition';
7864
					if (empty($conf->expedition->enabled)) {
7865
						continue; // Do not show if module disabled
7866
					}
7867
				} elseif ($objecttype == 'reception') {
7868
					$tplpath = 'reception';
7869
					if (empty($conf->reception->enabled)) {
7870
						continue; // Do not show if module disabled
7871
					}
7872
				} elseif ($objecttype == 'delivery') {
7873
					$tplpath = 'delivery';
7874
					if (empty($conf->expedition->enabled)) {
7875
						continue; // Do not show if module disabled
7876
					}
7877
				} elseif ($objecttype == 'ficheinter') {
7878
					$tplpath = 'fichinter';
7879
					if (empty($conf->ficheinter->enabled)) {
7880
						continue; // Do not show if module disabled
7881
					}
7882
				} elseif ($objecttype == 'invoice_supplier') {
7883
					$tplpath = 'fourn/facture';
7884
				} elseif ($objecttype == 'order_supplier') {
7885
					$tplpath = 'fourn/commande';
7886
				} elseif ($objecttype == 'expensereport') {
7887
					$tplpath = 'expensereport';
7888
				} elseif ($objecttype == 'subscription') {
7889
					$tplpath = 'adherents';
7890
				} elseif ($objecttype == 'conferenceorbooth') {
7891
					$tplpath = 'eventorganization';
7892
				} elseif ($objecttype == 'conferenceorboothattendee') {
7893
					$tplpath = 'eventorganization';
7894
				} elseif ($objecttype == 'mo') {
7895
					$tplpath = 'mrp';
7896
					if (empty($conf->mrp->enabled)) {
7897
						continue; // Do not show if module disabled
7898
					}
7899
				}
7900
7901
				global $linkedObjectBlock;
7902
				$linkedObjectBlock = $objects;
7903
7904
				// Output template part (modules that overwrite templates must declare this into descriptor)
7905
				$dirtpls = array_merge($conf->modules_parts['tpl'], array('/'.$tplpath.'/tpl'));
7906
				foreach ($dirtpls as $reldir) {
7907
					if ($nboftypesoutput == ($nbofdifferenttypes - 1)) {    // No more type to show after
7908
						global $noMoreLinkedObjectBlockAfter;
7909
						$noMoreLinkedObjectBlockAfter = 1;
7910
					}
7911
7912
					$res = @include dol_buildpath($reldir.'/'.$tplname.'.tpl.php');
7913
					if ($res) {
7914
						$nboftypesoutput++;
7915
						break;
7916
					}
7917
				}
7918
			}
7919
7920
			if (!$nboftypesoutput) {
7921
				print '<tr><td class="impair opacitymedium" colspan="7">'.$langs->trans("None").'</td></tr>';
7922
			}
7923
7924
			print '</table>';
7925
7926
			if (!empty($compatibleImportElementsList)) {
7927
				$res = @include dol_buildpath('core/tpl/ajax/objectlinked_lineimport.tpl.php');
7928
			}
7929
7930
7931
			print '</div>';
7932
7933
			return $nbofdifferenttypes;
7934
		}
7935
	}
7936
7937
	/**
7938
	 *  Show block with links to link to other objects.
7939
	 *
7940
	 *  @param	CommonObject	$object				Object we want to show links to
7941
	 *  @param	array			$restrictlinksto	Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
7942
	 *  @param	array			$excludelinksto		Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
7943
	 *  @return	string								<0 if KO, >0 if OK
7944
	 */
7945
	public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
7946
	{
7947
		global $conf, $langs, $hookmanager;
7948
		global $bc, $action;
7949
7950
		$linktoelem = '';
7951
		$linktoelemlist = '';
7952
		$listofidcompanytoscan = '';
7953
7954
		if (!is_object($object->thirdparty)) {
7955
			$object->fetch_thirdparty();
7956
		}
7957
7958
		$possiblelinks = array();
7959
		if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
7960
			$listofidcompanytoscan = $object->thirdparty->id;
7961
			if (($object->thirdparty->parent > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PARENT_IN_LINKTO)) {
7962
				$listofidcompanytoscan .= ','.$object->thirdparty->parent;
7963
			}
7964
			if (($object->fk_project > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO)) {
7965
				include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
7966
				$tmpproject = new Project($this->db);
7967
				$tmpproject->fetch($object->fk_project);
7968
				if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) {
7969
					$listofidcompanytoscan .= ','.$tmpproject->socid;
7970
				}
7971
				unset($tmpproject);
7972
			}
7973
7974
			$possiblelinks = array(
7975
				'propal'=>array('enabled'=>$conf->propal->enabled, 'perms'=>1, 'label'=>'LinkToProposal', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('propal').')'),
7976
				'order'=>array('enabled'=>$conf->commande->enabled, 'perms'=>1, 'label'=>'LinkToOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('commande').')'),
7977
				'invoice'=>array('enabled'=>$conf->facture->enabled, 'perms'=>1, 'label'=>'LinkToInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('invoice').')'),
7978
				'invoice_template'=>array('enabled'=>$conf->facture->enabled, 'perms'=>1, 'label'=>'LinkToTemplateInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.titre as ref, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture_rec as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('invoice').')'),
7979
				'contrat'=>array(
7980
					'enabled'=>$conf->contrat->enabled,
7981
					'perms'=>1,
7982
					'label'=>'LinkToContract',
7983
					'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_customer as ref_client, t.ref_supplier, SUM(td.total_ht) as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."contrat as t, ".MAIN_DB_PREFIX."contratdet as td WHERE t.fk_soc = s.rowid AND td.fk_contrat = t.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('contract').') GROUP BY s.rowid, s.nom, s.client, t.rowid, t.ref, t.ref_customer, t.ref_supplier'
7984
				),
7985
				'fichinter'=>array('enabled'=>$conf->ficheinter->enabled, 'perms'=>1, 'label'=>'LinkToIntervention', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."fichinter as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('intervention').')'),
7986
				'supplier_proposal'=>array('enabled'=>$conf->supplier_proposal->enabled, 'perms'=>1, 'label'=>'LinkToSupplierProposal', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, '' as ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('supplier_proposal').')'),
7987
				'order_supplier'=>array('enabled'=>$conf->supplier_order->enabled, 'perms'=>1, 'label'=>'LinkToSupplierOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('commande_fournisseur').')'),
7988
				'invoice_supplier'=>array('enabled'=>$conf->supplier_invoice->enabled, 'perms'=>1, 'label'=>'LinkToSupplierInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('facture_fourn').')'),
7989
				'ticket'=>array('enabled'=>$conf->ticket->enabled, 'perms'=>1, 'label'=>'LinkToTicket', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.track_id, '0' as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."ticket as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('ticket').')'),
7990
				'mo'=>array('enabled'=>$conf->mrp->enabled, 'perms'=>1, 'label'=>'LinkToMO', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM ".MAIN_DB_PREFIX."societe as s INNER JOIN ".MAIN_DB_PREFIX."mrp_mo as t ON t.fk_soc = s.rowid  WHERE  t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('mo').')')
7991
			);
7992
		}
7993
7994
		// Can complete the possiblelink array
7995
		$hookmanager->initHooks(array('commonobject'));
7996
		$parameters = array('listofidcompanytoscan' => $listofidcompanytoscan);
7997
7998
		if (!empty($listofidcompanytoscan)) {  // If empty, we don't have criteria to scan the object we can link to
7999
			$reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
8000
		}
8001
8002
		if (empty($reshook)) {
8003
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
8004
				$possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
8005
			}
8006
		} elseif ($reshook > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $reshook does not seem to be defined for all execution paths leading up to this point.
Loading history...
8007
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
8008
				$possiblelinks = $hookmanager->resArray;
8009
			}
8010
		}
8011
8012
		foreach ($possiblelinks as $key => $possiblelink) {
8013
			$num = 0;
8014
8015
			if (empty($possiblelink['enabled'])) {
8016
				continue;
8017
			}
8018
8019
			if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
8020
				print '<div id="'.$key.'list"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display:none"').'>';
8021
8022
				if (!empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8023
					print '<br><form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
8024
					print '<input type="hidden" name="id" value="' . $object->id . '">';
8025
					print '<input type="hidden" name="action" value="addlinkbyref">';
8026
					print '<input type="hidden" name="addlink" value="' . $key . '">';
8027
					print '<table class="noborder">';
8028
					print '<tr>';
8029
					print '<td>' . $langs->trans("Ref") . '</td>';
8030
					print '<td><input type="text" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">&nbsp;<input type="submit" class="button valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;<input type="submit" class="button" name="cancel" value="' . $langs->trans('Cancel') . '"></td>';
8031
					print '</tr>';
8032
					print '</table>';
8033
					print '</form>';
8034
				}
8035
8036
				$sql = $possiblelink['sql'];
8037
8038
				$resqllist = $this->db->query($sql);
8039
				if ($resqllist) {
8040
					$num = $this->db->num_rows($resqllist);
8041
					$i = 0;
8042
8043
					print '<br>';
8044
					print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
8045
					print '<input type="hidden" name="action" value="addlink">';
8046
					print '<input type="hidden" name="token" value="'.newToken().'">';
8047
					print '<input type="hidden" name="id" value="'.$object->id.'">';
8048
					print '<input type="hidden" name="addlink" value="'.$key.'">';
8049
					print '<table class="noborder">';
8050
					print '<tr class="liste_titre">';
8051
					print '<td class="nowrap"></td>';
8052
					print '<td class="center">'.$langs->trans("Ref").'</td>';
8053
					print '<td class="left">'.$langs->trans("RefCustomer").'</td>';
8054
					print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
8055
					print '<td class="left">'.$langs->trans("Company").'</td>';
8056
					print '</tr>';
8057
					while ($i < $num) {
8058
						$objp = $this->db->fetch_object($resqllist);
8059
8060
						print '<tr class="oddeven">';
8061
						print '<td class="left">';
8062
						print '<input type="radio" name="idtolinkto" id="'.$key.'_'.$objp->rowid.'" value="'.$objp->rowid.'">';
8063
						print '</td>';
8064
						print '<td class="center"><label for="'.$key.'_'.$objp->rowid.'">'.$objp->ref.'</label></td>';
8065
						print '<td>'.(!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')).'</td>';
8066
						print '<td class="right">';
8067
						if ($possiblelink['label'] == 'LinkToContract') {
8068
							$form = new Form($this->db);
8069
							print $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")).' ';
8070
						}
8071
						print '<span class="amount">'.price($objp->total_ht).'</span>';
8072
						print '</td>';
8073
						print '<td>'.$objp->name.'</td>';
8074
						print '</tr>';
8075
						$i++;
8076
					}
8077
					print '</table>';
8078
					print '<div class="center">';
8079
					print '<input type="submit" class="button valignmiddle marginleftonly marginrightonly" value="'.$langs->trans('ToLink').'">';
8080
					if (empty($conf->use_javascript_ajax)) {
8081
						print '<input type="submit" class="button button-cancel marginleftonly marginrightonly" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
8082
					} else {
8083
						print '<input type="submit"; onclick="javascript:jQuery(\'#'.$key.'list\').toggle(); return false;" class="button button-cancel marginleftonly marginrightonly" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
8084
					}
8085
					print '</form>';
8086
					$this->db->free($resqllist);
8087
				} else {
8088
					dol_print_error($this->db);
8089
				}
8090
				print '</div>';
8091
8092
				//$linktoelem.=($linktoelem?' &nbsp; ':'');
8093
				if ($num > 0 || !empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8094
					$linktoelemlist .= '<li><a href="#linkto'.$key.'" class="linkto dropdowncloseonclick" rel="'.$key.'">'.$langs->trans($possiblelink['label']).' ('.$num.')</a></li>';
8095
					// } else $linktoelem.=$langs->trans($possiblelink['label']);
8096
				} else {
8097
					$linktoelemlist .= '<li><span class="linktodisabled">'.$langs->trans($possiblelink['label']).' (0)</span></li>';
8098
				}
8099
			}
8100
		}
8101
8102
		if ($linktoelemlist) {
8103
			$linktoelem = '
8104
    		<dl class="dropdown" id="linktoobjectname">
8105
    		';
8106
			if (!empty($conf->use_javascript_ajax)) {
8107
				$linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>'.$langs->trans("LinkTo").'...</a></dt>';
8108
			}
8109
			$linktoelem .= '<dd>
8110
    		<div class="multiselectlinkto">
8111
    		<ul class="ulselectedfields">'.$linktoelemlist.'
8112
    		</ul>
8113
    		</div>
8114
    		</dd>
8115
    		</dl>';
8116
		} else {
8117
			$linktoelem = '';
8118
		}
8119
8120
		if (!empty($conf->use_javascript_ajax)) {
8121
			print '<!-- Add js to show linkto box -->
8122
				<script>
8123
				jQuery(document).ready(function() {
8124
					jQuery(".linkto").click(function() {
8125
						console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
8126
					    jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
8127
					});
8128
				});
8129
				</script>
8130
		    ';
8131
		}
8132
8133
		return $linktoelem;
8134
	}
8135
8136
	/**
8137
	 *	Return an html string with a select combo box to choose yes or no
8138
	 *
8139
	 *	@param	string		$htmlname		Name of html select field
8140
	 *	@param	string		$value			Pre-selected value
8141
	 *	@param	int			$option			0 return yes/no, 1 return 1/0
8142
	 *	@param	bool		$disabled		true or false
8143
	 *  @param	int      	$useempty		1=Add empty line
8144
	 *  @param	int			$addjscombo		1=Add js beautifier on combo box
8145
	 *  @param	string		$morecss		More CSS
8146
	 *	@return	string						See option
8147
	 */
8148
	public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = '')
8149
	{
8150
		global $langs;
8151
8152
		$yes = "yes";
8153
		$no = "no";
8154
		if ($option) {
8155
			$yes = "1";
8156
			$no = "0";
8157
		}
8158
8159
		$disabled = ($disabled ? ' disabled' : '');
8160
8161
		$resultyesno = '<select class="flat width75'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'"'.$disabled.'>'."\n";
8162
		if ($useempty) {
8163
			$resultyesno .= '<option value="-1"'.(($value < 0) ? ' selected' : '').'>&nbsp;</option>'."\n";
8164
		}
8165
		if (("$value" == 'yes') || ($value == 1)) {
8166
			$resultyesno .= '<option value="'.$yes.'" selected>'.$langs->trans("Yes").'</option>'."\n";
8167
			$resultyesno .= '<option value="'.$no.'">'.$langs->trans("No").'</option>'."\n";
8168
		} else {
8169
			$selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
8170
			$resultyesno .= '<option value="'.$yes.'">'.$langs->trans("Yes").'</option>'."\n";
8171
			$resultyesno .= '<option value="'.$no.'"'.$selected.'>'.$langs->trans("No").'</option>'."\n";
8172
		}
8173
		$resultyesno .= '</select>'."\n";
8174
8175
		if ($addjscombo) {
8176
			$resultyesno .= ajax_combobox($htmlname);
8177
		}
8178
8179
		return $resultyesno;
8180
	}
8181
8182
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8183
	/**
8184
	 *  Return list of export templates
8185
	 *
8186
	 *  @param	string	$selected          Id modele pre-selectionne
8187
	 *  @param  string	$htmlname          Name of HTML select
8188
	 *  @param  string	$type              Type of searched templates
8189
	 *  @param  int		$useempty          Affiche valeur vide dans liste
8190
	 *  @return	void
8191
	 */
8192
	public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
8193
	{
8194
		// phpcs:enable
8195
		$sql = "SELECT rowid, label";
8196
		$sql .= " FROM ".MAIN_DB_PREFIX."export_model";
8197
		$sql .= " WHERE type = '".$this->db->escape($type)."'";
8198
		$sql .= " ORDER BY rowid";
8199
		$result = $this->db->query($sql);
8200
		if ($result) {
8201
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
8202
			if ($useempty) {
8203
				print '<option value="-1">&nbsp;</option>';
8204
			}
8205
8206
			$num = $this->db->num_rows($result);
8207
			$i = 0;
8208
			while ($i < $num) {
8209
				$obj = $this->db->fetch_object($result);
8210
				if ($selected == $obj->rowid) {
8211
					print '<option value="'.$obj->rowid.'" selected>';
8212
				} else {
8213
					print '<option value="'.$obj->rowid.'">';
8214
				}
8215
				print $obj->label;
8216
				print '</option>';
8217
				$i++;
8218
			}
8219
			print "</select>";
8220
		} else {
8221
			dol_print_error($this->db);
8222
		}
8223
	}
8224
8225
	/**
8226
	 *    Return a HTML area with the reference of object and a navigation bar for a business object
8227
	 *    Note: To complete search with a particular filter on select, you can set $object->next_prev_filter set to define SQL criterias.
8228
	 *
8229
	 *    @param	object	$object			Object to show.
8230
	 *    @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link.
8231
	 *    @param	string	$morehtml  		More html content to output just before the nav bar.
8232
	 *    @param	int		$shownav	  	Show Condition (navigation is shown if value is 1).
8233
	 *    @param	string	$fieldid   		Name of field id into database to use for select next and previous (we make the select max and min on this field compared to $object->ref). Use 'none' to disable next/prev.
8234
	 *    @param	string	$fieldref   	Name of field ref of object (object->ref) to show or 'none' to not show ref.
8235
	 *    @param	string	$morehtmlref  	More html to show after ref.
8236
	 *    @param	string	$moreparam  	More param to add in nav link url. Must start with '&...'.
8237
	 *	  @param	int		$nodbprefix		Do not include DB prefix to forge table name.
8238
	 *	  @param	string	$morehtmlleft	More html code to show before ref.
8239
	 *	  @param	string	$morehtmlstatus	More html code to show under navigation arrows (status place).
8240
	 *	  @param	string	$morehtmlright	More html code to show after ref.
8241
	 * 	  @return	string    				Portion HTML with ref + navigation buttons
8242
	 */
8243
	public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
8244
	{
8245
		global $langs, $conf, $hookmanager, $extralanguages;
8246
8247
		$ret = '';
8248
		if (empty($fieldid)) {
8249
			$fieldid = 'rowid';
8250
		}
8251
		if (empty($fieldref)) {
8252
			$fieldref = 'ref';
8253
		}
8254
8255
		// Preparing gender's display if there is one
8256
		$addgendertxt = '';
8257
		if (!empty($object->gender)) {
8258
			$addgendertxt = ' ';
8259
			switch ($object->gender) {
8260
				case 'man':
8261
					$addgendertxt .= '<i class="fas fa-mars"></i>';
8262
					break;
8263
				case 'woman':
8264
					$addgendertxt .= '<i class="fas fa-venus"></i>';
8265
					break;
8266
				case 'other':
8267
					$addgendertxt .= '<i class="fas fa-genderless"></i>';
8268
					break;
8269
			}
8270
		}
8271
8272
		// Add where from hooks
8273
		if (is_object($hookmanager)) {
8274
			$parameters = array();
8275
			$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
8276
			$object->next_prev_filter .= $hookmanager->resPrint;
8277
		}
8278
		$previous_ref = $next_ref = '';
8279
		if ($shownav) {
8280
			//print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
8281
			$object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
8282
8283
			$navurl = $_SERVER["PHP_SELF"];
8284
			// Special case for project/task page
8285
			if ($paramid == 'project_ref') {
8286
				if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) {     // TODO Remove this when nav with project_ref on task pages are ok
8287
					$navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
8288
					$paramid = 'ref';
8289
				}
8290
			}
8291
8292
			// accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
8293
			// accesskey is for Mac:               CTRL + key for all browsers
8294
			$stringforfirstkey = $langs->trans("KeyboardShortcut");
8295
			if ($conf->browser->name == 'chrome') {
8296
				$stringforfirstkey .= ' ALT +';
8297
			} elseif ($conf->browser->name == 'firefox') {
8298
				$stringforfirstkey .= ' ALT + SHIFT +';
8299
			} else {
8300
				$stringforfirstkey .= ' CTL +';
8301
			}
8302
8303
			$previous_ref = $object->ref_previous ? '<a accesskey="p" title="'.$stringforfirstkey.' p" class="classfortooltip" href="'.$navurl.'?'.$paramid.'='.urlencode($object->ref_previous).$moreparam.'"><i class="fa fa-chevron-left"></i></a>' : '<span class="inactive"><i class="fa fa-chevron-left opacitymedium"></i></span>';
8304
			$next_ref     = $object->ref_next ? '<a accesskey="n" title="'.$stringforfirstkey.' n" class="classfortooltip" href="'.$navurl.'?'.$paramid.'='.urlencode($object->ref_next).$moreparam.'"><i class="fa fa-chevron-right"></i></a>' : '<span class="inactive"><i class="fa fa-chevron-right opacitymedium"></i></span>';
8305
		}
8306
8307
		//print "xx".$previous_ref."x".$next_ref;
8308
		$ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
8309
8310
		// Right part of banner
8311
		if ($morehtmlright) {
8312
			$ret .= '<div class="inline-block floatleft">'.$morehtmlright.'</div>';
8313
		}
8314
8315
		if ($previous_ref || $next_ref || $morehtml) {
8316
			$ret .= '<div class="pagination paginationref"><ul class="right">';
8317
		}
8318
		if ($morehtml) {
8319
			$ret .= '<li class="noborder litext'.(($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '').'">'.$morehtml.'</li>';
8320
		}
8321
		if ($shownav && ($previous_ref || $next_ref)) {
8322
			$ret .= '<li class="pagination">'.$previous_ref.'</li>';
8323
			$ret .= '<li class="pagination">'.$next_ref.'</li>';
8324
		}
8325
		if ($previous_ref || $next_ref || $morehtml) {
8326
			$ret .= '</ul></div>';
8327
		}
8328
8329
		$parameters = array();
8330
		$reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
8331
		if (empty($reshook)) {
8332
			$morehtmlstatus .= $hookmanager->resPrint;
8333
		} else {
8334
			$morehtmlstatus = $hookmanager->resPrint;
8335
		}
8336
		if ($morehtmlstatus) {
8337
			$ret .= '<div class="statusref">'.$morehtmlstatus.'</div>';
8338
		}
8339
8340
		$parameters = array();
8341
		$reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
8342
		if (empty($reshook)) {
8343
			$morehtmlref .= $hookmanager->resPrint;
8344
		} elseif ($reshook > 0) {
8345
			$morehtmlref = $hookmanager->resPrint;
8346
		}
8347
8348
		// Left part of banner
8349
		if ($morehtmlleft) {
8350
			if ($conf->browser->layout == 'phone') {
8351
				$ret .= '<!-- morehtmlleft --><div class="floatleft">'.$morehtmlleft.'</div>'; // class="center" to have photo in middle
8352
			} else {
8353
				$ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">'.$morehtmlleft.'</div>';
8354
			}
8355
		}
8356
8357
		//if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
8358
		$ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid'.(($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '').'">';
8359
8360
		// For thirdparty, contact, user, member, the ref is the id, so we show something else
8361
		if ($object->element == 'societe') {
8362
			$ret .= dol_htmlentities($object->name);
8363
8364
			// List of extra languages
8365
			$arrayoflangcode = array();
8366
			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
8367
				$arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
8368
			}
8369
8370
			if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
8371
				if (!is_object($extralanguages)) {
8372
					include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
8373
					$extralanguages = new ExtraLanguages($this->db);
8374
				}
8375
				$extralanguages->fetch_name_extralanguages('societe');
8376
8377
				if (!empty($extralanguages->attributes['societe']['name'])) {
8378
					$object->fetchValuesForExtraLanguages();
8379
8380
					$htmltext = '';
8381
					// If there is extra languages
8382
					foreach ($arrayoflangcode as $extralangcode) {
8383
						$htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
8384
						if ($object->array_languages['name'][$extralangcode]) {
8385
							$htmltext .= $object->array_languages['name'][$extralangcode];
8386
						} else {
8387
							$htmltext .= '<span class="opacitymedium">'.$langs->trans("SwitchInEditModeToAddTranslation").'</span>';
8388
						}
8389
					}
8390
					$ret .= '<!-- Show translations of name -->'."\n";
8391
					$ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
8392
				}
8393
			}
8394
		} elseif ($object->element == 'member') {
8395
			$ret .= $object->ref.'<br>';
8396
			$fullname = $object->getFullName($langs);
8397
			if ($object->morphy == 'mor' && $object->societe) {
8398
				$ret .= dol_htmlentities($object->societe).((!empty($fullname) && $object->societe != $fullname) ? ' ('.dol_htmlentities($fullname).$addgendertxt.')' : '');
8399
			} else {
8400
				$ret .= dol_htmlentities($fullname).$addgendertxt.((!empty($object->societe) && $object->societe != $fullname) ? ' ('.dol_htmlentities($object->societe).')' : '');
8401
			}
8402
		} elseif (in_array($object->element, array('contact', 'user', 'usergroup'))) {
8403
			$ret .= dol_htmlentities($object->getFullName($langs)).$addgendertxt;
8404
		} elseif (in_array($object->element, array('action', 'agenda'))) {
8405
			$ret .= $object->ref.'<br>'.$object->label;
8406
		} elseif (in_array($object->element, array('adherent_type'))) {
8407
			$ret .= $object->label;
8408
		} elseif ($object->element == 'ecm_directories') {
8409
			$ret .= '';
8410
		} elseif ($fieldref != 'none') {
8411
			$ret .= dol_htmlentities($object->$fieldref);
8412
		}
8413
8414
		if ($morehtmlref) {
8415
			// don't add a additional space, when "$morehtmlref" starts with a HTML div tag
8416
			if (substr($morehtmlref, 0, 4) != '<div') {
8417
				$ret .= ' ';
8418
			}
8419
8420
			$ret .= $morehtmlref;
8421
		}
8422
8423
		$ret .= '</div>';
8424
8425
		$ret .= '</div><!-- End banner content -->';
8426
8427
		return $ret;
8428
	}
8429
8430
8431
	/**
8432
	 *  Return HTML code to output a barcode
8433
	 *
8434
	 *  @param	Object	$object			Object containing data to retrieve file name
8435
	 * 	@param	int		$width			Width of photo
8436
	 * 	@param	string	$morecss		More CSS on img of barcode
8437
	 * 	@return string    				HTML code to output barcode
8438
	 */
8439
	public function showbarcode(&$object, $width = 100, $morecss = '')
8440
	{
8441
		global $conf;
8442
8443
		//Check if barcode is filled in the card
8444
		if (empty($object->barcode)) {
8445
			return '';
8446
		}
8447
8448
		// Complete object if not complete
8449
		if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
8450
			$result = $object->fetch_barcode();
8451
			//Check if fetch_barcode() failed
8452
			if ($result < 1) {
8453
				return '<!-- ErrorFetchBarcode -->';
8454
			}
8455
		}
8456
8457
		// Barcode image
8458
		$url = DOL_URL_ROOT.'/viewimage.php?modulepart=barcode&generator='.urlencode($object->barcode_type_coder).'&code='.urlencode($object->barcode).'&encoding='.urlencode($object->barcode_type_code);
8459
		$out = '<!-- url barcode = '.$url.' -->';
8460
		$out .= '<img src="'.$url.'"'.($morecss ? ' class="'.$morecss.'"' : '').'>';
8461
		return $out;
8462
	}
8463
8464
	/**
8465
	 *    	Return HTML code to output a photo
8466
	 *
8467
	 *    	@param	string		$modulepart			Key to define module concerned ('societe', 'userphoto', 'memberphoto')
8468
	 *     	@param  object		$object				Object containing data to retrieve file name
8469
	 * 		@param	int			$width				Width of photo
8470
	 * 		@param	int			$height				Height of photo (auto if 0)
8471
	 * 		@param	int			$caneditfield		Add edit fields
8472
	 * 		@param	string		$cssclass			CSS name to use on img for photo
8473
	 * 		@param	string		$imagesize		    'mini', 'small' or '' (original)
8474
	 *      @param  int         $addlinktofullsize  Add link to fullsize image
8475
	 *      @param  int         $cache              1=Accept to use image in cache
8476
	 *      @param	string		$forcecapture		'', 'user' or 'environment'. Force parameter capture on HTML input file element to ask a smartphone to allow to open camera to take photo. Auto if ''.
8477
	 *      @param	int			$noexternsourceoverwrite	No overwrite image with extern source (like 'gravatar' or other module)
8478
	 * 	  	@return string    						HTML code to output photo
8479
	 */
8480
	public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
8481
	{
8482
		global $conf, $langs;
8483
8484
		$entity = (!empty($object->entity) ? $object->entity : $conf->entity);
8485
		$id = (!empty($object->id) ? $object->id : $object->rowid);
8486
8487
		$ret = '';
8488
		$dir = '';
8489
		$file = '';
8490
		$originalfile = '';
8491
		$altfile = '';
8492
		$email = '';
8493
		$capture = '';
8494
		if ($modulepart == 'societe') {
8495
			$dir = $conf->societe->multidir_output[$entity];
8496
			if (!empty($object->logo)) {
8497
				if (dolIsAllowedForPreview($object->logo)) {
8498
					if ((string) $imagesize == 'mini') {
8499
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
8500
					} elseif ((string) $imagesize == 'small') {
8501
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_small');
8502
					} else {
8503
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
8504
					}
8505
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
8506
				}
8507
			}
8508
			$email = $object->email;
8509
		} elseif ($modulepart == 'contact')	{
8510
			$dir = $conf->societe->multidir_output[$entity].'/contact';
8511
			if (!empty($object->photo)) {
8512
				if (dolIsAllowedForPreview($object->photo)) {
8513
					if ((string) $imagesize == 'mini') {
8514
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_mini');
8515
					} elseif ((string) $imagesize == 'small') {
8516
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_small');
8517
					} else {
8518
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
8519
					}
8520
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
8521
				}
8522
			}
8523
			$email = $object->email;
8524
			$capture = 'user';
8525
		} elseif ($modulepart == 'userphoto') {
8526
			$dir = $conf->user->dir_output;
8527
			if (!empty($object->photo)) {
8528
				if (dolIsAllowedForPreview($object->photo)) {
8529
					if ((string) $imagesize == 'mini') {
8530
						$file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_mini');
8531
					} elseif ((string) $imagesize == 'small') {
8532
						$file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_small');
8533
					} else {
8534
						$file = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
8535
					}
8536
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
8537
				}
8538
			}
8539
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8540
				$altfile = $object->id.".jpg"; // For backward compatibility
8541
			}
8542
			$email = $object->email;
8543
			$capture = 'user';
8544
		} elseif ($modulepart == 'memberphoto')	{
8545
			$dir = $conf->adherent->dir_output;
8546
			if (!empty($object->photo)) {
8547
				if (dolIsAllowedForPreview($object->photo)) {
8548
					if ((string) $imagesize == 'mini') {
8549
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_mini');
8550
					} elseif ((string) $imagesize == 'small') {
8551
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_small');
8552
					} else {
8553
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
8554
					}
8555
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
8556
				}
8557
			}
8558
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8559
				$altfile = $object->id.".jpg"; // For backward compatibility
8560
			}
8561
			$email = $object->email;
8562
			$capture = 'user';
8563
		} else {
8564
			// Generic case to show photos
8565
			$dir = $conf->$modulepart->dir_output;
8566
			if (!empty($object->photo)) {
8567
				if (dolIsAllowedForPreview($object->photo)) {
8568
					if ((string) $imagesize == 'mini') {
8569
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_mini');
8570
					} elseif ((string) $imagesize == 'small') {
8571
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_small');
8572
					} else {
8573
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
8574
					}
8575
					$originalfile = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
8576
				}
8577
			}
8578
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
8579
				$altfile = $object->id.".jpg"; // For backward compatibility
8580
			}
8581
			$email = $object->email;
8582
		}
8583
8584
		if ($forcecapture) {
8585
			$capture = $forcecapture;
8586
		}
8587
8588
		if ($dir) {
8589
			if ($file && file_exists($dir."/".$file)) {
8590
				if ($addlinktofullsize) {
8591
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
8592
					if ($urladvanced) {
8593
						$ret .= '<a href="'.$urladvanced.'">';
8594
					} else {
8595
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
8596
					}
8597
				}
8598
				$ret .= '<img alt="Photo" class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').' photologo'.(preg_replace('/[^a-z]/i', '_', $file)).'" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($file).'&cache='.$cache.'">';
8599
				if ($addlinktofullsize) {
8600
					$ret .= '</a>';
8601
				}
8602
			} elseif ($altfile && file_exists($dir."/".$altfile)) {
8603
				if ($addlinktofullsize) {
8604
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
8605
					if ($urladvanced) {
8606
						$ret .= '<a href="'.$urladvanced.'">';
8607
					} else {
8608
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
8609
					}
8610
				}
8611
				$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="Photo alt" id="photologo'.(preg_replace('/[^a-z]/i', '_', $file)).'" class="'.$cssclass.'" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($altfile).'&cache='.$cache.'">';
8612
				if ($addlinktofullsize) {
8613
					$ret .= '</a>';
8614
				}
8615
			} else {
8616
				$nophoto = '/public/theme/common/nophoto.png';
8617
				$defaultimg = 'identicon';		// For gravatar
8618
				if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) {	// For modules that need a special image when photo not found
8619
					if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && strpos($object->morphy, 'mor')) !== false) {
8620
						$nophoto = 'company';
8621
					} else {
8622
						$nophoto = '/public/theme/common/user_anonymous.png';
8623
						if (!empty($object->gender) && $object->gender == 'man') {
8624
							$nophoto = '/public/theme/common/user_man.png';
8625
						}
8626
						if (!empty($object->gender) && $object->gender == 'woman') {
8627
							$nophoto = '/public/theme/common/user_woman.png';
8628
						}
8629
					}
8630
				}
8631
8632
				if (!empty($conf->gravatar->enabled) && $email && empty($noexternsourceoverwrite)) {
8633
					// see https://gravatar.com/site/implement/images/php/
8634
					$ret .= '<!-- Put link to gravatar -->';
8635
					$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="Gravatar avatar" title="'.$email.' Gravatar avatar" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="https://www.gravatar.com/avatar/'.md5(strtolower(trim($email))).'?s='.$width.'&d='.$defaultimg.'">'; // gravatar need md5 hash
8636
				} else {
8637
					if ($nophoto == 'company') {
8638
						$ret .= '<div class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').'">'.img_picto('', 'company').'</div>';
8639
					} else {
8640
						$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.$nophoto.'">';
8641
					}
8642
				}
8643
			}
8644
8645
			if ($caneditfield) {
8646
				if ($object->photo) {
8647
					$ret .= "<br>\n";
8648
				}
8649
				$ret .= '<table class="nobordernopadding centpercent">';
8650
				if ($object->photo) {
8651
					$ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> '.$langs->trans("Delete").'<br><br></td></tr>';
8652
				}
8653
				$ret .= '<tr><td class="tdoverflow"><input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"'.($capture ? ' capture="'.$capture.'"' : '').'></td></tr>';
8654
				$ret .= '</table>';
8655
			}
8656
		} else {
8657
			dol_print_error('', 'Call of showphoto with wrong parameters modulepart='.$modulepart);
8658
		}
8659
8660
		return $ret;
8661
	}
8662
8663
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8664
	/**
8665
	 *	Return select list of groups
8666
	 *
8667
	 *  @param	string	$selected       Id group preselected
8668
	 *  @param  string	$htmlname       Field name in form
8669
	 *  @param  int		$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
8670
	 *  @param  string	$exclude        Array list of groups id to exclude
8671
	 * 	@param	int		$disabled		If select list must be disabled
8672
	 *  @param  string	$include        Array list of groups id to include
8673
	 * 	@param	int		$enableonly		Array list of groups id to be enabled. All other must be disabled
8674
	 * 	@param	string	$force_entity	'0' or Ids of environment to force
8675
	 * 	@param	bool	$multiple		add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
8676
	 *  @param  string	$morecss		More css to add to html component
8677
	 *  @return	string
8678
	 *  @see select_dolusers()
8679
	 */
8680
	public function select_dolgroups($selected = '', $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $multiple = false, $morecss = '')
8681
	{
8682
		// phpcs:enable
8683
		global $conf, $user, $langs;
8684
8685
		// Permettre l'exclusion de groupes
8686
		if (is_array($exclude)) {
0 ignored issues
show
introduced by
The condition is_array($exclude) is always false.
Loading history...
8687
			$excludeGroups = implode(",", $exclude);
8688
		}
8689
		// Permettre l'inclusion de groupes
8690
		if (is_array($include)) {
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
8691
			$includeGroups = implode(",", $include);
8692
		}
8693
8694
		if (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
8695
			$selected = array($selected);
8696
		}
8697
8698
		$out = '';
8699
8700
		// On recherche les groupes
8701
		$sql = "SELECT ug.rowid, ug.nom as name";
8702
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
8703
			$sql .= ", e.label";
8704
		}
8705
		$sql .= " FROM ".MAIN_DB_PREFIX."usergroup as ug ";
8706
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
8707
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=ug.entity";
8708
			if ($force_entity) {
8709
				$sql .= " WHERE ug.entity IN (0, ".$force_entity.")";
8710
			} else {
8711
				$sql .= " WHERE ug.entity IS NOT NULL";
8712
			}
8713
		} else {
8714
			$sql .= " WHERE ug.entity IN (0, ".$conf->entity.")";
8715
		}
8716
		if (is_array($exclude) && $excludeGroups) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $excludeGroups does not seem to be defined for all execution paths leading up to this point.
Loading history...
introduced by
The condition is_array($exclude) is always false.
Loading history...
8717
			$sql .= " AND ug.rowid NOT IN (".$this->db->sanitize($excludeGroups).")";
8718
		}
8719
		if (is_array($include) && $includeGroups) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $includeGroups does not seem to be defined for all execution paths leading up to this point.
Loading history...
introduced by
The condition is_array($include) is always false.
Loading history...
8720
			$sql .= " AND ug.rowid IN (".$this->db->sanitize($includeGroups).")";
8721
		}
8722
		$sql .= " ORDER BY ug.nom ASC";
8723
8724
		dol_syslog(get_class($this)."::select_dolgroups", LOG_DEBUG);
8725
		$resql = $this->db->query($sql);
8726
		if ($resql) {
8727
			// Enhance with select2
8728
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
8729
			$out .= ajax_combobox($htmlname);
8730
8731
			$out .= '<select class="flat minwidth200'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
8732
8733
			$num = $this->db->num_rows($resql);
8734
			$i = 0;
8735
			if ($num) {
8736
				if ($show_empty && !$multiple) {
8737
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>&nbsp;</option>'."\n";
8738
				}
8739
8740
				while ($i < $num) {
8741
					$obj = $this->db->fetch_object($resql);
8742
					$disableline = 0;
8743
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
8744
						$disableline = 1;
8745
					}
8746
8747
					$out .= '<option value="'.$obj->rowid.'"';
8748
					if ($disableline) {
8749
						$out .= ' disabled';
8750
					}
8751
					if ((is_object($selected[0]) && $selected[0]->id == $obj->rowid) || (!is_object($selected[0]) && in_array($obj->rowid, $selected))) {
8752
						$out .= ' selected';
8753
					}
8754
					$out .= '>';
8755
8756
					$out .= $obj->name;
8757
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1) {
8758
						$out .= " (".$obj->label.")";
8759
					}
8760
8761
					$out .= '</option>';
8762
					$i++;
8763
				}
8764
			} else {
8765
				if ($show_empty) {
8766
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'></option>'."\n";
8767
				}
8768
				$out .= '<option value="" disabled>'.$langs->trans("NoUserGroupDefined").'</option>';
8769
			}
8770
			$out .= '</select>';
8771
		} else {
8772
			dol_print_error($this->db);
8773
		}
8774
8775
		return $out;
8776
	}
8777
8778
8779
	/**
8780
	 *	Return HTML to show the search and clear seach button
8781
	 *
8782
	 *  @return	string
8783
	 */
8784
	public function showFilterButtons()
8785
	{
8786
		$out = '<div class="nowraponall">';
8787
		$out .= '<button type="submit" class="liste_titre button_search" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
8788
		$out .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
8789
		$out .= '</div>';
8790
8791
		return $out;
8792
	}
8793
8794
	/**
8795
	 *	Return HTML to show the search and clear search button
8796
	 *
8797
	 *  @param  string  $cssclass                  CSS class
8798
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
8799
	 *  @param  string  $massactionname            Mass action button name that will launch an action on the selected items
8800
	 *  @return	string
8801
	 */
8802
	public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
8803
	{
8804
		global $conf, $langs;
8805
8806
		$out = '';
8807
8808
		if (!empty($conf->use_javascript_ajax)) {
8809
			$out .= '<div class="inline-block checkallactions"><input type="checkbox" id="'.$cssclass.'s" name="'.$cssclass.'s" class="checkallactions"></div>';
8810
		}
8811
		$out .= '<script>
8812
            $(document).ready(function() {
8813
                $("#' . $cssclass.'s").click(function() {
8814
                    if($(this).is(\':checked\')){
8815
                        console.log("We check all '.$cssclass.' and trigger the change method");
8816
                		$(".'.$cssclass.'").prop(\'checked\', true).trigger(\'change\');
8817
                    }
8818
                    else
8819
                    {
8820
                        console.log("We uncheck all");
8821
                		$(".'.$cssclass.'").prop(\'checked\', false).trigger(\'change\');
8822
                    }'."\n";
8823
		if ($calljsfunction) {
8824
			$out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "'.$massactionname.'", "'.$cssclass.'"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
8825
		}
8826
		$out .= '         });
8827
        	        $(".' . $cssclass.'").change(function() {
8828
					$(this).closest("tr").toggleClass("highlight", this.checked);
8829
				});
8830
		 	});
8831
    	</script>';
8832
8833
		return $out;
8834
	}
8835
8836
	/**
8837
	 *	Return HTML to show the search and clear seach button
8838
	 *
8839
	 *  @param	int  	$addcheckuncheckall        Add the check all/uncheck all checkbox (use javascript) and code to manage this
8840
	 *  @param  string  $cssclass                  CSS class
8841
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
8842
	 *  @param  string  $massactionname            Mass action name
8843
	 *  @return	string
8844
	 */
8845
	public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
8846
	{
8847
		$out = $this->showFilterButtons();
8848
		if ($addcheckuncheckall) {
8849
			$out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
8850
		}
8851
		return $out;
8852
	}
8853
8854
	/**
8855
	 * Return HTML to show the select of expense categories
8856
	 *
8857
	 * @param	string	$selected              preselected category
8858
	 * @param	string	$htmlname              name of HTML select list
8859
	 * @param	integer	$useempty              1=Add empty line
8860
	 * @param	array	$excludeid             id to exclude
8861
	 * @param	string	$target                htmlname of target select to bind event
8862
	 * @param	int		$default_selected      default category to select if fk_c_type_fees change = EX_KME
8863
	 * @param	array	$params                param to give
8864
	 * @param	int		$info_admin			   Show the tooltip help picto to setup list
8865
	 * @return	string
8866
	 */
8867
	public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
8868
	{
8869
		global $db, $langs, $user;
8870
8871
		$out = '';
8872
		$sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1';
8873
		$sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')';
8874
		if (!empty($excludeid)) {
8875
			$sql .= ' AND rowid NOT IN ('.$this->db->sanitize(implode(',', $excludeid)).')';
8876
		}
8877
		$sql .= ' ORDER BY label';
8878
8879
		$resql = $db->query($sql);
8880
		if ($resql) {
8881
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp maxwidth200">';
8882
			if ($useempty) {
8883
				$out .= '<option value="0">&nbsp;</option>';
8884
			}
8885
8886
			while ($obj = $db->fetch_object($resql)) {
8887
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.$langs->trans($obj->label).'</option>';
8888
			}
8889
			$out .= '</select>';
8890
			$out .= ajax_combobox('select_'.$htmlname);
8891
8892
			if (!empty($htmlname) && $user->admin && $info_admin) {
8893
				$out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
8894
			}
8895
8896
			if (!empty($target)) {
8897
				$sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
8898
				$resql = $db->query($sql);
8899
				if ($resql) {
8900
					if ($db->num_rows($resql) > 0) {
8901
						$obj = $db->fetch_object($resql);
8902
						$out .= '<script>
8903
							$(function() {
8904
								$("select[name='.$target.']").on("change", function() {
8905
									var current_val = $(this).val();
8906
									if (current_val == '.$obj->id.') {';
8907
						if (!empty($default_selected) || !empty($selected)) {
8908
							$out .= '$("select[name='.$htmlname.']").val("'.($default_selected > 0 ? $default_selected : $selected).'");';
8909
						}
8910
8911
						$out .= '
8912
										$("select[name='.$htmlname.']").change();
8913
									}
8914
								});
8915
8916
								$("select[name='.$htmlname.']").change(function() {
8917
8918
									if ($("select[name='.$target.']").val() == '.$obj->id.') {
8919
										// get price of kilometer to fill the unit price
8920
										$.ajax({
8921
											method: "POST",
8922
											dataType: "json",
8923
											data: { fk_c_exp_tax_cat: $(this).val(), token: \''.currentToken().'\' },
8924
											url: "'.(DOL_URL_ROOT.'/expensereport/ajax/ajaxik.php?'.$params).'",
8925
										}).done(function( data, textStatus, jqXHR ) {
8926
											console.log(data);
8927
											if (typeof data.up != "undefined") {
8928
												$("input[name=value_unit]").val(data.up);
8929
												$("select[name='.$htmlname.']").attr("title", data.title);
8930
											} else {
8931
												$("input[name=value_unit]").val("");
8932
												$("select[name='.$htmlname.']").attr("title", "");
8933
											}
8934
										});
8935
									}
8936
								});
8937
							});
8938
						</script>';
8939
					}
8940
				}
8941
			}
8942
		} else {
8943
			dol_print_error($db);
8944
		}
8945
8946
		return $out;
8947
	}
8948
8949
	/**
8950
	 * Return HTML to show the select ranges of expense range
8951
	 *
8952
	 * @param	string	$selected    preselected category
8953
	 * @param	string	$htmlname    name of HTML select list
8954
	 * @param	integer	$useempty    1=Add empty line
8955
	 * @return	string
8956
	 */
8957
	public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
8958
	{
8959
		global $db, $conf, $langs;
8960
8961
		$out = '';
8962
		$sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range';
8963
		$sql .= ' WHERE entity = '.$conf->entity.' AND active = 1';
8964
8965
		$resql = $db->query($sql);
8966
		if ($resql) {
8967
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
8968
			if ($useempty) {
8969
				$out .= '<option value="0"></option>';
8970
			}
8971
8972
			while ($obj = $db->fetch_object($resql)) {
8973
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.price($obj->range_ik, 0, $langs, 1, 0).'</option>';
8974
			}
8975
			$out .= '</select>';
8976
		} else {
8977
			dol_print_error($db);
8978
		}
8979
8980
		return $out;
8981
	}
8982
8983
	/**
8984
	 * Return HTML to show a select of expense
8985
	 *
8986
	 * @param	string	$selected    preselected category
8987
	 * @param	string	$htmlname    name of HTML select list
8988
	 * @param	integer	$useempty    1=Add empty choice
8989
	 * @param	integer	$allchoice   1=Add all choice
8990
	 * @param	integer	$useid       0=use 'code' as key, 1=use 'id' as key
8991
	 * @return	string
8992
	 */
8993
	public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
8994
	{
8995
		global $db, $langs;
8996
8997
		$out = '';
8998
		$sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees';
8999
		$sql .= ' WHERE active = 1';
9000
9001
		$resql = $db->query($sql);
9002
		if ($resql) {
9003
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
9004
			if ($useempty) {
9005
				$out .= '<option value="0"></option>';
9006
			}
9007
			if ($allchoice) {
9008
				$out .= '<option value="-1">'.$langs->trans('AllExpenseReport').'</option>';
9009
			}
9010
9011
			$field = 'code';
9012
			if ($useid) {
9013
				$field = 'id';
9014
			}
9015
9016
			while ($obj = $db->fetch_object($resql)) {
9017
				$key = $langs->trans($obj->code);
9018
				$out .= '<option '.($selected == $obj->{$field} ? 'selected="selected"' : '').' value="'.$obj->{$field}.'">'.($key != $obj->code ? $key : $obj->label).'</option>';
9019
			}
9020
			$out .= '</select>';
9021
		} else {
9022
			dol_print_error($db);
9023
		}
9024
9025
		return $out;
9026
	}
9027
9028
	/**
9029
	 *  Output a combo list with invoices qualified for a third party
9030
	 *
9031
	 *  @param	int		$socid      	Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id)
9032
	 *  @param  int		$selected   	Id invoice preselected
9033
	 *  @param  string	$htmlname   	Name of HTML select
9034
	 *	@param	int		$maxlength		Maximum length of label
9035
	 *	@param	int		$option_only	Return only html options lines without the select tag
9036
	 *	@param	string	$show_empty		Add an empty line ('1' or string to show for empty line)
9037
	 *  @param	int		$discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
9038
	 *  @param	int		$forcefocus		Force focus on field (works with javascript only)
9039
	 *  @param	int		$disabled		Disabled
9040
	 *  @param	string	$morecss        More css added to the select component
9041
	 *  @param	string	$projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
9042
	 *  @param	string	$showproject	'all' = Show project info, ''=Hide project info
9043
	 *  @param	User	$usertofilter	User object to use for filtering
9044
	 *	@return int         			Nbr of project if OK, <0 if KO
9045
	 */
9046
	public function selectInvoice($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null)
9047
	{
9048
		global $user, $conf, $langs;
9049
9050
		require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
9051
9052
		if (is_null($usertofilter)) {
9053
			$usertofilter = $user;
9054
		}
9055
9056
		$out = '';
9057
9058
		$hideunselectables = false;
9059
		if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) {
9060
			$hideunselectables = true;
9061
		}
9062
9063
		if (empty($projectsListId)) {
9064
			if (empty($usertofilter->rights->projet->all->lire)) {
9065
				$projectstatic = new Project($this->db);
9066
				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
9067
			}
9068
		}
9069
9070
		// Search all projects
9071
		$sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref,
9072
            p.title, p.fk_soc, p.fk_statut, p.public,";
9073
		$sql .= ' s.nom as name';
9074
		$sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p';
9075
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,';
9076
		$sql .= ' '.MAIN_DB_PREFIX.'facture as f';
9077
		$sql .= " WHERE p.entity IN (".getEntity('project').")";
9078
		$sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
9079
		//if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
9080
		//if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
9081
		//if ($socid > 0)  $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
9082
		$sql .= " ORDER BY p.ref, f.ref ASC";
9083
9084
		$resql = $this->db->query($sql);
9085
		if ($resql) {
9086
			// Use select2 selector
9087
			if (!empty($conf->use_javascript_ajax)) {
9088
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
9089
				$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
9090
				$out .= $comboenhancement;
9091
				$morecss = 'minwidth200imp maxwidth500';
9092
			}
9093
9094
			if (empty($option_only)) {
9095
				$out .= '<select class="valignmiddle flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').' id="'.$htmlname.'" name="'.$htmlname.'">';
9096
			}
9097
			if (!empty($show_empty)) {
9098
				$out .= '<option value="0" class="optiongrey">';
9099
				if (!is_numeric($show_empty)) {
9100
					$out .= $show_empty;
9101
				} else {
9102
					$out .= '&nbsp;';
9103
				}
9104
				$out .= '</option>';
9105
			}
9106
			$num = $this->db->num_rows($resql);
9107
			$i = 0;
9108
			if ($num) {
9109
				while ($i < $num) {
9110
					$obj = $this->db->fetch_object($resql);
9111
					// If we ask to filter on a company and user has no permission to see all companies and project is linked to another company, we hide project.
9112
					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire)) {
9113
						// Do nothing
9114
					} else {
9115
						if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
9116
							$i++;
9117
							continue;
9118
						}
9119
9120
						$labeltoshow = '';
9121
9122
						if ($showproject == 'all') {
9123
							$labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
9124
							if ($obj->name) {
9125
								$labeltoshow .= ' - '.$obj->name; // Soc name
9126
							}
9127
9128
							$disabled = 0;
9129
							if ($obj->fk_statut == Project::STATUS_DRAFT) {
9130
								$disabled = 1;
9131
								$labeltoshow .= ' - '.$langs->trans("Draft");
9132
							} elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
9133
								if ($discard_closed == 2) {
9134
									$disabled = 1;
9135
								}
9136
								$labeltoshow .= ' - '.$langs->trans("Closed");
9137
							} elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
9138
								$disabled = 1;
9139
								$labeltoshow .= ' - '.$langs->trans("LinkedToAnotherCompany");
9140
							}
9141
						}
9142
9143
						if (!empty($selected) && $selected == $obj->rowid) {
9144
							$out .= '<option value="'.$obj->rowid.'" selected';
9145
							//if ($disabled) $out.=' disabled';						// with select2, field can't be preselected if disabled
9146
							$out .= '>'.$labeltoshow.'</option>';
9147
						} else {
9148
							if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
9149
								$resultat = '';
9150
							} else {
9151
								$resultat = '<option value="'.$obj->rowid.'"';
9152
								if ($disabled) {
9153
									$resultat .= ' disabled';
9154
								}
9155
								//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
9156
								//else $labeltoshow.=' ('.$langs->trans("Private").')';
9157
								$resultat .= '>';
9158
								$resultat .= $labeltoshow;
9159
								$resultat .= '</option>';
9160
							}
9161
							$out .= $resultat;
9162
						}
9163
					}
9164
					$i++;
9165
				}
9166
			}
9167
			if (empty($option_only)) {
9168
				$out .= '</select>';
9169
			}
9170
9171
			print $out;
9172
9173
			$this->db->free($resql);
9174
			return $num;
9175
		} else {
9176
			dol_print_error($this->db);
9177
			return -1;
9178
		}
9179
	}
9180
9181
	/**
9182
	 * Output the component to make advanced search criteries
9183
	 *
9184
	 * @param	array		$arrayofcriterias			          Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...)
9185
	 * @param	array		$search_component_params	          Array of selected search criterias
9186
	 * @param   array       $arrayofinputfieldsalreadyoutput      Array of input fields already inform. The component will not generate a hidden input field if it is in this list.
9187
	 * @return	string									          HTML component for advanced search
9188
	 */
9189
	public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array())
9190
	{
9191
		global $langs;
9192
9193
		$ret = '';
9194
9195
		$ret .= '<div class="nowrap centpercent">';
9196
		//$ret .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9197
		$ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor paddingright">';
9198
		$ret .= '<span class="fas fa-filter linkobject boxfilter" title="Filter" id="idsubimgproductdistribution"></span>';
9199
		$ret .= $langs->trans("Filters");
9200
		$ret .= '</a>';
9201
		//$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9202
		$ret .= '<div name="search_component_params" class="search_component_params inline-block minwidth500 maxwidth300onsmartphone valignmiddle">';
9203
		$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
9204
9205
		$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
9206
		$ret .= '</div>';
9207
		$ret .= '<input type="hidden" name="search_component_params_hidden" class="search_component_params_hidden" value="'.GETPOST("search_component_params_hidden").'">';
9208
		// For compatibility with forms that show themself the search criteria in addition of this component, we output the fields
9209
		foreach ($arrayofcriterias as $criterias) {
9210
			foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {
9211
				if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
9212
					continue;
9213
				}
9214
				if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
9215
					continue;
9216
				}
9217
				if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
9218
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_start">';
9219
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startyear">';
9220
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startmonth">';
9221
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startday">';
9222
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_end">';
9223
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endyear">';
9224
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endmonth">';
9225
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endday">';
9226
				} else {
9227
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'">';
9228
				}
9229
			}
9230
		}
9231
		$ret .= '</div>';
9232
9233
9234
		return $ret;
9235
	}
9236
9237
	/**
9238
	 * selectModelMail
9239
	 *
9240
	 * @param   string   $prefix     	Prefix
9241
	 * @param   string   $modelType  	Model type
9242
	 * @param	int		 $default	 	1=Show also Default mail template
9243
	 * @param	int		 $addjscombo	Add js combobox
9244
	 * @return  string               	HTML select string
9245
	 */
9246
	public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
9247
	{
9248
		global $langs, $db, $user;
9249
9250
		$retstring = '';
9251
9252
		$TModels = array();
9253
9254
		include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
9255
		$formmail = new FormMail($db);
9256
		$result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
9257
9258
		if ($default) {
9259
			$TModels[0] = $langs->trans('DefaultMailModel');
9260
		}
9261
		if ($result > 0) {
9262
			foreach ($formmail->lines_model as $model) {
9263
				$TModels[$model->id] = $model->label;
9264
			}
9265
		}
9266
9267
		$retstring .= '<select class="flat" id="select_'.$prefix.'model_mail" name="'.$prefix.'model_mail">';
9268
9269
		foreach ($TModels as $id_model => $label_model) {
9270
			$retstring .= '<option value="'.$id_model.'"';
9271
			$retstring .= ">".$label_model."</option>";
9272
		}
9273
9274
		$retstring .= "</select>";
9275
9276
		if ($addjscombo) {
9277
			$retstring .= ajax_combobox('select_'.$prefix.'model_mail');
9278
		}
9279
9280
		return $retstring;
9281
	}
9282
9283
	/**
9284
	 * Output the buttons to submit a creation/edit form
9285
	 *
9286
	 * @param   string  $save_label     Alternative label for save button
9287
	 * @param   string  $cancel_label   Alternative label for cancel button
9288
	 * @param   array   $morefields     Add additional buttons between save and cancel
9289
	 * @param   bool    $withoutdiv     Option to remove enclosing centered div
9290
	 * @return 	string					Html code with the buttons
9291
	 */
9292
	public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morefields = array(), $withoutdiv = 0)
9293
	{
9294
		global $langs;
9295
9296
		$buttons = array();
9297
9298
		$save = array(
9299
			'name' => 'save',
9300
			'label_key' => $save_label,
9301
		);
9302
9303
		if ($save_label == 'Create' || $save_label == 'Add' ) {
9304
			$save['name'] = 'add';
9305
		} elseif ($save_label == 'Modify') {
9306
			$save['name'] = 'edit';
9307
		}
9308
9309
		$cancel = array(
9310
				'name' => 'cancel',
9311
				'label_key' => 'Cancel',
9312
		);
9313
9314
		!empty($save_label) ? $buttons[] = $save : '';
9315
9316
		if (!empty($morefields)) {
9317
			$buttons[] = $morefields;
9318
		}
9319
9320
		!empty($cancel_label) ? $buttons[] = $cancel : '';
9321
9322
		$retstring = $withoutdiv ? '': '<div class="center">';
9323
9324
		foreach ($buttons as $button) {
9325
			$addclass = empty($button['addclass']) ? '' : $button['addclass'];
9326
			$retstring .= '<input type="submit" class="button button-'.$button['name'].' '.$addclass.'" name="'.$button['name'].'" value="'.dol_escape_htmltag($langs->trans($button['label_key'])).'">';
9327
		}
9328
		$retstring .= $withoutdiv ? '': '</div>';
9329
9330
		return $retstring;
9331
	}
9332
}
9333