Passed
Branch develop (e2bf59)
by
unknown
29:11
created

Form::select_incoterms()   C

Complexity

Conditions 15
Paths 33

Size

Total Lines 74
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 48
nc 33
nop 7
dl 0
loc 74
rs 5.9166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

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-2022  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) && $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) && $perm) {
162
				$ret .= '</td>';
163
			}
164
			if (empty($notabletag) && $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.'&token='.newToken().'&'.$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) && $perm) {
177
				$ret .= '</td>';
178
			}
179
			if (empty($notabletag) && $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 numeric 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 in view mode (For example: 'dol_print_email')
202
	 * @param	string	$paramid		Key of parameter for id ('id', 'socid')
203
	 * @param	string	$gm				'auto' or 'tzuser' or 'tzserver' (when $typeofdata is a date)
204
	 * @return  string					HTML edit field
205
	 */
206
	public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 0, $formatfunc = '', $paramid = 'id', $gm = 'auto')
207
	{
208
		global $conf, $langs;
209
210
		$ret = '';
211
212
		// Check parameters
213
		if (empty($typeofdata)) {
214
			return 'ErrorBadParameter';
215
		}
216
217
		// When option to edit inline is activated
218
		if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
219
			$ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
220
		} else {
221
			$editmode = (GETPOST('action', 'aZ09') == 'edit'.$htmlname);
222
			if ($editmode) {
223
				$ret .= "\n";
224
				$ret .= '<form method="post" action="'.$_SERVER["PHP_SELF"].($moreparam ? '?'.$moreparam : '').'">';
225
				$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
226
				$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
227
				$ret .= '<input type="hidden" name="'.$paramid.'" value="'.$object->id.'">';
228
				if (empty($notabletag)) {
229
					$ret .= '<table class="nobordernopadding centpercent">';
230
				}
231
				if (empty($notabletag)) {
232
					$ret .= '<tr><td>';
233
				}
234
				if (preg_match('/^(string|safehtmlstring|email)/', $typeofdata)) {
235
					$tmp = explode(':', $typeofdata);
236
					$ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($editvalue ? $editvalue : $value).'"'.($tmp[1] ? ' size="'.$tmp[1].'"' : '').' autofocus>';
237
				} elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
238
					$tmp = explode(':', $typeofdata);
239
					$valuetoshow = price2num($editvalue ? $editvalue : $value);
240
					$ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($valuetoshow != '' ? price($valuetoshow) : '').'"'.($tmp[1] ? ' size="'.$tmp[1].'"' : '').' autofocus>';
241
				} elseif (preg_match('/^(checkbox)/', $typeofdata)) {
242
					$tmp = explode(':', $typeofdata);
243
					$ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
244
				} elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {	// if wysiwyg is enabled $typeofdata = 'ckeditor'
245
					$tmp = explode(':', $typeofdata);
246
					$cols = $tmp[2];
247
					$morealt = '';
248
					if (preg_match('/%/', $cols)) {
249
						$morealt = ' style="width: '.$cols.'"';
250
						$cols = '';
251
					}
252
253
					$valuetoshow = ($editvalue ? $editvalue : $value);
254
					$ret .= '<textarea id="'.$htmlname.'" name="'.$htmlname.'" wrap="soft" rows="'.($tmp[1] ? $tmp[1] : '20').'"'.($cols ? ' cols="'.$cols.'"' : 'class="quatrevingtpercent"').$morealt.'" autofocus>';
255
					// textarea convert automatically entities chars into simple chars.
256
					// 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.
257
					$valuetoshow = str_replace('&', '&amp;', $valuetoshow);
258
					$ret .= dol_string_neverthesehtmltags($valuetoshow, array('textarea'));
259
					$ret .= '</textarea>';
260
				} elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
261
					$ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0, 0, '', '', '', '', 1, '', '', $gm);
262
				} elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
263
					$ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0, 0, '', '', '', '', 1, '', '', $gm);
264
				} elseif (preg_match('/^select;/', $typeofdata)) {
265
					$arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
266
					$arraylist = array();
267
					foreach ($arraydata as $val) {
268
						$tmp = explode(':', $val);
269
						$tmpkey = str_replace('|', ':', $tmp[0]);
270
						$arraylist[$tmpkey] = $tmp[1];
271
					}
272
					$ret .= $this->selectarray($htmlname, $arraylist, $value);
273
				} elseif (preg_match('/^ckeditor/', $typeofdata)) {
274
					$tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
275
					require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
276
					$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'));
277
					$ret .= $doleditor->Create(1);
278
				}
279
				if (empty($notabletag)) {
280
					$ret .= '</td>';
281
				}
282
283
				if (empty($notabletag)) {
284
					$ret .= '<td class="left">';
285
				}
286
				//else $ret.='<div class="clearboth"></div>';
287
				$ret .= '<input type="submit" class="smallpaddingimp button'.(empty($notabletag) ? '' : ' ').'" name="modify" value="'.$langs->trans("Modify").'">';
288
				if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
289
					$ret .= '<br>'."\n";
290
				}
291
				$ret .= '<input type="submit" class="smallpaddingimp button button-cancel'.(empty($notabletag) ? '' : ' ').'" name="cancel" value="'.$langs->trans("Cancel").'">';
292
				if (empty($notabletag)) {
293
					$ret .= '</td>';
294
				}
295
296
				if (empty($notabletag)) {
297
					$ret .= '</tr></table>'."\n";
298
				}
299
				$ret .= '</form>'."\n";
300
			} else {
301
				if (preg_match('/^(email)/', $typeofdata)) {
302
					$ret .= dol_print_email($value, 0, 0, 0, 0, 1);
303
				} elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
304
					$ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : '');
305
				} elseif (preg_match('/^(checkbox)/', $typeofdata)) {
306
					$tmp = explode(':', $typeofdata);
307
					$ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
308
				} elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
309
					$ret .= dol_htmlentitiesbr($value);
310
				} elseif (preg_match('/^safehtmlstring/', $typeofdata)) {
311
					$ret .= dol_string_onlythesehtmltags($value);
312
				} elseif (preg_match('/^restricthtml/', $typeofdata)) {
313
					$ret .= dol_string_onlythesehtmltags($value);
314
				} elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
315
					$ret .= '<span class="valuedate">'.dol_print_date($value, 'day', $gm).'</span>';
316
				} elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
317
					$ret .= '<span class="valuedate">'.dol_print_date($value, 'dayhour', $gm).'</span>';
318
				} elseif (preg_match('/^select;/', $typeofdata)) {
319
					$arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
320
					$arraylist = array();
321
					foreach ($arraydata as $val) {
322
						$tmp = explode(':', $val);
323
						$arraylist[$tmp[0]] = $tmp[1];
324
					}
325
					$ret .= $arraylist[$value];
326
					if ($htmlname == 'fk_product_type') {
327
						if ($value == 0) {
328
							$ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
329
						} else {
330
							$ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
331
						}
332
					}
333
				} elseif (preg_match('/^ckeditor/', $typeofdata)) {
334
					$tmpcontent = dol_htmlentitiesbr($value);
335
					if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) {
336
						$firstline = preg_replace('/<br>.*/', '', $tmpcontent);
337
						$firstline = preg_replace('/[\n\r].*/', '', $firstline);
338
						$tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
339
					}
340
					// We dont use dol_escape_htmltag to get the html formating active, but this need we must also
341
					// clean data from some dangerous html
342
					$ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
343
				} else {
344
					$ret .= dol_escape_htmltag($value);
345
				}
346
347
				if ($formatfunc && method_exists($object, $formatfunc)) {
348
					$ret = $object->$formatfunc($ret);
349
				}
350
			}
351
		}
352
		return $ret;
353
	}
354
355
	/**
356
	 * Output edit in place form
357
	 *
358
	 * @param   string	$fieldname		Name of the field
359
	 * @param	object	$object			Object
360
	 * @param	boolean	$perm			Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
361
	 * @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]'...)
362
	 * @param	string	$check			Same coe than $check parameter of GETPOST()
363
	 * @param	string	$morecss		More CSS
364
	 * @return	string   		      	HTML code for the edit of alternative language
365
	 */
366
	public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
367
	{
368
		global $conf, $langs, $extralanguages;
369
370
		$result = '';
371
372
		// List of extra languages
373
		$arrayoflangcode = array();
374
		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
375
			$arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
376
		}
377
378
		if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
379
			if (!is_object($extralanguages)) {
380
				include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
381
				$extralanguages = new ExtraLanguages($this->db);
382
			}
383
			$extralanguages->fetch_name_extralanguages('societe');
384
385
			if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
386
				return ''; // No extralang field to show
387
			}
388
389
			$result .= '<!-- Widget for translation -->'."\n";
390
			$result .= '<div class="inline-block paddingleft image-'.$object->element.'-'.$fieldname.'">';
391
			$s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
392
			$result .= $s;
393
			$result .= '</div>';
394
395
			$result .= '<div class="inline-block hidden field-'.$object->element.'-'.$fieldname.'">';
396
397
			$resultforextrlang = '';
398
			foreach ($arrayoflangcode as $langcode) {
399
				$valuetoshow = GETPOSTISSET('field-'.$object->element."-".$fieldname."-".$langcode) ? GETPOST('field-'.$object->element.'-'.$fieldname."-".$langcode, $check) : '';
400
				if (empty($valuetoshow)) {
401
					$object->fetchValuesForExtraLanguages();
402
					//var_dump($object->array_languages);
403
					$valuetoshow = $object->array_languages[$fieldname][$langcode];
404
				}
405
406
				$s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
407
				$resultforextrlang .= $s;
408
409
				// TODO Use the showInputField() method of ExtraLanguages object
410
				if ($typeofdata == 'textarea') {
411
					$resultforextrlang .= '<textarea name="field-'.$object->element."-".$fieldname."-".$langcode.'" id="'.$fieldname."-".$langcode.'" class="'.$morecss.'" rows="'.ROWS_2.'" wrap="soft">';
412
					$resultforextrlang .= $valuetoshow;
413
					$resultforextrlang .= '</textarea>';
414
				} else {
415
					$resultforextrlang .= '<input type="text" class="inputfieldforlang '.($morecss ? ' '.$morecss : '').'" name="field-'.$object->element.'-'.$fieldname.'-'.$langcode.'" value="'.$valuetoshow.'">';
416
				}
417
			}
418
			$result .= $resultforextrlang;
419
420
			$result .= '</div>';
421
			$result .= '<script>$(".image-'.$object->element.'-'.$fieldname.'").click(function() { console.log("Toggle lang widget"); jQuery(".field-'.$object->element.'-'.$fieldname.'").toggle(); });</script>';
422
		}
423
424
		return $result;
425
	}
426
427
	/**
428
	 * Output edit in place form
429
	 *
430
	 * @param	object	$object			Object
431
	 * @param	string	$value			Value to show/edit
432
	 * @param	string	$htmlname		DIV ID (field name)
433
	 * @param	int		$condition		Condition to edit
434
	 * @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')
435
	 * @param	string	$editvalue		When in edit mode, use this value as $value instead of value
436
	 * @param	object	$extObject		External object
437
	 * @param	mixed	$custommsg		String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
438
	 * @return	string   		      	HTML edit in place
439
	 */
440
	protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
441
	{
442
		global $conf;
443
444
		$out = '';
445
446
		// Check parameters
447
		if (preg_match('/^text/', $inputType)) {
448
			$value = dol_nl2br($value);
449
		} elseif (preg_match('/^numeric/', $inputType)) {
450
			$value = price($value);
451
		} elseif ($inputType == 'day' || $inputType == 'datepicker') {
452
			$value = dol_print_date($value, 'day');
453
		}
454
455
		if ($condition) {
456
			$element = false;
457
			$table_element = false;
458
			$fk_element		= false;
459
			$loadmethod		= false;
460
			$savemethod		= false;
461
			$ext_element	= false;
462
			$button_only	= false;
463
			$inputOption = '';
464
465
			if (is_object($object)) {
466
				$element = $object->element;
467
				$table_element = $object->table_element;
468
				$fk_element = $object->id;
469
			}
470
471
			if (is_object($extObject)) {
472
				$ext_element = $extObject->element;
473
			}
474
475
			if (preg_match('/^(string|email|numeric)/', $inputType)) {
476
				$tmp = explode(':', $inputType);
477
				$inputType = $tmp[0];
478
				if (!empty($tmp[1])) {
479
					$inputOption = $tmp[1];
480
				}
481
				if (!empty($tmp[2])) {
482
					$savemethod = $tmp[2];
483
				}
484
				$out .= '<input id="width_'.$htmlname.'" value="'.$inputOption.'" type="hidden"/>'."\n";
485
			} elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
486
				$tmp = explode(':', $inputType);
487
				$inputType = $tmp[0];
488
				if (!empty($tmp[1])) {
489
					$inputOption = $tmp[1];
490
				}
491
				if (!empty($tmp[2])) {
492
					$savemethod = $tmp[2];
493
				}
494
495
				$out .= '<input id="timestamp" type="hidden"/>'."\n"; // Use for timestamp format
496
			} elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
497
				$tmp = explode(':', $inputType);
498
				$inputType = $tmp[0];
499
				$loadmethod = $tmp[1];
500
				if (!empty($tmp[2])) {
501
					$savemethod = $tmp[2];
502
				}
503
				if (!empty($tmp[3])) {
504
					$button_only = true;
505
				}
506
			} elseif (preg_match('/^textarea/', $inputType)) {
507
				$tmp = explode(':', $inputType);
508
				$inputType = $tmp[0];
509
				$rows = (empty($tmp[1]) ? '8' : $tmp[1]);
510
				$cols = (empty($tmp[2]) ? '80' : $tmp[2]);
511
			} elseif (preg_match('/^ckeditor/', $inputType)) {
512
				$tmp = explode(':', $inputType);
513
				$inputType = $tmp[0];
514
				$toolbar = $tmp[1];
515
				if (!empty($tmp[2])) {
516
					$width = $tmp[2];
517
				}
518
				if (!empty($tmp[3])) {
519
					$heigth = $tmp[3];
520
				}
521
				if (!empty($tmp[4])) {
522
					$savemethod = $tmp[4];
523
				}
524
525
				if (!empty($conf->fckeditor->enabled)) {
526
					$out .= '<input id="ckeditor_toolbar" value="'.$toolbar.'" type="hidden"/>'."\n";
527
				} else {
528
					$inputType = 'textarea';
529
				}
530
			}
531
532
			$out .= '<input id="element_'.$htmlname.'" value="'.$element.'" type="hidden"/>'."\n";
533
			$out .= '<input id="table_element_'.$htmlname.'" value="'.$table_element.'" type="hidden"/>'."\n";
534
			$out .= '<input id="fk_element_'.$htmlname.'" value="'.$fk_element.'" type="hidden"/>'."\n";
535
			$out .= '<input id="loadmethod_'.$htmlname.'" value="'.$loadmethod.'" type="hidden"/>'."\n";
536
			if (!empty($savemethod)) {
537
				$out .= '<input id="savemethod_'.$htmlname.'" value="'.$savemethod.'" type="hidden"/>'."\n";
538
			}
539
			if (!empty($ext_element)) {
540
				$out .= '<input id="ext_element_'.$htmlname.'" value="'.$ext_element.'" type="hidden"/>'."\n";
541
			}
542
			if (!empty($custommsg)) {
543
				if (is_array($custommsg)) {
544
					if (!empty($custommsg['success'])) {
545
						$out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg['success'].'" type="hidden"/>'."\n";
546
					}
547
					if (!empty($custommsg['error'])) {
548
						$out .= '<input id="errormsg_'.$htmlname.'" value="'.$custommsg['error'].'" type="hidden"/>'."\n";
549
					}
550
				} else {
551
					$out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg.'" type="hidden"/>'."\n";
552
				}
553
			}
554
			if ($inputType == 'textarea') {
555
				$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...
556
				$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...
557
			}
558
			$out .= '<span id="viewval_'.$htmlname.'" class="viewval_'.$inputType.($button_only ? ' inactive' : ' active').'">'.$value.'</span>'."\n";
559
			$out .= '<span id="editval_'.$htmlname.'" class="editval_'.$inputType.($button_only ? ' inactive' : ' active').' hideobject">'.(!empty($editvalue) ? $editvalue : $value).'</span>'."\n";
560
		} else {
561
			$out = $value;
562
		}
563
564
		return $out;
565
	}
566
567
	/**
568
	 *	Show a text and picto with tooltip on text or picto.
569
	 *  Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip
570
	 *
571
	 *	@param	string		$text				Text to show
572
	 *	@param	string		$htmltext			HTML content of tooltip. Must be HTML/UTF8 encoded.
573
	 *	@param	int			$tooltipon			1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2
574
	 *	@param	int			$direction			-1=image is before, 0=no image, 1=image is after
575
	 *	@param	string		$img				Html code for image (use img_xxx() function to get it)
576
	 *	@param	string		$extracss			Add a CSS style to td tags
577
	 *	@param	int			$notabs				0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
578
	 *	@param	string		$incbefore			Include code before the text
579
	 *	@param	int			$noencodehtmltext	Do not encode into html entity the htmltext
580
	 *  @param  string      $tooltiptrigger		''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key)
581
	 *  @param	int			$forcenowrap		Force no wrap between text and picto (works with notabs=2 only)
582
	 *	@return	string							Code html du tooltip (texte+picto)
583
	 *	@see	textwithpicto() Use thisfunction if you can.
584
	 */
585
	public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
586
	{
587
		if ($incbefore) {
588
			$text = $incbefore.$text;
589
		}
590
		if (!$htmltext) {
591
			return $text;
592
		}
593
		$direction = (int) $direction;	// For backward compatibility when $direction was set to '' instead of 0
594
595
		$tag = 'td';
596
		if ($notabs == 2) {
597
			$tag = 'div';
598
		}
599
		if ($notabs == 3) {
600
			$tag = 'span';
601
		}
602
		// Sanitize tooltip
603
		$htmltext = str_replace(array("\r", "\n"), '', $htmltext);
604
605
		$extrastyle = '';
606
		if ($direction < 0) {
607
			$extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
608
			$extrastyle = 'padding: 0px; padding-left: 3px !important;';
609
		}
610
		if ($direction > 0) {
611
			$extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
612
			$extrastyle = 'padding: 0px; padding-right: 3px !important;';
613
		}
614
615
		$classfortooltip = 'classfortooltip';
616
617
		$s = '';
618
		$textfordialog = '';
619
620
		if ($tooltiptrigger == '') {
621
			$htmltext = str_replace('"', '&quot;', $htmltext);
622
		} else {
623
			$classfortooltip = 'classfortooltiponclick';
624
			$textfordialog .= '<div style="display: none;" id="idfortooltiponclick_'.$tooltiptrigger.'" class="classfortooltiponclicktext">'.$htmltext.'</div>';
625
		}
626
		if ($tooltipon == 2 || $tooltipon == 3) {
627
			$paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"';
628
			if ($tooltiptrigger == '') {
629
				$paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip
630
			} else {
631
				$paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"';
632
			}
633
		} else {
634
			$paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
635
		}
636
		if ($tooltipon == 1 || $tooltipon == 3) {
637
			$paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" ';
638
			if ($tooltiptrigger == '') {
639
				$paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip
640
			} else {
641
				$paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"';
642
			}
643
		} else {
644
			$paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
645
		}
646
		if (empty($notabs)) {
647
			$s .= '<table class="nobordernopadding"><tr style="height: auto;">';
648
		} elseif ($notabs == 2) {
649
			$s .= '<div class="inline-block'.($forcenowrap ? ' nowrap' : '').'">';
650
		}
651
		// Define value if value is before
652
		if ($direction < 0) {
653
			$s .= '<'.$tag.$paramfortooltipimg;
654
			if ($tag == 'td') {
655
				$s .= ' class=valigntop" width="14"';
656
			}
657
			$s .= '>'.$textfordialog.$img.'</'.$tag.'>';
658
		}
659
		// Use another method to help avoid having a space in value in order to use this value with jquery
660
		// Define label
661
		if ((string) $text != '') {
662
			$s .= '<'.$tag.$paramfortooltiptd.'>'.$text.'</'.$tag.'>';
663
		}
664
		// Define value if value is after
665
		if ($direction > 0) {
666
			$s .= '<'.$tag.$paramfortooltipimg;
667
			if ($tag == 'td') {
668
				$s .= ' class="valignmiddle" width="14"';
669
			}
670
			$s .= '>'.$textfordialog.$img.'</'.$tag.'>';
671
		}
672
		if (empty($notabs)) {
673
			$s .= '</tr></table>';
674
		} elseif ($notabs == 2) {
675
			$s .= '</div>';
676
		}
677
678
		return $s;
679
	}
680
681
	/**
682
	 *	Show a text with a picto and a tooltip on picto
683
	 *
684
	 *	@param	string	$text				Text to show
685
	 *	@param  string	$htmltext	     	Content of tooltip
686
	 *	@param	int		$direction			1=Icon is after text, -1=Icon is before text, 0=no icon
687
	 * 	@param	string	$type				Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none'
688
	 *  @param  string	$extracss           Add a CSS style to td, div or span tag
689
	 *  @param  int		$noencodehtmltext   Do not encode into html entity the htmltext
690
	 *  @param	int		$notabs				0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
691
	 *  @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')
692
	 *  @param	int		$forcenowrap		Force no wrap between text and picto (works with notabs=2 only)
693
	 * 	@return	string						HTML code of text, picto, tooltip
694
	 */
695
	public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
696
	{
697
		global $conf, $langs;
698
699
		$alt = '';
700
		if ($tooltiptrigger) {
701
			$alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
702
		}
703
704
		//For backwards compatibility
705
		if ($type == '0') {
706
			$type = 'info';
707
		} elseif ($type == '1') {
708
			$type = 'help';
709
		}
710
711
		// If info or help with no javascript, show only text
712
		if (empty($conf->use_javascript_ajax)) {
713
			if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
714
				return $text;
715
			} else {
716
				$alt = $htmltext;
717
				$htmltext = '';
718
			}
719
		}
720
721
		// If info or help with smartphone, show only text (tooltip hover can't works)
722
		if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
723
			if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
724
				return $text;
725
			}
726
		}
727
		// If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
728
		//if (! empty($conf->dol_no_mouse_hover) && ! empty($tooltiptrigger))
729
		//{
730
		//if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.''</a>';
731
		//}
732
733
		$img = '';
734
		if ($type == 'info') {
735
			$img = img_help(0, $alt);
736
		} elseif ($type == 'help') {
737
			$img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
738
		} elseif ($type == 'helpclickable') {
739
			$img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
740
		} elseif ($type == 'superadmin') {
741
			$img = img_picto($alt, 'redstar');
742
		} elseif ($type == 'admin') {
743
			$img = img_picto($alt, 'star');
744
		} elseif ($type == 'warning') {
745
			$img = img_warning($alt);
746
		} elseif ($type != 'none') {
747
			$img = img_picto($alt, $type); // $type can be an image path
748
		}
749
750
		return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
751
	}
752
753
	/**
754
	 * Generate select HTML to choose massaction
755
	 *
756
	 * @param	string	$selected		Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default.
757
	 * @param	array	$arrayofaction	array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action.
758
	 * @param   int     $alwaysvisible  1=select button always visible
759
	 * @param   string  $name     		Name for massaction
760
	 * @param   string  $cssclass 		CSS class used to check for select
761
	 * @return	string|void				Select list
762
	 */
763
	public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
764
	{
765
		global $conf, $langs, $hookmanager;
766
767
768
		$disabled = 0;
769
		$ret = '<div class="centpercent center">';
770
		$ret .= '<select class="flat'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'select valignmiddle alignstart" id="'.$name.'" name="'.$name.'"'.($disabled ? ' disabled="disabled"' : '').'>';
771
772
		// 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.
773
		$parameters = array();
774
		$reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
775
		// check if there is a mass action
776
		if (count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
777
			return;
778
		}
779
		if (empty($reshook)) {
780
			$ret .= '<option value="0"'.($disabled ? ' disabled="disabled"' : '').'>-- '.$langs->trans("SelectAction").' --</option>';
781
			foreach ($arrayofaction as $code => $label) {
782
				$ret .= '<option value="'.$code.'"'.($disabled ? ' disabled="disabled"' : '').' data-html="'.dol_escape_htmltag($label).'">'.$label.'</option>';
783
			}
784
		}
785
		$ret .= $hookmanager->resPrint;
786
787
		$ret .= '</select>';
788
789
		if (empty($conf->dol_optimize_smallscreen)) {
790
			$ret .= ajax_combobox('.'.$name.'select');
791
		}
792
793
		// 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
794
		$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.
795
		$ret .= '<input type="submit" disabled name="confirmmassaction"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display: none"').' class="button smallpaddingimp'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'confirmed" value="'.dol_escape_htmltag($langs->trans("Confirm")).'">';
796
		$ret .= '</div>';
797
798
		if (!empty($conf->use_javascript_ajax)) {
799
			$ret .= '<!-- JS CODE TO ENABLE mass action select -->
800
    		<script>
801
                        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 */
802
        		{
803
        			atleastoneselected=0;
804
                                jQuery("."+cssclass).each(function( index ) {
805
    	  				/* console.log( index + ": " + $( this ).text() ); */
806
    	  				if ($(this).is(\':checked\')) atleastoneselected++;
807
    	  			});
808
809
					console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
810
811
    	  			if (atleastoneselected || '.$alwaysvisible.')
812
    	  			{
813
                                    jQuery("."+name).show();
814
        			    '.($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("'.$selected.'").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '').'
815
        			    '.($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '').'
816
    	  			}
817
    	  			else
818
    	  			{
819
                                    jQuery("."+name).hide();
820
                                    jQuery("."+name+"other").hide();
821
    	            }
822
        		}
823
824
        	jQuery(document).ready(function () {
825
                    initCheckForSelect(0, "' . $name.'", "'.$cssclass.'");
826
                    jQuery(".' . $cssclass.'").click(function() {
827
                        initCheckForSelect(1, "'.$name.'", "'.$cssclass.'");
828
                    });
829
                        jQuery(".' . $name.'select").change(function() {
830
        			var massaction = $( this ).val();
831
        			var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
832
        			if (massaction == "builddoc")
833
                    {
834
                        urlform = urlform + "#show_files";
835
    	            }
836
        			$( this ).closest("form").attr("action", urlform);
837
                    console.log("we select a mass action name='.$name.' massaction="+massaction+" - "+urlform);
838
        	        /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
839
        			if ($(this).val() != \'0\')
840
    	  			{
841
                                        jQuery(".' . $name.'confirmed").prop(\'disabled\', false);
842
										jQuery(".' . $name.'other").hide();	/* To disable if another div was open */
843
                                        jQuery(".' . $name.'"+massaction).show();
844
    	  			}
845
    	  			else
846
    	  			{
847
                                        jQuery(".' . $name.'confirmed").prop(\'disabled\', true);
848
										jQuery(".' . $name.'other").hide();	/* To disable any div open */
849
    	  			}
850
    	        });
851
        	});
852
    		</script>
853
        	';
854
		}
855
856
		return $ret;
857
	}
858
859
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
860
	/**
861
	 *  Return combo list of activated countries, into language of user
862
	 *
863
	 *  @param	string	$selected       		Id or Code or Label of preselected country
864
	 *  @param  string	$htmlname       		Name of html select object
865
	 *  @param  string	$htmloption     		More html options on select object
866
	 *  @param	integer	$maxlength				Max length for labels (0=no limit)
867
	 *  @param	string	$morecss				More css class
868
	 *  @param	string	$usecodeaskey			''=Use id as key (default), 'code3'=Use code on 3 alpha as key, 'code2"=Use code on 2 alpha as key
869
	 *  @param	int		$showempty				Show empty choice
870
	 *  @param	int		$disablefavorites		1=Disable favorites,
871
	 *  @param	int		$addspecialentries		1=Add dedicated entries for group of countries (like 'European Economic Community', ...)
872
	 *  @param	array	$exclude_country_code	Array of country code (iso2) to exclude
873
	 *  @param	int		$hideflags				Hide flags
874
	 *  @return string           				HTML string with select
875
	 */
876
	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)
877
	{
878
		// phpcs:enable
879
		global $conf, $langs, $mysoc;
880
881
		$langs->load("dict");
882
883
		$out = '';
884
		$countryArray = array();
885
		$favorite = array();
886
		$label = array();
887
		$atleastonefavorite = 0;
888
889
		$sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
890
		$sql .= " FROM ".$this->db->prefix()."c_country";
891
		$sql .= " WHERE active > 0";
892
		//$sql.= " ORDER BY code ASC";
893
894
		dol_syslog(get_class($this)."::select_country", LOG_DEBUG);
895
		$resql = $this->db->query($sql);
896
		if ($resql) {
897
			$out .= '<select id="select'.$htmlname.'" class="flat maxwidth200onsmartphone selectcountry'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" '.$htmloption.'>';
898
			$num = $this->db->num_rows($resql);
899
			$i = 0;
900
			if ($num) {
901
				while ($i < $num) {
902
					$obj = $this->db->fetch_object($resql);
903
904
					$countryArray[$i]['rowid'] = $obj->rowid;
905
					$countryArray[$i]['code_iso'] = $obj->code_iso;
906
					$countryArray[$i]['code_iso3'] 	= $obj->code_iso3;
907
					$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 : ''));
908
					$countryArray[$i]['favorite'] = $obj->favorite;
909
					$countryArray[$i]['eec'] = $obj->eec;
910
					$favorite[$i] = $obj->favorite;
911
					$label[$i] = dol_string_unaccent($countryArray[$i]['label']);
912
					$i++;
913
				}
914
915
				if (empty($disablefavorites)) {
916
					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

916
					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

916
					array_multisort($favorite, SORT_DESC, $label, /** @scrutinizer ignore-type */ SORT_ASC, $countryArray);
Loading history...
917
				} else {
918
					$countryArray = dol_sort_array($countryArray, 'label');
919
				}
920
921
				if ($showempty) {
922
					$out .= '<option value="">&nbsp;</option>'."\n";
923
				}
924
925
				if ($addspecialentries) {	// Add dedicated entries for groups of countries
926
					//if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
927
					$out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
928
					$out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
929
					if ($mysoc->isInEEC()) {
930
						$out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
931
					}
932
					$out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
933
					$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
934
				}
935
936
				foreach ($countryArray as $row) {
937
					//if (empty($showempty) && empty($row['rowid'])) continue;
938
					if (empty($row['rowid'])) {
939
						continue;
940
					}
941
					if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
942
						continue; // exclude some countries
943
					}
944
945
					if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
946
						$atleastonefavorite++;
947
					}
948
					if (empty($row['favorite']) && $atleastonefavorite) {
949
						$atleastonefavorite = 0;
950
						$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
951
					}
952
953
					$labeltoshow = '';
954
					if ($row['label']) {
955
						$labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
956
					} else {
957
						$labeltoshow .= '&nbsp;';
958
					}
959
					if ($row['code_iso']) {
960
						$labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
961
						if (empty($hideflags)) {
962
							$tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
963
							$labeltoshow = $tmpflag.' '.$labeltoshow;
964
						}
965
					}
966
967
					if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
968
						$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']).'">';
969
					} else {
970
						$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']).'">';
971
					}
972
					$out .= $labeltoshow;
973
					$out .= '</option>'."\n";
974
				}
975
			}
976
			$out .= '</select>';
977
		} else {
978
			dol_print_error($this->db);
979
		}
980
981
		// Make select dynamic
982
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
983
		$out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
984
985
		return $out;
986
	}
987
988
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
989
	/**
990
	 *  Return select list of incoterms
991
	 *
992
	 *  @param	string	$selected       		Id or Code of preselected incoterm
993
	 *  @param	string	$location_incoterms     Value of input location
994
	 *  @param	string	$page       			Defined the form action
995
	 *  @param  string	$htmlname       		Name of html select object
996
	 *  @param  string	$htmloption     		Options html on select object
997
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
998
	 *  @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')))
999
	 *  @return string           				HTML string with select and input
1000
	 */
1001
	public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array())
1002
	{
1003
		// phpcs:enable
1004
		global $conf, $langs;
1005
1006
		$langs->load("dict");
1007
1008
		$out = '';
1009
		$moreattrib = '';
1010
		$incotermArray = array();
1011
1012
		$sql = "SELECT rowid, code";
1013
		$sql .= " FROM ".$this->db->prefix()."c_incoterms";
1014
		$sql .= " WHERE active > 0";
1015
		$sql .= " ORDER BY code ASC";
1016
1017
		dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
1018
		$resql = $this->db->query($sql);
1019
		if ($resql) {
1020
			if ($conf->use_javascript_ajax && !$forcecombo) {
1021
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1022
				$out .= ajax_combobox($htmlname, $events);
1023
			}
1024
1025
			if (!empty($page)) {
1026
				$out .= '<form method="post" action="'.$page.'">';
1027
				$out .= '<input type="hidden" name="action" value="set_incoterms">';
1028
				$out .= '<input type="hidden" name="token" value="'.newToken().'">';
1029
			}
1030
1031
			$out .= '<select id="'.$htmlname.'" class="flat selectincoterm width75" name="'.$htmlname.'" '.$htmloption.'>';
1032
			$out .= '<option value="0">&nbsp;</option>';
1033
			$num = $this->db->num_rows($resql);
1034
			$i = 0;
1035
			if ($num) {
1036
				$foundselected = false;
1037
1038
				while ($i < $num) {
1039
					$obj = $this->db->fetch_object($resql);
1040
					$incotermArray[$i]['rowid'] = $obj->rowid;
1041
					$incotermArray[$i]['code'] = $obj->code;
1042
					$i++;
1043
				}
1044
1045
				foreach ($incotermArray as $row) {
1046
					if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1047
						$out .= '<option value="'.$row['rowid'].'" selected>';
1048
					} else {
1049
						$out .= '<option value="'.$row['rowid'].'">';
1050
					}
1051
1052
					if ($row['code']) {
1053
						$out .= $row['code'];
1054
					}
1055
1056
					$out .= '</option>';
1057
				}
1058
			}
1059
			$out .= '</select>';
1060
1061
			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...
1062
				$out .= ajax_multiautocompleter('location_incoterms', '', DOL_URL_ROOT.'/core/ajax/locationincoterms.php')."\n";
1063
				$moreattrib .= ' autocomplete="off"';
1064
			}
1065
			$out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="'.$location_incoterms.'">'."\n";
1066
1067
			if (!empty($page)) {
1068
				$out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1069
			}
1070
		} else {
1071
			dol_print_error($this->db);
1072
		}
1073
1074
		return $out;
1075
	}
1076
1077
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1078
	/**
1079
	 *	Return list of types of lines (product or service)
1080
	 * 	Example: 0=product, 1=service, 9=other (for external module)
1081
	 *
1082
	 *	@param  string	$selected       Preselected type
1083
	 *	@param  string	$htmlname       Name of field in html form
1084
	 * 	@param	int		$showempty		Add an empty field
1085
	 * 	@param	int		$hidetext		Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
1086
	 * 	@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')
1087
	 *  @return	void
1088
	 */
1089
	public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1090
	{
1091
		// phpcs:enable
1092
		global $langs, $conf;
1093
1094
		// If product & services are enabled or both disabled.
1095
		if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled))
1096
			|| (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) {
1097
			if (empty($hidetext)) {
1098
				print $langs->trans("Type").': ';
1099
			}
1100
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1101
			if ($showempty) {
1102
				print '<option value="-1"';
1103
				if ($selected == -1) {
1104
					print ' selected';
1105
				}
1106
				print '>&nbsp;</option>';
1107
			}
1108
1109
			print '<option value="0"';
1110
			if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1111
				print ' selected';
1112
			}
1113
			print '>'.$langs->trans("Product");
1114
1115
			print '<option value="1"';
1116
			if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1117
				print ' selected';
1118
			}
1119
			print '>'.$langs->trans("Service");
1120
1121
			print '</select>';
1122
			print ajax_combobox('select_'.$htmlname);
1123
			//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1124
		}
1125
		if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) {
1126
			print $langs->trans("Service");
1127
			print '<input type="hidden" name="'.$htmlname.'" value="1">';
1128
		}
1129
		if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) {
1130
			print $langs->trans("Product");
1131
			print '<input type="hidden" name="'.$htmlname.'" value="0">';
1132
		}
1133
		if ($forceall < 0) {	// This should happened only for contracts when both predefined product and service are disabled.
1134
			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
1135
		}
1136
	}
1137
1138
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1139
	/**
1140
	 *	Load into cache cache_types_fees, array of types of fees
1141
	 *
1142
	 *	@return     int             Nb of lines loaded, <0 if KO
1143
	 */
1144
	public function load_cache_types_fees()
1145
	{
1146
		// phpcs:enable
1147
		global $langs;
1148
1149
		$num = count($this->cache_types_fees);
1150
		if ($num > 0) {
1151
			return 0; // Cache already loaded
1152
		}
1153
1154
		dol_syslog(__METHOD__, LOG_DEBUG);
1155
1156
		$langs->load("trips");
1157
1158
		$sql = "SELECT c.code, c.label";
1159
		$sql .= " FROM ".$this->db->prefix()."c_type_fees as c";
1160
		$sql .= " WHERE active > 0";
1161
1162
		$resql = $this->db->query($sql);
1163
		if ($resql) {
1164
			$num = $this->db->num_rows($resql);
1165
			$i = 0;
1166
1167
			while ($i < $num) {
1168
				$obj = $this->db->fetch_object($resql);
1169
1170
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1171
				$label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1172
				$this->cache_types_fees[$obj->code] = $label;
1173
				$i++;
1174
			}
1175
1176
			asort($this->cache_types_fees);
1177
1178
			return $num;
1179
		} else {
1180
			dol_print_error($this->db);
1181
			return -1;
1182
		}
1183
	}
1184
1185
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1186
	/**
1187
	 *	Return list of types of notes
1188
	 *
1189
	 *	@param	string		$selected		Preselected type
1190
	 *	@param  string		$htmlname		Name of field in form
1191
	 * 	@param	int			$showempty		Add an empty field
1192
	 * 	@return	void
1193
	 */
1194
	public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1195
	{
1196
		// phpcs:enable
1197
		global $user, $langs;
1198
1199
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1200
1201
		$this->load_cache_types_fees();
1202
1203
		print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1204
		if ($showempty) {
1205
			print '<option value="-1"';
1206
			if ($selected == -1) {
1207
				print ' selected';
1208
			}
1209
			print '>&nbsp;</option>';
1210
		}
1211
1212
		foreach ($this->cache_types_fees as $key => $value) {
1213
			print '<option value="'.$key.'"';
1214
			if ($key == $selected) {
1215
				print ' selected';
1216
			}
1217
			print '>';
1218
			print $value;
1219
			print '</option>';
1220
		}
1221
1222
		print '</select>';
1223
		if ($user->admin) {
1224
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1225
		}
1226
	}
1227
1228
1229
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1230
	/**
1231
	 *  Output html form to select a third party
1232
	 *
1233
	 *	@param	string	$selected       		Preselected type
1234
	 *	@param  string	$htmlname       		Name of field in form
1235
	 *  @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)')
1236
	 *	@param	string	$showempty				Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
1237
	 * 	@param	int		$showtype				Show third party type in combolist (customer, prospect or supplier)
1238
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
1239
	 *  @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')))
1240
	 *	@param	int		$limit					Maximum number of elements
1241
	 *  @param	string	$morecss				Add more css styles to the SELECT component
1242
	 *	@param  string	$moreparam      		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1243
	 *	@param	string	$selected_input_value	Value of preselected input text (for use with ajax)
1244
	 *  @param	int		$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1245
	 *  @param	array	$ajaxoptions			Options for ajax_autocompleter
1246
	 * 	@param  bool	$multiple				add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
1247
	 *  @param	array	$excludeids				Exclude IDs from the select combo
1248
	 * 	@return	string							HTML string with select box for thirdparty.
1249
	 */
1250
	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())
1251
	{
1252
		// phpcs:enable
1253
		global $conf, $user, $langs;
1254
1255
		$out = '';
1256
1257
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1258
			if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
1259
				$ajaxoptions = array();
1260
			}
1261
1262
			require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1263
1264
			// No immediate load of all database
1265
			$placeholder = '';
1266
			if ($selected && empty($selected_input_value)) {
1267
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1268
				$societetmp = new Societe($this->db);
1269
				$societetmp->fetch($selected);
1270
				$selected_input_value = $societetmp->name;
1271
				unset($societetmp);
1272
			}
1273
1274
			// mode 1
1275
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '');
1276
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1277
1278
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1279
			if (empty($hidelabel)) {
1280
				print $langs->trans("RefOrLabel").' : ';
1281
			} elseif ($hidelabel > 1) {
1282
				$placeholder = $langs->trans("RefOrLabel");
1283
				if ($hidelabel == 2) {
1284
					$out .= img_picto($langs->trans("Search"), 'search');
1285
				}
1286
			}
1287
			$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' : '').' />';
1288
			if ($hidelabel == 3) {
1289
				$out .= img_picto($langs->trans("Search"), 'search');
1290
			}
1291
		} else {
1292
			// Immediate load of all database
1293
			$out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids);
1294
		}
1295
1296
		return $out;
1297
	}
1298
1299
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1300
	/**
1301
	 *  Output html form to select a third party.
1302
	 *  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.
1303
	 *
1304
	 *	@param	string	$selected       Preselected type
1305
	 *	@param  string	$htmlname       Name of field in form
1306
	 *  @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.
1307
	 *	@param	string	$showempty		Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
1308
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
1309
	 * 	@param	int		$forcecombo		Force to use standard HTML select component without beautification
1310
	 *  @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')))
1311
	 *  @param	string	$filterkey		Filter on key value
1312
	 *  @param	int		$outputmode		0=HTML select string, 1=Array
1313
	 *  @param	int		$limit			Limit number of answers
1314
	 *  @param	string	$morecss		Add more css styles to the SELECT component
1315
	 *	@param  string	$moreparam      Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1316
	 *	@param  bool	$multiple       add [] in the name of element and add 'multiple' attribut
1317
	 *  @param	array	$excludeids		Exclude IDs from the select combo
1318
	 * 	@return	string					HTML string with
1319
	 */
1320
	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())
1321
	{
1322
		// phpcs:enable
1323
		global $conf, $user, $langs;
1324
		global $hookmanager;
1325
1326
		$out = '';
1327
		$num = 0;
1328
		$outarray = array();
1329
1330
		if ($selected === '') {
1331
			$selected = array();
1332
		} elseif (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
1333
			$selected = array($selected);
1334
		}
1335
1336
		// Clean $filter that may contains sql conditions so sql code
1337
		if (function_exists('testSqlAndScriptInject')) {
1338
			if (testSqlAndScriptInject($filter, 3) > 0) {
1339
				$filter = '';
1340
			}
1341
		}
1342
1343
		// We search companies
1344
		$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1345
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1346
			$sql .= ", s.address, s.zip, s.town";
1347
			$sql .= ", dictp.code as country_code";
1348
		}
1349
		$sql .= " FROM ".$this->db->prefix()."societe as s";
1350
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1351
			$sql .= " LEFT JOIN ".$this->db->prefix()."c_country as dictp ON dictp.rowid = s.fk_pays";
1352
		}
1353
		if (empty($user->rights->societe->client->voir) && !$user->socid) {
1354
			$sql .= ", ".$this->db->prefix()."societe_commerciaux as sc";
1355
		}
1356
		$sql .= " WHERE s.entity IN (".getEntity('societe').")";
1357
		if (!empty($user->socid)) {
1358
			$sql .= " AND s.rowid = ".((int) $user->socid);
1359
		}
1360
		if ($filter) {
1361
			$sql .= " AND (".$filter.")";
1362
		}
1363
		if (empty($user->rights->societe->client->voir) && !$user->socid) {
1364
			$sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1365
		}
1366
		if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1367
			$sql .= " AND s.status <> 0";
1368
		}
1369
		if (!empty($excludeids)) {
1370
			$sql .= " AND s.rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1371
		}
1372
		// Add where from hooks
1373
		$parameters = array();
1374
		$reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1375
		$sql .= $hookmanager->resPrint;
1376
		// Add criteria
1377
		if ($filterkey && $filterkey != '') {
1378
			$sql .= " AND (";
1379
			$prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1380
			// For natural search
1381
			$scrit = explode(' ', $filterkey);
1382
			$i = 0;
1383
			if (count($scrit) > 1) {
1384
				$sql .= "(";
1385
			}
1386
			foreach ($scrit as $crit) {
1387
				if ($i > 0) {
1388
					$sql .= " AND ";
1389
				}
1390
				$sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1391
				$i++;
1392
			}
1393
			if (count($scrit) > 1) {
1394
				$sql .= ")";
1395
			}
1396
			if (!empty($conf->barcode->enabled)) {
1397
				$sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1398
			}
1399
			$sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1400
			$sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1401
			$sql .= ")";
1402
		}
1403
		$sql .= $this->db->order("nom", "ASC");
1404
		$sql .= $this->db->plimit($limit, 0);
1405
1406
		// Build output string
1407
		dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1408
		$resql = $this->db->query($sql);
1409
		if ($resql) {
1410
			if (!$forcecombo) {
1411
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1412
				$out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1413
			}
1414
1415
			// Construct $out and $outarray
1416
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1417
1418
			$textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1419
			if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1420
				// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1421
				//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1422
				if ($showempty && !is_numeric($showempty)) {
1423
					$textifempty = $langs->trans($showempty);
1424
				} else {
1425
					$textifempty .= $langs->trans("All");
1426
				}
1427
			}
1428
			if ($showempty) {
1429
				$out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.($textifempty ? $textifempty : '&nbsp;').'</span>').'">'.$textifempty.'</option>'."\n";
1430
			}
1431
1432
			$num = $this->db->num_rows($resql);
1433
			$i = 0;
1434
			if ($num) {
1435
				while ($i < $num) {
1436
					$obj = $this->db->fetch_object($resql);
1437
					$label = '';
1438
					if ($conf->global->SOCIETE_ADD_REF_IN_LIST) {
1439
						if (($obj->client) && (!empty($obj->code_client))) {
1440
							$label = $obj->code_client.' - ';
1441
						}
1442
						if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1443
							$label .= $obj->code_fournisseur.' - ';
1444
						}
1445
						$label .= ' '.$obj->name;
1446
					} else {
1447
						$label = $obj->name;
1448
					}
1449
1450
					if (!empty($obj->name_alias)) {
1451
						$label .= ' ('.$obj->name_alias.')';
1452
					}
1453
1454
					if (!empty($conf->global->SOCIETE_SHOW_VAT_IN_LIST) && !empty($obj->tva_intra)) {
1455
						$label .= ' - '.$obj->tva_intra.'';
1456
					}
1457
1458
					if ($showtype) {
1459
						if ($obj->client || $obj->fournisseur) {
1460
							$label .= ' (';
1461
						}
1462
						if ($obj->client == 1 || $obj->client == 3) {
1463
							$label .= $langs->trans("Customer");
1464
						}
1465
						if ($obj->client == 2 || $obj->client == 3) {
1466
							$label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1467
						}
1468
						if ($obj->fournisseur) {
1469
							$label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1470
						}
1471
						if ($obj->client || $obj->fournisseur) {
1472
							$label .= ')';
1473
						}
1474
					}
1475
1476
					if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1477
						$label .= ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1478
						if (!empty($obj->country_code)) {
1479
							$label .= ', '.$langs->trans('Country'.$obj->country_code);
1480
						}
1481
					}
1482
1483
					if (empty($outputmode)) {
1484
						if (in_array($obj->rowid, $selected)) {
1485
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
1486
						} else {
1487
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
1488
						}
1489
					} else {
1490
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
1491
					}
1492
1493
					$i++;
1494
					if (($i % 10) == 0) {
1495
						$out .= "\n";
1496
					}
1497
				}
1498
			}
1499
			$out .= '</select>'."\n";
1500
		} else {
1501
			dol_print_error($this->db);
1502
		}
1503
1504
		$this->result = array('nbofthirdparties'=>$num);
1505
1506
		if ($outputmode) {
1507
			return $outarray;
1508
		}
1509
		return $out;
1510
	}
1511
1512
1513
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1514
	/**
1515
	 *  Return HTML combo list of absolute discounts
1516
	 *
1517
	 *  @param	string	$selected       Id remise fixe pre-selectionnee
1518
	 *  @param  string	$htmlname       Nom champ formulaire
1519
	 *  @param  string	$filter         Criteres optionnels de filtre
1520
	 *  @param	int		$socid			Id of thirdparty
1521
	 *  @param	int		$maxvalue		Max value for lines that can be selected
1522
	 *  @return	int						Return number of qualifed lines in list
1523
	 */
1524
	public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1525
	{
1526
		// phpcs:enable
1527
		global $langs, $conf;
1528
1529
		// On recherche les remises
1530
		$sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1531
		$sql .= " re.description, re.fk_facture_source";
1532
		$sql .= " FROM ".$this->db->prefix()."societe_remise_except as re";
1533
		$sql .= " WHERE re.fk_soc = ".(int) $socid;
1534
		$sql .= " AND re.entity = ".$conf->entity;
1535
		if ($filter) {
1536
			$sql .= " AND ".$filter;
1537
		}
1538
		$sql .= " ORDER BY re.description ASC";
1539
1540
		dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1541
		$resql = $this->db->query($sql);
1542
		if ($resql) {
1543
			print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1544
			$num = $this->db->num_rows($resql);
1545
1546
			$qualifiedlines = $num;
1547
1548
			$i = 0;
1549
			if ($num) {
1550
				print '<option value="0">&nbsp;</option>';
1551
				while ($i < $num) {
1552
					$obj = $this->db->fetch_object($resql);
1553
					$desc = dol_trunc($obj->description, 40);
1554
					if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
1555
						$desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1556
					}
1557
					if (preg_match('/\(DEPOSIT\)/', $desc)) {
1558
						$desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1559
					}
1560
					if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
1561
						$desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1562
					}
1563
					if (preg_match('/\(EXCESS PAID\)/', $desc)) {
1564
						$desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1565
					}
1566
1567
					$selectstring = '';
1568
					if ($selected > 0 && $selected == $obj->rowid) {
1569
						$selectstring = ' selected';
1570
					}
1571
1572
					$disabled = '';
1573
					if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1574
						$qualifiedlines--;
1575
						$disabled = ' disabled';
1576
					}
1577
1578
					if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1579
						$tmpfac = new Facture($this->db);
1580
						if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1581
							$desc = $desc.' - '.$tmpfac->ref;
1582
						}
1583
					}
1584
1585
					print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1586
					$i++;
1587
				}
1588
			}
1589
			print '</select>';
1590
			return $qualifiedlines;
1591
		} else {
1592
			dol_print_error($this->db);
1593
			return -1;
1594
		}
1595
	}
1596
1597
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1598
	/**
1599
	 *  Return list of all contacts (for a third party or all)
1600
	 *
1601
	 *  @param	int		$socid      	Id ot third party or 0 for all
1602
	 *  @param  string	$selected   	Id contact pre-selectionne
1603
	 *  @param  string	$htmlname  	    Name of HTML field ('none' for a not editable field)
1604
	 *  @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
1605
	 *  @param  string	$exclude        List of contacts id to exclude
1606
	 *  @param	string	$limitto		Disable answers that are not id in this array list
1607
	 *  @param	integer	$showfunction   Add function into label
1608
	 *  @param	string	$morecss		Add more class to class style
1609
	 *  @param	integer	$showsoc	    Add company into label
1610
	 *  @param	int		$forcecombo		Force to use combo box
1611
	 *  @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')))
1612
	 *  @param	bool	$options_only	Return options only (for ajax treatment)
1613
	 *  @param	string	$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1614
	 *  @param	string	$htmlid			Html id to use instead of htmlname
1615
	 *  @return	int						<0 if KO, Nb of contact in list if OK
1616
	 *  @deprecated						You can use selectcontacts directly (warning order of param was changed)
1617
	 */
1618
	public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '')
1619
	{
1620
		// phpcs:enable
1621
		print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1622
		return $this->num;
1623
	}
1624
1625
	/**
1626
	 *	Return HTML code of the SELECT of list of all contacts (for a third party or all).
1627
	 *  This also set the number of contacts found into $this->num
1628
	 *
1629
	 * @since 9.0 Add afterSelectContactOptions hook
1630
	 *
1631
	 *	@param	int			$socid      	Id ot third party or 0 for all or -1 for empty list
1632
	 *	@param  array|int	$selected   	Array of ID of pre-selected contact id
1633
	 *	@param  string		$htmlname  	    Name of HTML field ('none' for a not editable field)
1634
	 *	@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
1635
	 *	@param  string		$exclude        List of contacts id to exclude
1636
	 *	@param	string		$limitto		Disable answers that are not id in this array list
1637
	 *	@param	integer		$showfunction   Add function into label
1638
	 *	@param	string		$morecss		Add more class to class style
1639
	 *	@param	bool		$options_only	Return options only (for ajax treatment)
1640
	 *	@param	integer		$showsoc	    Add company into label
1641
	 * 	@param	int			$forcecombo		Force to use combo box (so no ajax beautify effect)
1642
	 *  @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')))
1643
	 *  @param	string		$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1644
	 *  @param	string		$htmlid			Html id to use instead of htmlname
1645
	 *  @param	bool		$multiple		add [] in the name of element and add 'multiple' attribut
1646
	 *  @param	integer		$disableifempty Set tag 'disabled' on select if there is no choice
1647
	 *	@return	 int|string					<0 if KO, HTML with select string if OK.
1648
	 */
1649
	public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0)
1650
	{
1651
		global $conf, $langs, $hookmanager, $action;
1652
1653
		$langs->load('companies');
1654
1655
		if (empty($htmlid)) {
1656
			$htmlid = $htmlname;
1657
		}
1658
		$num = 0;
1659
1660
		if ($selected === '') {
1661
			$selected = array();
1662
		} elseif (!is_array($selected)) {
1663
			$selected = array($selected);
1664
		}
1665
		$out = '';
1666
1667
		if (!is_object($hookmanager)) {
1668
			include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1669
			$hookmanager = new HookManager($this->db);
1670
		}
1671
1672
		// We search third parties
1673
		$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";
1674
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1675
			$sql .= ", s.nom as company, s.town AS company_town";
1676
		}
1677
		$sql .= " FROM ".$this->db->prefix()."socpeople as sp";
1678
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1679
			$sql .= " LEFT OUTER JOIN  ".$this->db->prefix()."societe as s ON s.rowid=sp.fk_soc";
1680
		}
1681
		$sql .= " WHERE sp.entity IN (".getEntity('contact').")";
1682
		if ($socid > 0 || $socid == -1) {
1683
			$sql .= " AND sp.fk_soc = ".((int) $socid);
1684
		}
1685
		if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1686
			$sql .= " AND sp.statut <> 0";
1687
		}
1688
		// Add where from hooks
1689
		$parameters = array();
1690
		$reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1691
		$sql .= $hookmanager->resPrint;
1692
		$sql .= " ORDER BY sp.lastname ASC";
1693
1694
		dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1695
		$resql = $this->db->query($sql);
1696
		if ($resql) {
1697
			$num = $this->db->num_rows($resql);
1698
1699
			if ($htmlname != 'none' && !$options_only) {
1700
				$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1701
			}
1702
1703
			if ($showempty && ! is_numeric($showempty)) {
1704
				$textforempty = $showempty;
1705
				$out .= '<option class="optiongrey" value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>'.$textforempty.'</option>';
1706
			} else {
1707
				if (($showempty == 1 || ($showempty == 3 && $num > 1)) && ! $multiple) {
1708
					$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1709
				}
1710
				if ($showempty == 2) {
1711
					$out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1712
				}
1713
			}
1714
1715
			$i = 0;
1716
			if ($num) {
1717
				include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1718
				$contactstatic = new Contact($this->db);
1719
1720
				while ($i < $num) {
1721
					$obj = $this->db->fetch_object($resql);
1722
1723
					// Set email (or phones) and town extended infos
1724
					$extendedInfos = '';
1725
					if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1726
						$extendedInfos = array();
1727
						$email = trim($obj->email);
1728
						if (!empty($email)) {
1729
							$extendedInfos[] = $email;
1730
						} else {
1731
							$phone = trim($obj->phone);
1732
							$phone_perso = trim($obj->phone_perso);
1733
							$phone_mobile = trim($obj->phone_mobile);
1734
							if (!empty($phone)) {
1735
								$extendedInfos[] = $phone;
1736
							}
1737
							if (!empty($phone_perso)) {
1738
								$extendedInfos[] = $phone_perso;
1739
							}
1740
							if (!empty($phone_mobile)) {
1741
								$extendedInfos[] = $phone_mobile;
1742
							}
1743
						}
1744
						$contact_town = trim($obj->contact_town);
1745
						$company_town = trim($obj->company_town);
1746
						if (!empty($contact_town)) {
1747
							$extendedInfos[] = $contact_town;
1748
						} elseif (!empty($company_town)) {
1749
							$extendedInfos[] = $company_town;
1750
						}
1751
						$extendedInfos = implode(' - ', $extendedInfos);
1752
						if (!empty($extendedInfos)) {
1753
							$extendedInfos = ' - '.$extendedInfos;
1754
						}
1755
					}
1756
1757
					$contactstatic->id = $obj->rowid;
1758
					$contactstatic->lastname = $obj->lastname;
1759
					$contactstatic->firstname = $obj->firstname;
1760
					if ($obj->statut == 1) {
1761
						if ($htmlname != 'none') {
1762
							$disabled = 0;
1763
							if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1764
								$disabled = 1;
1765
							}
1766
							if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1767
								$disabled = 1;
1768
							}
1769
							if (!empty($selected) && in_array($obj->rowid, $selected)) {
1770
								$out .= '<option value="'.$obj->rowid.'"';
1771
								if ($disabled) {
1772
									$out .= ' disabled';
1773
								}
1774
								$out .= ' selected>';
1775
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1776
								if ($showfunction && $obj->poste) {
1777
									$out .= ' ('.$obj->poste.')';
1778
								}
1779
								if (($showsoc > 0) && $obj->company) {
1780
									$out .= ' - ('.$obj->company.')';
1781
								}
1782
								$out .= '</option>';
1783
							} else {
1784
								$out .= '<option value="'.$obj->rowid.'"';
1785
								if ($disabled) {
1786
									$out .= ' disabled';
1787
								}
1788
								$out .= '>';
1789
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1790
								if ($showfunction && $obj->poste) {
1791
									$out .= ' ('.$obj->poste.')';
1792
								}
1793
								if (($showsoc > 0) && $obj->company) {
1794
									$out .= ' - ('.$obj->company.')';
1795
								}
1796
								$out .= '</option>';
1797
							}
1798
						} else {
1799
							if (in_array($obj->rowid, $selected)) {
1800
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1801
								if ($showfunction && $obj->poste) {
1802
									$out .= ' ('.$obj->poste.')';
1803
								}
1804
								if (($showsoc > 0) && $obj->company) {
1805
									$out .= ' - ('.$obj->company.')';
1806
								}
1807
							}
1808
						}
1809
					}
1810
					$i++;
1811
				}
1812
			} else {
1813
				$labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1814
				$out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1815
				$out .= $labeltoshow;
1816
				$out .= '</option>';
1817
			}
1818
1819
			$parameters = array(
1820
				'socid'=>$socid,
1821
				'htmlname'=>$htmlname,
1822
				'resql'=>$resql,
1823
				'out'=>&$out,
1824
				'showfunction'=>$showfunction,
1825
				'showsoc'=>$showsoc,
1826
			);
1827
1828
			$reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1829
1830
			if ($htmlname != 'none' && !$options_only) {
1831
				$out .= '</select>';
1832
			}
1833
1834
			if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1835
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1836
				$out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1837
			}
1838
1839
			$this->num = $num;
1840
			return $out;
1841
		} else {
1842
			dol_print_error($this->db);
1843
			return -1;
1844
		}
1845
	}
1846
1847
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1848
	/**
1849
	 *	Return the HTML select list of users
1850
	 *
1851
	 *  @param	string			$selected       Id user preselected
1852
	 *  @param  string			$htmlname       Field name in form
1853
	 *  @param  int				$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
1854
	 *  @param  array			$exclude        Array list of users id to exclude
1855
	 * 	@param	int				$disabled		If select list must be disabled
1856
	 *  @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
1857
	 * 	@param	int				$enableonly		Array list of users id to be enabled. All other must be disabled
1858
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1859
	 * 	@return	void
1860
	 *  @deprecated		Use select_dolusers instead
1861
	 *  @see select_dolusers()
1862
	 */
1863
	public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1864
	{
1865
		// phpcs:enable
1866
		print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1867
	}
1868
1869
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1870
	/**
1871
	 *	Return select list of users
1872
	 *
1873
	 *  @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)
1874
	 *  @param  string			$htmlname       Field name in form
1875
	 *  @param  int|string		$show_empty     0=list with no empty value, 1=add also an empty value into list
1876
	 *  @param  array			$exclude        Array list of users id to exclude
1877
	 * 	@param	int				$disabled		If select list must be disabled
1878
	 *  @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
1879
	 * 	@param	array			$enableonly		Array list of users id to be enabled. If defined, it means that others will be disabled
1880
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1881
	 *  @param	int				$maxlength		Maximum length of string into list (0=no limit)
1882
	 *  @param	int				$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
1883
	 *  @param	string			$morefilter		Add more filters into sql request (Example: 'employee = 1'). This value must not come from user input.
1884
	 *  @param	integer			$show_every		0=default list, 1=add also a value "Everybody" at beginning of list
1885
	 *  @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.
1886
	 *  @param	string			$morecss		More css
1887
	 *  @param  int     		$noactive       Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
1888
	 *  @param  int				$outputmode     0=HTML select string, 1=Array
1889
	 *  @param  bool			$multiple       add [] in the name of element and add 'multiple' attribut
1890
	 * 	@return	string							HTML select string
1891
	 *  @see select_dolgroups()
1892
	 */
1893
	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)
1894
	{
1895
		// phpcs:enable
1896
		global $conf, $user, $langs, $hookmanager;
1897
1898
		// If no preselected user defined, we take current user
1899
		if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1900
			$selected = $user->id;
1901
		}
1902
1903
		if ($selected === '') {
1904
			$selected = array();
1905
		} elseif (!is_array($selected)) {
1906
			$selected = array($selected);
1907
		}
1908
1909
		$excludeUsers = null;
1910
		$includeUsers = null;
1911
1912
		// Permettre l'exclusion d'utilisateurs
1913
		if (is_array($exclude)) {
1914
			$excludeUsers = implode(",", $exclude);
1915
		}
1916
		// Permettre l'inclusion d'utilisateurs
1917
		if (is_array($include)) {
1918
			$includeUsers = implode(",", $include);
1919
		} elseif ($include == 'hierarchy') {
1920
			// Build list includeUsers to have only hierarchy
1921
			$includeUsers = implode(",", $user->getAllChildIds(0));
1922
		} elseif ($include == 'hierarchyme') {
1923
			// Build list includeUsers to have only hierarchy and current user
1924
			$includeUsers = implode(",", $user->getAllChildIds(1));
1925
		}
1926
1927
		$out = '';
1928
		$outarray = array();
1929
1930
		// Forge request to select users
1931
		$sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1932
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1933
			$sql .= ", e.label";
1934
		}
1935
		$sql .= " FROM ".$this->db->prefix()."user as u";
1936
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1937
			$sql .= " LEFT JOIN ".$this->db->prefix()."entity as e ON e.rowid = u.entity";
1938
			if ($force_entity) {
1939
				$sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
1940
			} else {
1941
				$sql .= " WHERE u.entity IS NOT NULL";
1942
			}
1943
		} else {
1944
			if (!empty($conf->multicompany->enabled) && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1945
				$sql .= " LEFT JOIN ".$this->db->prefix()."usergroup_user as ug";
1946
				$sql .= " ON ug.fk_user = u.rowid";
1947
				$sql .= " WHERE ug.entity = ".$conf->entity;
1948
			} else {
1949
				$sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
1950
			}
1951
		}
1952
		if (!empty($user->socid)) {
1953
			$sql .= " AND u.fk_soc = ".((int) $user->socid);
1954
		}
1955
		if (is_array($exclude) && $excludeUsers) {
1956
			$sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
1957
		}
1958
		if ($includeUsers) {
1959
			$sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
1960
		}
1961
		if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) {
1962
			$sql .= " AND u.statut <> 0";
1963
		}
1964
		if (!empty($morefilter)) {
1965
			$sql .= " ".$morefilter;
1966
		}
1967
1968
		//Add hook to filter on user (for exemple on usergroup define in custom modules)
1969
		$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...
1970
		if (!empty($reshook)) {
1971
			$sql .= $hookmanager->resPrint;
1972
		}
1973
1974
		if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {	// MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
1975
			$sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
1976
		} else {
1977
			$sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
1978
		}
1979
1980
		dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
1981
1982
		$resql = $this->db->query($sql);
1983
		if ($resql) {
1984
			$num = $this->db->num_rows($resql);
1985
			$i = 0;
1986
			if ($num) {
1987
				// do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
1988
				$out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
1989
				if ($show_empty && !$multiple) {
1990
					$textforempty = ' ';
1991
					if (!empty($conf->use_javascript_ajax)) {
1992
						$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
1993
					}
1994
					if (!is_numeric($show_empty)) {
1995
						$textforempty = $show_empty;
1996
					}
1997
					$out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
1998
				}
1999
				if ($show_every) {
2000
					$out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
2001
				}
2002
2003
				$userstatic = new User($this->db);
2004
2005
				while ($i < $num) {
2006
					$obj = $this->db->fetch_object($resql);
2007
2008
					$userstatic->id = $obj->rowid;
2009
					$userstatic->lastname = $obj->lastname;
2010
					$userstatic->firstname = $obj->firstname;
2011
					$userstatic->photo = $obj->photo;
2012
					$userstatic->statut = $obj->status;
2013
					$userstatic->entity = $obj->entity;
2014
					$userstatic->admin = $obj->admin;
2015
2016
					$disableline = '';
2017
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2018
						$disableline = ($enableonlytext ? $enableonlytext : '1');
2019
					}
2020
2021
					$labeltoshow = '';
2022
2023
					// $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2024
					$fullNameMode = 0;
2025
					if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2026
						$fullNameMode = 1; //Firstname+lastname
2027
					}
2028
					$labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2029
					if (empty($obj->firstname) && empty($obj->lastname)) {
2030
						$labeltoshow .= $obj->login;
2031
					}
2032
2033
					// Complete name with more info
2034
					$moreinfo = '';
2035
					if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2036
						$moreinfo .= ($moreinfo ? ' - ' : ' (').$obj->login;
2037
					}
2038
					if ($showstatus >= 0) {
2039
						if ($obj->status == 1 && $showstatus == 1) {
2040
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2041
						}
2042
						if ($obj->status == 0 && $showstatus == 1) {
2043
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2044
						}
2045
					}
2046
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2047
						if (!$obj->entity) {
2048
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2049
						} else {
2050
							if ($obj->entity != $conf->entity) {
2051
								$moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2052
							}
2053
						}
2054
					}
2055
					$moreinfo .= ($moreinfo ? ')' : '');
2056
					if ($disableline && $disableline != '1') {
2057
						$moreinfo .= ' - '.$disableline; // This is text from $enableonlytext parameter
2058
					}
2059
					$labeltoshow .= $moreinfo;
2060
2061
					$out .= '<option value="'.$obj->rowid.'"';
2062
					if ($disableline) {
2063
						$out .= ' disabled';
2064
					}
2065
					if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2066
						$out .= ' selected';
2067
					}
2068
					$out .= ' data-html="';
2069
					$outhtml = '';
2070
					// if (!empty($obj->photo)) {
2071
					$outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2072
					// }
2073
					if ($showstatus >= 0 && $obj->status == 0) {
2074
						$outhtml .= '<strike class="opacitymediumxxx">';
2075
					}
2076
					$outhtml .= $labeltoshow;
2077
					if ($showstatus >= 0 && $obj->status == 0) {
2078
						$outhtml .= '</strike>';
2079
					}
2080
					$out .= dol_escape_htmltag($outhtml);
2081
					$out .= '">';
2082
					$out .= $labeltoshow;
2083
					$out .= '</option>';
2084
2085
					$outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2086
2087
					$i++;
2088
				}
2089
			} else {
2090
				$out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2091
				$out .= '<option value="">'.$langs->trans("None").'</option>';
2092
			}
2093
			$out .= '</select>';
2094
2095
			if ($num) {
2096
				// Enhance with select2
2097
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2098
				$out .= ajax_combobox($htmlname);
2099
			}
2100
		} else {
2101
			dol_print_error($this->db);
2102
		}
2103
2104
		if ($outputmode) {
2105
			return $outarray;
2106
		}
2107
2108
		return $out;
2109
	}
2110
2111
2112
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2113
	/**
2114
	 *	Return select list of users. Selected users are stored into session.
2115
	 *  List of users are provided into $_SESSION['assignedtouser'].
2116
	 *
2117
	 *  @param  string	$action         Value for $action
2118
	 *  @param  string	$htmlname       Field name in form
2119
	 *  @param  int		$show_empty     0=list without the empty value, 1=add empty value
2120
	 *  @param  array	$exclude        Array list of users id to exclude
2121
	 * 	@param	int		$disabled		If select list must be disabled
2122
	 *  @param  array	$include        Array list of users id to include or 'hierarchy' to have only supervised users
2123
	 * 	@param	array	$enableonly		Array list of users id to be enabled. All other must be disabled
2124
	 *  @param	int		$force_entity	'0' or Ids of environment to force
2125
	 *  @param	int		$maxlength		Maximum length of string into list (0=no limit)
2126
	 *  @param	int		$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2127
	 *  @param	string	$morefilter		Add more filters into sql request
2128
	 *  @param	int		$showproperties		Show properties of each attendees
2129
	 *  @param	array	$listofuserid		Array with properties of each user
2130
	 *  @param	array	$listofcontactid	Array with properties of each contact
2131
	 *  @param	array	$listofotherid		Array with properties of each other contact
2132
	 * 	@return	string					HTML select string
2133
	 *  @see select_dolgroups()
2134
	 */
2135
	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())
2136
	{
2137
		// phpcs:enable
2138
		global $conf, $user, $langs;
2139
2140
		$userstatic = new User($this->db);
2141
		$out = '';
2142
2143
2144
		$assignedtouser = array();
2145
		if (!empty($_SESSION['assignedtouser'])) {
2146
			$assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2147
		}
2148
		$nbassignetouser = count($assignedtouser);
2149
2150
		//if ($nbassignetouser && $action != 'view') $out .= '<br>';
2151
		if ($nbassignetouser) {
2152
			$out .= '<ul class="attendees">';
2153
		}
2154
		$i = 0;
2155
		$ownerid = 0;
2156
		foreach ($assignedtouser as $key => $value) {
2157
			if ($value['id'] == $ownerid) {
2158
				continue;
2159
			}
2160
2161
			$out .= '<li>';
2162
			$userstatic->fetch($value['id']);
2163
			$out .= $userstatic->getNomUrl(-1);
2164
			if ($i == 0) {
2165
				$ownerid = $value['id'];
2166
				$out .= ' ('.$langs->trans("Owner").')';
2167
			}
2168
			if ($nbassignetouser > 1 && $action != 'view') {
2169
				$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.'">';
2170
			}
2171
			// Show my availability
2172
			if ($showproperties) {
2173
				if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2174
					$out .= '<div class="myavailability inline-block">';
2175
					$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>';
2176
					$out .= '</div>';
2177
				}
2178
			}
2179
			//$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2180
			//$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2181
2182
			$out .= '</li>';
2183
			$i++;
2184
		}
2185
		if ($nbassignetouser) {
2186
			$out .= '</ul>';
2187
		}
2188
2189
		// Method with no ajax
2190
		if ($action != 'view') {
2191
			$out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2192
			$out .= '<script type="text/javascript">jQuery(document).ready(function () {';
2193
			$out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2194
			$out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2195
			$out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2196
			$out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2197
			$out .= '});';
2198
			$out .= '})</script>';
2199
			$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2200
			$out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2201
			$out .= '<br>';
2202
		}
2203
2204
		return $out;
2205
	}
2206
2207
2208
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2209
	/**
2210
	 *  Return list of products for customer in Ajax if Ajax activated or go to select_produits_list
2211
	 *
2212
	 *  @param		int			$selected				Preselected products
2213
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
2214
	 *  @param		int|string	$filtertype				Filter on product type (''=nofilter, 0=product, 1=service)
2215
	 *  @param		int			$limit					Limit on number of returned lines
2216
	 *  @param		int			$price_level			Level of price to show
2217
	 *  @param		int			$status					Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell
2218
	 *  @param		int			$finished				2=all, 1=finished, 0=raw material
2219
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
2220
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
2221
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
2222
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
2223
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2224
	 * 	@param		int			$forcecombo				Force to use combo box
2225
	 *  @param      string      $morecss                Add more css on select
2226
	 *  @param      int         $hidepriceinlabel       1=Hide prices in label
2227
	 *  @param      string      $warehouseStatus        Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used
2228
	 *										            'warehouseopen' = count products from open warehouses,
2229
	 *										            'warehouseclosed' = count products from closed warehouses,
2230
	 *										            'warehouseinternal' = count products from warehouses for internal correct/transfer only
2231
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
2232
	 *  @param		string		$nooutput				No print, return the output into a string
2233
	 *  @param		int			$status_purchase		Purchase status -1=Return all products, 0=Products not on purchase, 1=Products on purchase
2234
	 *  @return		void|string
2235
	 */
2236
	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, $status_purchase = -1)
2237
	{
2238
		// phpcs:enable
2239
		global $langs, $conf;
2240
2241
		$out = '';
2242
2243
		// check parameters
2244
		$price_level = (!empty($price_level) ? $price_level : 0);
2245
		if (is_null($ajaxoptions)) {
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
2246
			$ajaxoptions = array();
2247
		}
2248
2249
		if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) {
2250
			if (!empty($conf->product->enabled) && empty($conf->service->enabled)) {
2251
				$filtertype = '0';
2252
			} elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) {
2253
				$filtertype = '1';
2254
			}
2255
		}
2256
2257
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2258
			$placeholder = '';
2259
2260
			if ($selected && empty($selected_input_value)) {
2261
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2262
				$producttmpselect = new Product($this->db);
2263
				$producttmpselect->fetch($selected);
2264
				$selected_input_value = $producttmpselect->ref;
2265
				unset($producttmpselect);
2266
			}
2267
			// handle case where product or service module is disabled + no filter specified
2268
			if ($filtertype == '') {
2269
				if (empty($conf->product->enabled)) { // when product module is disabled, show services only
2270
					$filtertype = 1;
2271
				} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2272
					$filtertype = 0;
2273
				}
2274
			}
2275
			// mode=1 means customers products
2276
			$urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&status_purchase='.$status_purchase.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
2277
			//Price by customer
2278
			if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2279
				$urloption .= '&socid='.$socid;
2280
			}
2281
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2282
2283
			if (!empty($conf->variants->enabled) && is_array($selected_combinations)) {
2284
				// Code to automatically insert with javascript the select of attributes under the select of product
2285
				// when a parent of variant has been selected.
2286
				$out .= '
2287
				<!-- script to auto show attributes select tags if a variant was selected -->
2288
				<script>
2289
					// auto show attributes fields
2290
					selected = '.json_encode($selected_combinations).';
2291
					combvalues = {};
2292
2293
					jQuery(document).ready(function () {
2294
2295
						jQuery("input[name=\'prod_entry_mode\']").change(function () {
2296
							if (jQuery(this).val() == \'free\') {
2297
								jQuery(\'div#attributes_box\').empty();
2298
							}
2299
						});
2300
2301
						jQuery("input#'.$htmlname.'").change(function () {
2302
2303
							if (!jQuery(this).val()) {
2304
								jQuery(\'div#attributes_box\').empty();
2305
								return;
2306
							}
2307
2308
							console.log("A change has started. We get variants fields to inject html select");
2309
2310
							jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2311
								id: jQuery(this).val()
2312
							}, function (data) {
2313
								jQuery(\'div#attributes_box\').empty();
2314
2315
								jQuery.each(data, function (key, val) {
2316
2317
									combvalues[val.id] = val.values;
2318
2319
									var span = jQuery(document.createElement(\'div\')).css({
2320
										\'display\': \'table-row\'
2321
									});
2322
2323
									span.append(
2324
										jQuery(document.createElement(\'div\')).text(val.label).css({
2325
											\'font-weight\': \'bold\',
2326
											\'display\': \'table-cell\'
2327
										})
2328
									);
2329
2330
									var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2331
										\'margin-left\': \'15px\',
2332
										\'white-space\': \'pre\'
2333
									}).append(
2334
										jQuery(document.createElement(\'option\')).val(\'\')
2335
									);
2336
2337
									jQuery.each(combvalues[val.id], function (key, val) {
2338
										var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2339
2340
										if (selected[val.fk_product_attribute] == val.id) {
2341
											tag.attr(\'selected\', \'selected\');
2342
										}
2343
2344
										html.append(tag);
2345
									});
2346
2347
									span.append(html);
2348
									jQuery(\'div#attributes_box\').append(span);
2349
								});
2350
							})
2351
						});
2352
2353
						'.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2354
					});
2355
				</script>
2356
                ';
2357
			}
2358
2359
			if (empty($hidelabel)) {
2360
				$out .= $langs->trans("RefOrLabel").' : ';
2361
			} elseif ($hidelabel > 1) {
2362
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2363
				if ($hidelabel == 2) {
2364
					$out .= img_picto($langs->trans("Search"), 'search');
2365
				}
2366
			}
2367
			$out .= '<input type="text" class="minwidth100'.($morecss ? ' '.$morecss : '').'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
2368
			if ($hidelabel == 3) {
2369
				$out .= img_picto($langs->trans("Search"), 'search');
2370
			}
2371
		} else {
2372
			$out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2373
		}
2374
2375
		if (empty($nooutput)) {
2376
			print $out;
2377
		} else {
2378
			return $out;
2379
		}
2380
	}
2381
2382
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2383
2384
	/**
2385
	 *  Return list of BOM for customer in Ajax if Ajax activated or go to select_produits_list
2386
	 *
2387
	 * @param int $selected Preselected BOM id
2388
	 * @param string $htmlname Name of HTML select field (must be unique in page).
2389
	 * @param int $limit Limit on number of returned lines
2390
	 * @param int $status Sell status -1=Return all bom, 0=Draft BOM, 1=Validated BOM
2391
	 * @param int $type type of the BOM (-1=Return all BOM, 0=Return disassemble BOM, 1=Return manufacturing BOM)
2392
	 * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2393
	 * @param string $morecss Add more css on select
2394
	 * @param string $nooutput No print, return the output into a string
2395
	 * @param int $forcecombo Force to use combo box
2396
	 * @return        void|string
2397
	 */
2398
	public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 1, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0)
2399
	{
2400
		// phpcs:enable
2401
		global $conf, $user, $langs, $db;
2402
2403
		require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2404
2405
		$error = 0;
2406
		$out = '';
2407
2408
		if (!$forcecombo) {
2409
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2410
			$out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $events seems to be never defined.
Loading history...
2411
		}
2412
2413
		$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2414
2415
		$sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2416
		$sql.= ' FROM '.MAIN_DB_PREFIX.'bom_bom as b';
2417
		$sql.= ' WHERE b.entity IN ('.getEntity('bom').')';
2418
		if (!empty($status)) $sql.= ' AND status = '. (int) $status;
2419
		if (!empty($type)) $sql.= ' AND status = '. (int) $type;
2420
		if (!empty($limit)) $sql.= 'LIMIT '. (int) $limit;
2421
		$resql = $db->query($sql);
2422
		if ($resql) {
2423
			if ($showempty)	{
2424
				$out .= '<option value="-1"';
2425
				if (empty($selected)) $out .= ' selected';
2426
				$out .= '>&nbsp;</option>';
2427
			}
2428
			while ($obj = $db->fetch_object($resql)) {
2429
				$product = new Product($db);
2430
				$res = $product->fetch($obj->fk_product);
2431
				if ($obj->rowid == $selected) $out .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.' - '. $product->label .' - '.$obj->label.'</option>';
2432
				$out .= '<option value="'.$obj->rowid.'">'.$obj->ref.' - '.$product->label .' - '. $obj->label.'</option>';
2433
			}
2434
		} else {
2435
			$error++;
2436
			dol_print_error($db);
2437
		}
2438
		if (empty($nooutput)) {
2439
			print $out;
2440
		} else {
2441
			return $out;
2442
		}
2443
	}
2444
2445
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2446
	/**
2447
	 *	Return list of products for a customer.
2448
	 *  Called by select_produits.
2449
	 *
2450
	 *	@param      int		$selected           Preselected product
2451
	 *	@param      string	$htmlname           Name of select html
2452
	 *  @param		string	$filtertype         Filter on product type (''=nofilter, 0=product, 1=service)
2453
	 *	@param      int		$limit              Limit on number of returned lines
2454
	 *	@param      int		$price_level        Level of price to show
2455
	 * 	@param      string	$filterkey          Filter on product
2456
	 *	@param		int		$status             -1=Return all products, 0=Products not on sell, 1=Products on sell
2457
	 *  @param      int		$finished           Filter on finished field: 2=No filter
2458
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
2459
	 *  @param      int		$socid     		    Thirdparty Id (to get also price dedicated to this customer)
2460
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2461
	 * 	@param		int		$forcecombo		    Force to use combo box
2462
	 *  @param      string  $morecss            Add more css on select
2463
	 *  @param      int     $hidepriceinlabel   1=Hide prices in label
2464
	 *  @param      string  $warehouseStatus    Warehouse status filter to group/count stock. Following comma separated filter options can be used.
2465
	 *										    'warehouseopen' = count products from open warehouses,
2466
	 *										    'warehouseclosed' = count products from closed warehouses,
2467
	 *										    'warehouseinternal' = count products from warehouses for internal correct/transfer only
2468
	 *  @param		int		$status_purchase	Purchase status -1=Return all products, 0=Products not on purchase, 1=Products on purchase
2469
	 *  @return     array    				    Array of keys for json
2470
	 */
2471
	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 = '', $status_purchase = -1)
2472
	{
2473
		// phpcs:enable
2474
		global $langs, $conf;
2475
		global $hookmanager;
2476
2477
		$out = '';
2478
		$outarray = array();
2479
2480
		// Units
2481
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2482
			$langs->load('other');
2483
		}
2484
2485
		$warehouseStatusArray = array();
2486
		if (!empty($warehouseStatus)) {
2487
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2488
			if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2489
				$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2490
			}
2491
			if (preg_match('/warehouseopen/', $warehouseStatus)) {
2492
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2493
			}
2494
			if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2495
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2496
			}
2497
		}
2498
2499
		$selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.default_vat_code, p.duration, p.fk_price_expression";
2500
		if (count($warehouseStatusArray)) {
2501
			$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
2502
		} else {
2503
			$selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2504
		}
2505
2506
		$sql = "SELECT ";
2507
		$sql .= $selectFields.$selectFieldsGrouped;
2508
2509
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2510
			//Product category
2511
			$sql .= ", (SELECT ".$this->db->prefix()."categorie_product.fk_categorie
2512
						FROM ".$this->db->prefix()."categorie_product
2513
						WHERE ".$this->db->prefix()."categorie_product.fk_product=p.rowid
2514
						LIMIT 1
2515
				) AS categorie_product_id ";
2516
		}
2517
2518
		//Price by customer
2519
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2520
			$sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2521
			$sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.default_vat_code as custdefault_vat_code, pcp.ref_customer as custref';
2522
			$selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2523
		}
2524
		// Units
2525
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2526
			$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";
2527
			$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';
2528
		}
2529
2530
		// Multilang : we add translation
2531
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2532
			$sql .= ", pl.label as label_translated";
2533
			$sql .= ", pl.description as description_translated";
2534
			$selectFields .= ", label_translated";
2535
			$selectFields .= ", description_translated";
2536
		}
2537
		// Price by quantity
2538
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2539
			$sql .= ", (SELECT pp.rowid FROM ".$this->db->prefix()."product_price as pp WHERE pp.fk_product = p.rowid";
2540
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2541
				$sql .= " AND price_level = ".((int) $price_level);
2542
			}
2543
			$sql .= " ORDER BY date_price";
2544
			$sql .= " DESC LIMIT 1) as price_rowid";
2545
			$sql .= ", (SELECT pp.price_by_qty FROM ".$this->db->prefix()."product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable
2546
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2547
				$sql .= " AND price_level = ".((int) $price_level);
2548
			}
2549
			$sql .= " ORDER BY date_price";
2550
			$sql .= " DESC LIMIT 1) as price_by_qty";
2551
			$selectFields .= ", price_rowid, price_by_qty";
2552
		}
2553
		$sql .= " FROM ".$this->db->prefix()."product as p";
2554
		if (count($warehouseStatusArray)) {
2555
			$sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.fk_product = p.rowid";
2556
			$sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2557
			$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.
2558
		}
2559
2560
		// include search in supplier ref
2561
		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2562
			$sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2563
		}
2564
2565
		//Price by customer
2566
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2567
			$sql .= " LEFT JOIN  ".$this->db->prefix()."product_customer_price as pcp ON pcp.fk_soc=".((int) $socid)." AND pcp.fk_product=p.rowid";
2568
		}
2569
		// Units
2570
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2571
			$sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
2572
		}
2573
		// Multilang : we add translation
2574
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2575
			$sql .= " LEFT JOIN ".$this->db->prefix()."product_lang as pl ON pl.fk_product = p.rowid ";
2576
			if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2577
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2578
				$soc = new Societe($this->db);
2579
				$result = $soc->fetch($socid);
2580
				if ($result > 0 && !empty($soc->default_lang)) {
2581
					$sql .= " AND pl.lang = '".$this->db->escape($soc->default_lang)."'";
2582
				} else {
2583
					$sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2584
				}
2585
			} else {
2586
				$sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2587
			}
2588
		}
2589
2590
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2591
			$sql .= " LEFT JOIN ".$this->db->prefix()."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2592
		}
2593
2594
		$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2595
2596
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2597
			$sql .= " AND pac.rowid IS NULL";
2598
		}
2599
2600
		if ($finished == 0) {
2601
			$sql .= " AND p.finished = ".((int) $finished);
2602
		} elseif ($finished == 1) {
2603
			$sql .= " AND p.finished = ".((int) $finished);
2604
			if ($status >= 0) {
2605
				$sql .= " AND p.tosell = ".((int) $status);
2606
			}
2607
		} elseif ($status >= 0) {
2608
			$sql .= " AND p.tosell = ".((int) $status);
2609
		}
2610
		if ($status_purchase >= 0) {
2611
			$sql .= " AND p.tobuy = ".((int) $status_purchase);
2612
		}
2613
		// Filter by product type
2614
		if (strval($filtertype) != '') {
2615
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
2616
		} elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only
2617
			$sql .= " AND p.fk_product_type = 1";
2618
		} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2619
			$sql .= " AND p.fk_product_type = 0";
2620
		}
2621
		// Add where from hooks
2622
		$parameters = array();
2623
		$reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
2624
		$sql .= $hookmanager->resPrint;
2625
		// Add criteria on ref/label
2626
		if ($filterkey != '') {
2627
			$sql .= ' AND (';
2628
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2629
			// For natural search
2630
			$scrit = explode(' ', $filterkey);
2631
			$i = 0;
2632
			if (count($scrit) > 1) {
2633
				$sql .= "(";
2634
			}
2635
			foreach ($scrit as $crit) {
2636
				if ($i > 0) {
2637
					$sql .= " AND ";
2638
				}
2639
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2640
				if (!empty($conf->global->MAIN_MULTILANGS)) {
2641
					$sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2642
				}
2643
				if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) {
2644
					$sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2645
				}
2646
				if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2647
					$sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2648
					if (!empty($conf->global->MAIN_MULTILANGS)) {
2649
						$sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2650
					}
2651
				}
2652
				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2653
					$sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2654
				}
2655
				$sql .= ")";
2656
				$i++;
2657
			}
2658
			if (count($scrit) > 1) {
2659
				$sql .= ")";
2660
			}
2661
			if (!empty($conf->barcode->enabled)) {
2662
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2663
			}
2664
			$sql .= ')';
2665
		}
2666
		if (count($warehouseStatusArray)) {
2667
			$sql .= " GROUP BY ".$selectFields;
2668
		}
2669
2670
		//Sort by category
2671
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2672
			$sql .= " ORDER BY categorie_product_id ";
2673
			//ASC OR DESC order
2674
			($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2675
		} else {
2676
			$sql .= $this->db->order("p.ref");
2677
		}
2678
2679
		$sql .= $this->db->plimit($limit, 0);
2680
2681
		// Build output string
2682
		dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2683
		$result = $this->db->query($sql);
2684
		if ($result) {
2685
			require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2686
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2687
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2688
2689
			$num = $this->db->num_rows($result);
2690
2691
			$events = null;
2692
2693
			if (!$forcecombo) {
2694
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2695
				$out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2696
			}
2697
2698
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2699
2700
			$textifempty = '';
2701
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2702
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2703
			if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2704
				if ($showempty && !is_numeric($showempty)) {
2705
					$textifempty = $langs->trans($showempty);
2706
				} else {
2707
					$textifempty .= $langs->trans("All");
2708
				}
2709
			} else {
2710
				if ($showempty && !is_numeric($showempty)) {
2711
					$textifempty = $langs->trans($showempty);
2712
				}
2713
			}
2714
			if ($showempty) {
2715
				$out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2716
			}
2717
2718
			$i = 0;
2719
			while ($num && $i < $num) {
2720
				$opt = '';
2721
				$optJson = array();
2722
				$objp = $this->db->fetch_object($result);
2723
2724
				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
2725
					$sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2726
					$sql .= " FROM ".$this->db->prefix()."product_price_by_qty";
2727
					$sql .= " WHERE fk_product_price = ".((int) $objp->price_rowid);
2728
					$sql .= " ORDER BY quantity ASC";
2729
2730
					dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2731
					$result2 = $this->db->query($sql);
2732
					if ($result2) {
2733
						$nb_prices = $this->db->num_rows($result2);
2734
						$j = 0;
2735
						while ($nb_prices && $j < $nb_prices) {
2736
							$objp2 = $this->db->fetch_object($result2);
2737
2738
							$objp->price_by_qty_rowid = $objp2->rowid;
2739
							$objp->price_by_qty_price_base_type = $objp2->price_base_type;
2740
							$objp->price_by_qty_quantity = $objp2->quantity;
2741
							$objp->price_by_qty_unitprice = $objp2->unitprice;
2742
							$objp->price_by_qty_remise_percent = $objp2->remise_percent;
2743
							// For backward compatibility
2744
							$objp->quantity = $objp2->quantity;
2745
							$objp->price = $objp2->price;
2746
							$objp->unitprice = $objp2->unitprice;
2747
							$objp->remise_percent = $objp2->remise_percent;
2748
							$objp->remise = $objp2->remise;
2749
2750
							//$objp->tva_tx is not overwritten by $objp2 value
2751
							//$objp->default_vat_code is not overwritten by $objp2 value
2752
2753
							$this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2754
2755
							$j++;
2756
2757
							// Add new entry
2758
							// "key" value of json key array is used by jQuery automatically as selected value
2759
							// "label" value of json key array is used by jQuery automatically as text for combo box
2760
							$out .= $opt;
2761
							array_push($outarray, $optJson);
2762
						}
2763
					}
2764
				} else {
2765
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
2766
						$price_product = new Product($this->db);
2767
						$price_product->fetch($objp->rowid, '', '', 1);
2768
						$priceparser = new PriceParser($this->db);
2769
						$price_result = $priceparser->parseProduct($price_product);
2770
						if ($price_result >= 0) {
2771
							$objp->price = $price_result;
2772
							$objp->unitprice = $price_result;
2773
							//Calculate the VAT
2774
							$objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2775
							$objp->price_ttc = price2num($objp->price_ttc, 'MU');
2776
						}
2777
					}
2778
2779
					$this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2780
					// Add new entry
2781
					// "key" value of json key array is used by jQuery automatically as selected value
2782
					// "label" value of json key array is used by jQuery automatically as text for combo box
2783
					$out .= $opt;
2784
					array_push($outarray, $optJson);
2785
				}
2786
2787
				$i++;
2788
			}
2789
2790
			$out .= '</select>';
2791
2792
			$this->db->free($result);
2793
2794
			if (empty($outputmode)) {
2795
				return $out;
2796
			}
2797
			return $outarray;
2798
		} else {
2799
			dol_print_error($this->db);
2800
		}
2801
	}
2802
2803
	/**
2804
	 * Function to forge the string with OPTIONs of SELECT.
2805
	 * This define value for &$opt and &$optJson.
2806
	 * This function is called by select_produits_list().
2807
	 *
2808
	 * @param 	resource	$objp			    Resultset of fetch
2809
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
2810
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
2811
	 * @param 	int			$price_level	    Price level
2812
	 * @param 	string		$selected		    Preselected value
2813
	 * @param   int         $hidepriceinlabel   Hide price in label
2814
	 * @param   string      $filterkey          Filter key to highlight
2815
	 * @param	int			$novirtualstock 	Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on.
2816
	 * @return	void
2817
	 */
2818
	protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2819
	{
2820
		global $langs, $conf, $user;
2821
2822
		$outkey = '';
2823
		$outval = '';
2824
		$outref = '';
2825
		$outlabel = '';
2826
		$outlabel_translated = '';
2827
		$outdesc = '';
2828
		$outdesc_translated = '';
2829
		$outbarcode = '';
2830
		$outorigin = '';
2831
		$outtype = '';
2832
		$outprice_ht = '';
2833
		$outprice_ttc = '';
2834
		$outpricebasetype = '';
2835
		$outtva_tx = '';
2836
		$outdefault_vat_code = '';
2837
		$outqty = 1;
2838
		$outdiscount = 0;
2839
2840
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2841
2842
		$label = $objp->label;
2843
		if (!empty($objp->label_translated)) {
2844
			$label = $objp->label_translated;
2845
		}
2846
		if (!empty($filterkey) && $filterkey != '') {
2847
			$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2848
		}
2849
2850
		$outkey = $objp->rowid;
2851
		$outref = $objp->ref;
2852
		$outrefcust = empty($objp->custref) ? '' : $objp->custref;
2853
		$outlabel = $objp->label;
2854
		$outdesc = $objp->description;
2855
		if (!empty($conf->global->MAIN_MULTILANGS)) {
2856
			$outlabel_translated = $objp->label_translated;
2857
			$outdesc_translated = $objp->description_translated;
2858
		}
2859
		$outbarcode = $objp->barcode;
2860
		$outorigin = $objp->fk_country;
2861
		$outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2862
2863
		$outtype = $objp->fk_product_type;
2864
		$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2865
		$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2866
2867
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2868
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2869
		}
2870
2871
		// Units
2872
		$outvalUnits = '';
2873
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2874
			if (!empty($objp->unit_short)) {
2875
				$outvalUnits .= ' - '.$objp->unit_short;
2876
			}
2877
		}
2878
		if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2879
			if (!empty($objp->weight) && $objp->weight_units !== null) {
2880
				$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2881
				$outvalUnits .= ' - '.$unitToShow;
2882
			}
2883
			if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2884
				$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2885
				$outvalUnits .= ' - '.$unitToShow;
2886
			}
2887
			if (!empty($objp->surface) && $objp->surface_units !== null) {
2888
				$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2889
				$outvalUnits .= ' - '.$unitToShow;
2890
			}
2891
			if (!empty($objp->volume) && $objp->volume_units !== null) {
2892
				$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2893
				$outvalUnits .= ' - '.$unitToShow;
2894
			}
2895
		}
2896
		if ($outdurationvalue && $outdurationunit) {
2897
			$da = array(
2898
				'h' => $langs->trans('Hour'),
2899
				'd' => $langs->trans('Day'),
2900
				'w' => $langs->trans('Week'),
2901
				'm' => $langs->trans('Month'),
2902
				'y' => $langs->trans('Year')
2903
			);
2904
			if (isset($da[$outdurationunit])) {
2905
				$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2906
			}
2907
		}
2908
2909
		$opt = '<option value="'.$objp->rowid.'"';
2910
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
2911
		if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2912
			$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.'"';
2913
		}
2914
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2915
			if (!empty($user->rights->stock->lire)) {
2916
				if ($objp->stock > 0) {
2917
					$opt .= ' class="product_line_stock_ok"';
2918
				} elseif ($objp->stock <= 0) {
2919
					$opt .= ' class="product_line_stock_too_low"';
2920
				}
2921
			}
2922
		}
2923
		if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2924
			$opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2925
			$opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2926
		}
2927
		$opt .= '>';
2928
		$opt .= $objp->ref;
2929
		if (! empty($objp->custref)) {
2930
			$opt.= ' (' . $objp->custref . ')';
2931
		}
2932
		if ($outbarcode) {
2933
			$opt .= ' ('.$outbarcode.')';
2934
		}
2935
		$opt .= ' - '.dol_trunc($label, $maxlengtharticle);
2936
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2937
			$opt .= ' ('.getCountry($outorigin, 1).')';
2938
		}
2939
2940
		$objRef = $objp->ref;
2941
		if (! empty($objp->custref)) {
2942
			$objRef .= ' (' . $objp->custref . ')';
2943
		}
2944
		if (!empty($filterkey) && $filterkey != '') {
2945
			$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2946
		}
2947
		$outval .= $objRef;
2948
		if ($outbarcode) {
2949
			$outval .= ' ('.$outbarcode.')';
2950
		}
2951
		$outval .= ' - '.dol_trunc($label, $maxlengtharticle);
2952
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2953
			$outval .= ' ('.getCountry($outorigin, 1).')';
2954
		}
2955
2956
		// Units
2957
		$opt .= $outvalUnits;
2958
		$outval .= $outvalUnits;
2959
2960
		$found = 0;
2961
2962
		// Multiprice
2963
		// If we need a particular price level (from 1 to n)
2964
		if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
2965
			$sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
2966
			$sql .= " FROM ".$this->db->prefix()."product_price";
2967
			$sql .= " WHERE fk_product = ".((int) $objp->rowid);
2968
			$sql .= " AND entity IN (".getEntity('productprice').")";
2969
			$sql .= " AND price_level = ".((int) $price_level);
2970
			$sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
2971
			$sql .= " LIMIT 1";
2972
2973
			dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
2974
			$result2 = $this->db->query($sql);
2975
			if ($result2) {
2976
				$objp2 = $this->db->fetch_object($result2);
2977
				if ($objp2) {
2978
					$found = 1;
2979
					if ($objp2->price_base_type == 'HT') {
2980
						$opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2981
						$outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2982
					} else {
2983
						$opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2984
						$outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2985
					}
2986
					$outprice_ht = price($objp2->price);
2987
					$outprice_ttc = price($objp2->price_ttc);
2988
					$outpricebasetype = $objp2->price_base_type;
2989
					if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) {  // using this option is a bug. kept for backward compatibility
2990
						$outtva_tx = $objp2->tva_tx;						// We use the vat rate on line of multiprice
2991
						$outdefault_vat_code = $objp2->default_vat_code;	// We use the vat code on line of multiprice
2992
					} else {
2993
						$outtva_tx = $objp->tva_tx;							// We use the vat rate of product, not the one on line of multiprice
2994
						$outdefault_vat_code = $objp->default_vat_code;		// We use the vat code or product, not the one on line of multiprice
2995
					}
2996
				}
2997
			} else {
2998
				dol_print_error($this->db);
2999
			}
3000
		}
3001
3002
		// Price by quantity
3003
		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))) {
3004
			$found = 1;
3005
			$outqty = $objp->quantity;
3006
			$outdiscount = $objp->remise_percent;
3007
			if ($objp->quantity == 1) {
3008
				$opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
3009
				$outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
3010
				$opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3011
				$outval .= $langs->transnoentities("Unit");
3012
			} else {
3013
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3014
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3015
				$opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3016
				$outval .= $langs->transnoentities("Units");
3017
			}
3018
3019
			$outprice_ht = price($objp->unitprice);
3020
			$outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3021
			$outpricebasetype = $objp->price_base_type;
3022
			$outtva_tx = $objp->tva_tx;							// This value is the value on product when constructProductListOption is called by select_produits_list even if other field $objp-> are from table price_by_qty
3023
			$outdefault_vat_code = $objp->default_vat_code;		// This value is the value on product when constructProductListOption is called by select_produits_list even if other field $objp-> are from table price_by_qty
3024
		}
3025
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3026
			$opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3027
			$outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3028
		}
3029
		if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3030
			$opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3031
			$outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3032
		}
3033
3034
		// Price by customer
3035
		if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
3036
			if (!empty($objp->idprodcustprice)) {
3037
				$found = 1;
3038
3039
				if ($objp->custprice_base_type == 'HT') {
3040
					$opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3041
					$outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3042
				} else {
3043
					$opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3044
					$outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3045
				}
3046
3047
				$outprice_ht = price($objp->custprice);
3048
				$outprice_ttc = price($objp->custprice_ttc);
3049
				$outpricebasetype = $objp->custprice_base_type;
3050
				$outtva_tx = $objp->custtva_tx;
3051
				$outdefault_vat_code = $objp->custdefault_vat_code;
3052
			}
3053
		}
3054
3055
		// If level no defined or multiprice not found, we used the default price
3056
		if (empty($hidepriceinlabel) && !$found) {
3057
			if ($objp->price_base_type == 'HT') {
3058
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3059
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3060
			} else {
3061
				$opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3062
				$outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3063
			}
3064
			$outprice_ht = price($objp->price);
3065
			$outprice_ttc = price($objp->price_ttc);
3066
			$outpricebasetype = $objp->price_base_type;
3067
			$outtva_tx = $objp->tva_tx;
3068
			$outdefault_vat_code = $objp->default_vat_code;
3069
		}
3070
3071
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3072
			if (!empty($user->rights->stock->lire)) {
3073
				$opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3074
3075
				if ($objp->stock > 0) {
3076
					$outval .= ' - <span class="product_line_stock_ok">';
3077
				} elseif ($objp->stock <= 0) {
3078
					$outval .= ' - <span class="product_line_stock_too_low">';
3079
				}
3080
				$outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
3081
				$outval .= '</span>';
3082
				if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
3083
					$langs->load("stocks");
3084
3085
					$tmpproduct = new Product($this->db);
3086
					$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3087
					$tmpproduct->load_virtual_stock();
3088
					$virtualstock = $tmpproduct->stock_theorique;
3089
3090
					$opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3091
3092
					$outval .= ' - '.$langs->transnoentities("VirtualStock").':';
3093
					if ($virtualstock > 0) {
3094
						$outval .= '<span class="product_line_stock_ok">';
3095
					} elseif ($virtualstock <= 0) {
3096
						$outval .= '<span class="product_line_stock_too_low">';
3097
					}
3098
					$outval .= $virtualstock;
3099
					$outval .= '</span>';
3100
3101
					unset($tmpproduct);
3102
				}
3103
			}
3104
		}
3105
3106
		$opt .= "</option>\n";
3107
		$optJson = array(
3108
			'key'=>$outkey,
3109
			'value'=>$outref,
3110
			'label'=>$outval,
3111
			'label2'=>$outlabel,
3112
			'desc'=>$outdesc,
3113
			'type'=>$outtype,
3114
			'price_ht'=>price2num($outprice_ht),
3115
			'price_ttc'=>price2num($outprice_ttc),
3116
			'pricebasetype'=>$outpricebasetype,
3117
			'tva_tx'=>$outtva_tx,
3118
			'default_vat_code'=>$outdefault_vat_code,
3119
			'qty'=>$outqty,
3120
			'discount'=>$outdiscount,
3121
			'duration_value'=>$outdurationvalue,
3122
			'duration_unit'=>$outdurationunit,
3123
			'pbq'=>$outpbq,
3124
			'labeltrans'=>$outlabel_translated,
3125
			'desctrans'=>$outdesc_translated,
3126
			'ref_customer'=>$outrefcust
3127
		);
3128
	}
3129
3130
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3131
	/**
3132
	 *	Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list)
3133
	 *
3134
	 *	@param	int		$socid			Id third party
3135
	 *	@param  string	$selected       Preselected product
3136
	 *	@param  string	$htmlname       Name of HTML Select
3137
	 *  @param	string	$filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
3138
	 *	@param  string	$filtre			For a SQL filter
3139
	 *	@param	array	$ajaxoptions	Options for ajax_autocompleter
3140
	 *  @param	int		$hidelabel		Hide label (0=no, 1=yes)
3141
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3142
	 *  @param	string	$morecss		More CSS
3143
	 *  @param	string	$placeholder	Placeholder
3144
	 *	@return	void
3145
	 */
3146
	public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3147
	{
3148
		// phpcs:enable
3149
		global $langs, $conf;
3150
		global $price_level, $status, $finished;
3151
3152
		if (!isset($status)) {
3153
			$status = 1;
3154
		}
3155
3156
		$selected_input_value = '';
3157
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3158
			if ($selected > 0) {
3159
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3160
				$producttmpselect = new Product($this->db);
3161
				$producttmpselect->fetch($selected);
3162
				$selected_input_value = $producttmpselect->ref;
3163
				unset($producttmpselect);
3164
			}
3165
3166
			// mode=2 means suppliers products
3167
			$urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3168
			print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3169
			print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3170
		} else {
3171
			print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3172
		}
3173
	}
3174
3175
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3176
	/**
3177
	 *	Return list of suppliers products
3178
	 *
3179
	 *	@param	int		$socid   			Id of supplier thirdparty (0 = no filter)
3180
	 *	@param  int		$selected       	Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD')
3181
	 *	@param  string	$htmlname       	Name of HTML select
3182
	 *  @param	string	$filtertype     	Filter on product type (''=nofilter, 0=product, 1=service)
3183
	 *	@param  string	$filtre         	Generic filter. Data must not come from user input.
3184
	 *	@param  string	$filterkey      	Filter of produdts
3185
	 *  @param  int		$statut         	-1=Return all products, 0=Products not on buy, 1=Products on buy
3186
	 *  @param  int		$outputmode     	0=HTML select string, 1=Array
3187
	 *  @param  int     $limit          	Limit of line number
3188
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
3189
	 *  @param	string	$morecss			Add more CSS
3190
	 *  @param	int		$showstockinlist	Show stock information (slower).
3191
	 *  @param	string	$placeholder		Placeholder
3192
	 *  @return array           			Array of keys for json
3193
	 */
3194
	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 = '')
3195
	{
3196
		// phpcs:enable
3197
		global $langs, $conf, $user;
3198
		global $hookmanager;
3199
3200
		$out = '';
3201
		$outarray = array();
3202
3203
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3204
3205
		$langs->load('stocks');
3206
		// Units
3207
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3208
			$langs->load('other');
3209
		}
3210
3211
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock,";
3212
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3213
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name,";
3214
		$sql .= " pfp.supplier_reputation";
3215
		// if we use supplier description of the products
3216
		if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3217
			$sql .= " ,pfp.desc_fourn as description";
3218
		} else {
3219
			$sql .= " ,p.description";
3220
		}
3221
		// Units
3222
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3223
			$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";
3224
		}
3225
		if (!empty($conf->barcode->enabled)) {
3226
			$sql .= ", pfp.barcode";
3227
		}
3228
		$sql .= " FROM ".$this->db->prefix()."product as p";
3229
		$sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3230
		if ($socid > 0) {
3231
			$sql .= " AND pfp.fk_soc = ".((int) $socid);
3232
		}
3233
		$sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3234
		// Units
3235
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3236
			$sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
3237
		}
3238
		$sql .= " WHERE p.entity IN (".getEntity('product').")";
3239
		if ($statut != -1) {
3240
			$sql .= " AND p.tobuy = ".((int) $statut);
3241
		}
3242
		if (strval($filtertype) != '') {
3243
			$sql .= " AND p.fk_product_type = ".((int) $filtertype);
3244
		}
3245
		if (!empty($filtre)) {
3246
			$sql .= " ".$filtre;
3247
		}
3248
		// Add where from hooks
3249
		$parameters = array();
3250
		$reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3251
		$sql .= $hookmanager->resPrint;
3252
		// Add criteria on ref/label
3253
		if ($filterkey != '') {
3254
			$sql .= ' AND (';
3255
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3256
			// For natural search
3257
			$scrit = explode(' ', $filterkey);
3258
			$i = 0;
3259
			if (count($scrit) > 1) {
3260
				$sql .= "(";
3261
			}
3262
			foreach ($scrit as $crit) {
3263
				if ($i > 0) {
3264
					$sql .= " AND ";
3265
				}
3266
				$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)."%'";
3267
				if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3268
					$sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3269
				}
3270
				$sql .= ")";
3271
				$i++;
3272
			}
3273
			if (count($scrit) > 1) {
3274
				$sql .= ")";
3275
			}
3276
			if (!empty($conf->barcode->enabled)) {
3277
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3278
				$sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3279
			}
3280
			$sql .= ')';
3281
		}
3282
		$sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3283
		$sql .= $this->db->plimit($limit, 0);
3284
3285
		// Build output string
3286
3287
		dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3288
		$result = $this->db->query($sql);
3289
		if ($result) {
3290
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3291
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3292
3293
			$num = $this->db->num_rows($result);
3294
3295
			//$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';	// remove select to have id same with combo and ajax
3296
			$out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3297
			if (!$selected) {
3298
				$out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3299
			} else {
3300
				$out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3301
			}
3302
3303
			$i = 0;
3304
			while ($i < $num) {
3305
				$objp = $this->db->fetch_object($result);
3306
3307
				$outkey = $objp->idprodfournprice; // id in table of price
3308
				if (!$outkey && $alsoproductwithnosupplierprice) {
3309
					$outkey = 'idprod_'.$objp->rowid; // id of product
3310
				}
3311
3312
				$outref = $objp->ref;
3313
				$outval = '';
3314
				$outbarcode = $objp->barcode;
3315
				$outqty = 1;
3316
				$outdiscount = 0;
3317
				$outtype = $objp->fk_product_type;
3318
				$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3319
				$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3320
3321
				// Units
3322
				$outvalUnits = '';
3323
				if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3324
					if (!empty($objp->unit_short)) {
3325
						$outvalUnits .= ' - '.$objp->unit_short;
3326
					}
3327
					if (!empty($objp->weight) && $objp->weight_units !== null) {
3328
						$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3329
						$outvalUnits .= ' - '.$unitToShow;
3330
					}
3331
					if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3332
						$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3333
						$outvalUnits .= ' - '.$unitToShow;
3334
					}
3335
					if (!empty($objp->surface) && $objp->surface_units !== null) {
3336
						$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3337
						$outvalUnits .= ' - '.$unitToShow;
3338
					}
3339
					if (!empty($objp->volume) && $objp->volume_units !== null) {
3340
						$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3341
						$outvalUnits .= ' - '.$unitToShow;
3342
					}
3343
					if ($outdurationvalue && $outdurationunit) {
3344
						$da = array(
3345
							'h' => $langs->trans('Hour'),
3346
							'd' => $langs->trans('Day'),
3347
							'w' => $langs->trans('Week'),
3348
							'm' => $langs->trans('Month'),
3349
							'y' => $langs->trans('Year')
3350
						);
3351
						if (isset($da[$outdurationunit])) {
3352
							$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3353
						}
3354
					}
3355
				}
3356
3357
				$objRef = $objp->ref;
3358
				if ($filterkey && $filterkey != '') {
3359
					$objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3360
				}
3361
				$objRefFourn = $objp->ref_fourn;
3362
				if ($filterkey && $filterkey != '') {
3363
					$objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3364
				}
3365
				$label = $objp->label;
3366
				if ($filterkey && $filterkey != '') {
3367
					$label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3368
				}
3369
3370
				$optlabel = $objp->ref;
3371
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3372
					$optlabel .= ' <span class="opacitymedium">('.$objp->ref_fourn.')</span>';
3373
				}
3374
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3375
					$optlabel .= ' ('.$outbarcode.')';
3376
				}
3377
				$optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3378
3379
				$outvallabel = $objRef;
3380
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3381
					$outvallabel .= ' ('.$objRefFourn.')';
3382
				}
3383
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3384
					$outvallabel .= ' ('.$outbarcode.')';
3385
				}
3386
				$outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3387
3388
				// Units
3389
				$optlabel .= $outvalUnits;
3390
				$outvallabel .= $outvalUnits;
3391
3392
				if (!empty($objp->idprodfournprice)) {
3393
					$outqty = $objp->quantity;
3394
					$outdiscount = $objp->remise_percent;
3395
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3396
						$prod_supplier = new ProductFournisseur($this->db);
3397
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3398
						$prod_supplier->id = $objp->fk_product;
3399
						$prod_supplier->fourn_qty = $objp->quantity;
3400
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3401
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3402
						$priceparser = new PriceParser($this->db);
3403
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3404
						if ($price_result >= 0) {
3405
							$objp->fprice = $price_result;
3406
							if ($objp->quantity >= 1) {
3407
								$objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3408
							}
3409
						}
3410
					}
3411
					if ($objp->quantity == 1) {
3412
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3413
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3414
						$optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3415
						$outvallabel .= $langs->transnoentities("Unit");
3416
					} else {
3417
						$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;
3418
						$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;
3419
						$optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3420
						$outvallabel .= ' '.$langs->transnoentities("Units");
3421
					}
3422
3423
					if ($objp->quantity > 1) {
3424
						$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
3425
						$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
3426
					}
3427
					if ($objp->remise_percent >= 1) {
3428
						$optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3429
						$outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3430
					}
3431
					if ($objp->duration) {
3432
						$optlabel .= " - ".$objp->duration;
3433
						$outvallabel .= " - ".$objp->duration;
3434
					}
3435
					if (!$socid) {
3436
						$optlabel .= " - ".dol_trunc($objp->name, 8);
3437
						$outvallabel .= " - ".dol_trunc($objp->name, 8);
3438
					}
3439
					if ($objp->supplier_reputation) {
3440
						//TODO dictionary
3441
						$reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3442
3443
						$optlabel .= " - ".$reputations[$objp->supplier_reputation];
3444
						$outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3445
					}
3446
				} else {
3447
					if (empty($alsoproductwithnosupplierprice)) {     // No supplier price defined for couple product/supplier
3448
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3449
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3450
					} else // No supplier price defined for product, even on other suppliers
3451
					{
3452
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3453
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3454
					}
3455
				}
3456
3457
				if (!empty($conf->stock->enabled) && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3458
					$novirtualstock = ($showstockinlist == 2);
3459
3460
					if (!empty($user->rights->stock->lire)) {
3461
						$outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3462
3463
						if ($objp->stock > 0) {
3464
							$optlabel .= ' - <span class="product_line_stock_ok">';
3465
						} elseif ($objp->stock <= 0) {
3466
							$optlabel .= ' - <span class="product_line_stock_too_low">';
3467
						}
3468
						$optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3469
						$optlabel .= '</span>';
3470
						if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) {  // Warning, this option may slow down combo list generation
3471
							$langs->load("stocks");
3472
3473
							$tmpproduct = new Product($this->db);
3474
							$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3475
							$tmpproduct->load_virtual_stock();
3476
							$virtualstock = $tmpproduct->stock_theorique;
3477
3478
							$outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3479
3480
							$optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3481
							if ($virtualstock > 0) {
3482
								$optlabel .= '<span class="product_line_stock_ok">';
3483
							} elseif ($virtualstock <= 0) {
3484
								$optlabel .= '<span class="product_line_stock_too_low">';
3485
							}
3486
							$optlabel .= $virtualstock;
3487
							$optlabel .= '</span>';
3488
3489
							unset($tmpproduct);
3490
						}
3491
					}
3492
				}
3493
3494
				$opt = '<option value="'.$outkey.'"';
3495
				if ($selected && $selected == $objp->idprodfournprice) {
3496
					$opt .= ' selected';
3497
				}
3498
				if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3499
					$opt .= ' disabled';
3500
				}
3501
				if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3502
					$opt .= ' data-product-id="'.$objp->rowid.'" data-price-id="'.$objp->idprodfournprice.'" data-qty="'.$objp->quantity.'" data-up="'.$objp->unitprice.'" data-discount="'.$outdiscount.'"';
3503
				}
3504
				$opt .= ' data-description="'.dol_escape_htmltag($objp->description, 0, 1).'"';
3505
				$opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"';
3506
				$opt .= '>';
3507
3508
				$opt .= $optlabel;
3509
				$outval .= $outvallabel;
3510
3511
				$opt .= "</option>\n";
3512
3513
				// Add new entry
3514
				// "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
3515
				// "label" value of json key array is used by jQuery automatically as text for combo box
3516
				$out .= $opt;
3517
				array_push(
3518
					$outarray,
3519
					array('key'=>$outkey,
3520
						'value'=>$outref,
3521
						'label'=>$outval,
3522
						'qty'=>$outqty,
3523
						'price_qty_ht'=>price2num($objp->fprice, 'MU'),	// Keep higher resolution for price for the min qty
3524
						'price_unit_ht'=>price2num($objp->unitprice, 'MU'),	// This is used to fill the Unit Price
3525
						'price_ht'=>price2num($objp->unitprice, 'MU'),		// This is used to fill the Unit Price (for compatibility)
3526
						'tva_tx'=>$objp->tva_tx,
3527
						'default_vat_code'=>$objp->default_vat_code,
3528
						'discount'=>$outdiscount,
3529
						'type'=>$outtype,
3530
						'duration_value'=>$outdurationvalue,
3531
						'duration_unit'=>$outdurationunit,
3532
						'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3533
						'description'=>$objp->description
3534
					)
3535
				);
3536
				// Exemple of var_dump $outarray
3537
				// array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3538
				//           ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3539
				//      	 ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3540
				//}
3541
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3542
				//$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3543
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3544
3545
				$i++;
3546
			}
3547
			$out .= '</select>';
3548
3549
			$this->db->free($result);
3550
3551
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3552
			$out .= ajax_combobox($htmlname);
3553
3554
			if (empty($outputmode)) {
3555
				return $out;
3556
			}
3557
			return $outarray;
3558
		} else {
3559
			dol_print_error($this->db);
3560
		}
3561
	}
3562
3563
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3564
	/**
3565
	 *	Return list of suppliers prices for a product
3566
	 *
3567
	 *  @param	    int		$productid       	Id of product
3568
	 *  @param      string	$htmlname        	Name of HTML field
3569
	 *  @param      int		$selected_supplier  Pre-selected supplier if more than 1 result
3570
	 *  @return	    string
3571
	 */
3572
	public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3573
	{
3574
		// phpcs:enable
3575
		global $langs, $conf;
3576
3577
		$langs->load('stocks');
3578
3579
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3580
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3581
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3582
		$sql .= " FROM ".$this->db->prefix()."product as p";
3583
		$sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3584
		$sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3585
		$sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3586
		$sql .= " AND p.tobuy = 1";
3587
		$sql .= " AND s.fournisseur = 1";
3588
		$sql .= " AND p.rowid = ".((int) $productid);
3589
		$sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3590
3591
		dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3592
		$result = $this->db->query($sql);
3593
3594
		if ($result) {
3595
			$num = $this->db->num_rows($result);
3596
3597
			$form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3598
3599
			if (!$num) {
3600
				$form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3601
			} else {
3602
				require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3603
				$form .= '<option value="0">&nbsp;</option>';
3604
3605
				$i = 0;
3606
				while ($i < $num) {
3607
					$objp = $this->db->fetch_object($result);
3608
3609
					$opt = '<option value="'.$objp->idprodfournprice.'"';
3610
					//if there is only one supplier, preselect it
3611
					if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier)) {
3612
						$opt .= ' selected';
3613
					}
3614
					$opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3615
3616
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3617
						$prod_supplier = new ProductFournisseur($this->db);
3618
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3619
						$prod_supplier->id = $productid;
3620
						$prod_supplier->fourn_qty = $objp->quantity;
3621
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3622
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3623
						$priceparser = new PriceParser($this->db);
3624
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3625
						if ($price_result >= 0) {
3626
							$objp->fprice = $price_result;
3627
							if ($objp->quantity >= 1) {
3628
								$objp->unitprice = $objp->fprice / $objp->quantity;
3629
							}
3630
						}
3631
					}
3632
					if ($objp->quantity == 1) {
3633
						$opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3634
					}
3635
3636
					$opt .= $objp->quantity.' ';
3637
3638
					if ($objp->quantity == 1) {
3639
						$opt .= $langs->trans("Unit");
3640
					} else {
3641
						$opt .= $langs->trans("Units");
3642
					}
3643
					if ($objp->quantity > 1) {
3644
						$opt .= " - ";
3645
						$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");
3646
					}
3647
					if ($objp->duration) {
3648
						$opt .= " - ".$objp->duration;
3649
					}
3650
					$opt .= "</option>\n";
3651
3652
					$form .= $opt;
3653
					$i++;
3654
				}
3655
			}
3656
3657
			$form .= '</select>';
3658
			$this->db->free($result);
3659
			return $form;
3660
		} else {
3661
			dol_print_error($this->db);
3662
		}
3663
	}
3664
3665
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3666
	/**
3667
	 *    Return list of delivery address
3668
	 *
3669
	 *    @param    string	$selected          	Id contact pre-selectionn
3670
	 *    @param    int		$socid				Id of company
3671
	 *    @param    string	$htmlname          	Name of HTML field
3672
	 *    @param    int		$showempty         	Add an empty field
3673
	 *    @return	integer|null
3674
	 */
3675
	public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3676
	{
3677
		// phpcs:enable
3678
		// looking for users
3679
		$sql = "SELECT a.rowid, a.label";
3680
		$sql .= " FROM ".$this->db->prefix()."societe_address as a";
3681
		$sql .= " WHERE a.fk_soc = ".((int) $socid);
3682
		$sql .= " ORDER BY a.label ASC";
3683
3684
		dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3685
		$resql = $this->db->query($sql);
3686
		if ($resql) {
3687
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3688
			if ($showempty) {
3689
				print '<option value="0">&nbsp;</option>';
3690
			}
3691
			$num = $this->db->num_rows($resql);
3692
			$i = 0;
3693
			if ($num) {
3694
				while ($i < $num) {
3695
					$obj = $this->db->fetch_object($resql);
3696
3697
					if ($selected && $selected == $obj->rowid) {
3698
						print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3699
					} else {
3700
						print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3701
					}
3702
					$i++;
3703
				}
3704
			}
3705
			print '</select>';
3706
			return $num;
3707
		} else {
3708
			dol_print_error($this->db);
3709
		}
3710
	}
3711
3712
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3713
	/**
3714
	 *      Load into cache list of payment terms
3715
	 *
3716
	 *      @return     int             Nb of lines loaded, <0 if KO
3717
	 */
3718
	public function load_cache_conditions_paiements()
3719
	{
3720
		// phpcs:enable
3721
		global $langs;
3722
3723
		$num = count($this->cache_conditions_paiements);
3724
		if ($num > 0) {
3725
			return 0; // Cache already loaded
3726
		}
3727
3728
		dol_syslog(__METHOD__, LOG_DEBUG);
3729
3730
		$sql = "SELECT rowid, code, libelle as label";
3731
		$sql .= " FROM ".$this->db->prefix().'c_payment_term';
3732
		$sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3733
		$sql .= " AND active > 0";
3734
		$sql .= " ORDER BY sortorder";
3735
3736
		$resql = $this->db->query($sql);
3737
		if ($resql) {
3738
			$num = $this->db->num_rows($resql);
3739
			$i = 0;
3740
			while ($i < $num) {
3741
				$obj = $this->db->fetch_object($resql);
3742
3743
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3744
				$label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3745
				$this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3746
				$this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3747
				$i++;
3748
			}
3749
3750
			//$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1);		// We use the field sortorder of table
3751
3752
			return $num;
3753
		} else {
3754
			dol_print_error($this->db);
3755
			return -1;
3756
		}
3757
	}
3758
3759
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3760
	/**
3761
	 *      Load int a cache property th elist of possible delivery delays.
3762
	 *
3763
	 *      @return     int             Nb of lines loaded, <0 if KO
3764
	 */
3765
	public function load_cache_availability()
3766
	{
3767
		// phpcs:enable
3768
		global $langs;
3769
3770
		$num = count($this->cache_availability);	// TODO Use $conf->cache['availability'] instead of $this->cache_availability
3771
		if ($num > 0) {
3772
			return 0; // Cache already loaded
3773
		}
3774
3775
		dol_syslog(__METHOD__, LOG_DEBUG);
3776
3777
		$langs->load('propal');
3778
3779
		$sql = "SELECT rowid, code, label, position";
3780
		$sql .= " FROM ".$this->db->prefix().'c_availability';
3781
		$sql .= " WHERE active > 0";
3782
3783
		$resql = $this->db->query($sql);
3784
		if ($resql) {
3785
			$num = $this->db->num_rows($resql);
3786
			$i = 0;
3787
			while ($i < $num) {
3788
				$obj = $this->db->fetch_object($resql);
3789
3790
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3791
				$label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3792
				$this->cache_availability[$obj->rowid]['code'] = $obj->code;
3793
				$this->cache_availability[$obj->rowid]['label'] = $label;
3794
				$this->cache_availability[$obj->rowid]['position'] = $obj->position;
3795
				$i++;
3796
			}
3797
3798
			$this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3799
3800
			return $num;
3801
		} else {
3802
			dol_print_error($this->db);
3803
			return -1;
3804
		}
3805
	}
3806
3807
	/**
3808
	 *      Retourne la liste des types de delais de livraison possibles
3809
	 *
3810
	 *      @param	int		$selected       Id du type de delais pre-selectionne
3811
	 *      @param  string	$htmlname       Nom de la zone select
3812
	 *      @param  string	$filtertype     To add a filter
3813
	 *		@param	int		$addempty		Add empty entry
3814
	 * 		@param	string	$morecss		More CSS
3815
	 *		@return	void
3816
	 */
3817
	public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3818
	{
3819
		global $langs, $user;
3820
3821
		$this->load_cache_availability();
3822
3823
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3824
3825
		print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3826
		if ($addempty) {
3827
			print '<option value="0">&nbsp;</option>';
3828
		}
3829
		foreach ($this->cache_availability as $id => $arrayavailability) {
3830
			if ($selected == $id) {
3831
				print '<option value="'.$id.'" selected>';
3832
			} else {
3833
				print '<option value="'.$id.'">';
3834
			}
3835
			print dol_escape_htmltag($arrayavailability['label']);
3836
			print '</option>';
3837
		}
3838
		print '</select>';
3839
		if ($user->admin) {
3840
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3841
		}
3842
		print ajax_combobox($htmlname);
3843
	}
3844
3845
	/**
3846
	 *      Load into cache cache_demand_reason, array of input reasons
3847
	 *
3848
	 *      @return     int             Nb of lines loaded, <0 if KO
3849
	 */
3850
	public function loadCacheInputReason()
3851
	{
3852
		global $langs;
3853
3854
		$num = count($this->cache_demand_reason);	// TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
3855
		if ($num > 0) {
3856
			return 0; // Cache already loaded
3857
		}
3858
3859
		$sql = "SELECT rowid, code, label";
3860
		$sql .= " FROM ".$this->db->prefix().'c_input_reason';
3861
		$sql .= " WHERE active > 0";
3862
3863
		$resql = $this->db->query($sql);
3864
		if ($resql) {
3865
			$num = $this->db->num_rows($resql);
3866
			$i = 0;
3867
			$tmparray = array();
3868
			while ($i < $num) {
3869
				$obj = $this->db->fetch_object($resql);
3870
3871
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3872
				$label = ($obj->label != '-' ? $obj->label : '');
3873
				if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
3874
					$label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3875
				}
3876
				if ($langs->trans($obj->code) != $obj->code) {
3877
					$label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3878
				}
3879
3880
				$tmparray[$obj->rowid]['id']   = $obj->rowid;
3881
				$tmparray[$obj->rowid]['code'] = $obj->code;
3882
				$tmparray[$obj->rowid]['label'] = $label;
3883
				$i++;
3884
			}
3885
3886
			$this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
3887
3888
			unset($tmparray);
3889
			return $num;
3890
		} else {
3891
			dol_print_error($this->db);
3892
			return -1;
3893
		}
3894
	}
3895
3896
	/**
3897
	 *	Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
3898
	 *  List found into table c_input_reason loaded by loadCacheInputReason
3899
	 *
3900
	 *  @param	int		$selected        Id or code of type origin to select by default
3901
	 *  @param  string	$htmlname        Nom de la zone select
3902
	 *  @param  string	$exclude         To exclude a code value (Example: SRC_PROP)
3903
	 *	@param	int		$addempty		 Add an empty entry
3904
	 *  @param  string	$morecss		 Add more css to the HTML select component
3905
	 *  @param	int		$notooltip		 Do not show the tooltip for admin
3906
	 *	@return	void
3907
	 */
3908
	public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
3909
	{
3910
		global $langs, $user;
3911
3912
		$this->loadCacheInputReason();
3913
3914
		print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3915
		if ($addempty) {
3916
			print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
3917
		}
3918
		foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
3919
			if ($arraydemandreason['code'] == $exclude) {
3920
				continue;
3921
			}
3922
3923
			if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
3924
				print '<option value="'.$arraydemandreason['id'].'" selected>';
3925
			} else {
3926
				print '<option value="'.$arraydemandreason['id'].'">';
3927
			}
3928
			$label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
3929
			print $langs->trans($label);
3930
			print '</option>';
3931
		}
3932
		print '</select>';
3933
		if ($user->admin && empty($notooltip)) {
3934
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3935
		}
3936
		print ajax_combobox('select_'.$htmlname);
3937
	}
3938
3939
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3940
	/**
3941
	 *      Charge dans cache la liste des types de paiements possibles
3942
	 *
3943
	 *      @return     int                 Nb of lines loaded, <0 if KO
3944
	 */
3945
	public function load_cache_types_paiements()
3946
	{
3947
		// phpcs:enable
3948
		global $langs;
3949
3950
		$num = count($this->cache_types_paiements);		// TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
3951
		if ($num > 0) {
3952
			return $num; // Cache already loaded
3953
		}
3954
3955
		dol_syslog(__METHOD__, LOG_DEBUG);
3956
3957
		$this->cache_types_paiements = array();
3958
3959
		$sql = "SELECT id, code, libelle as label, type, active";
3960
		$sql .= " FROM ".$this->db->prefix()."c_paiement";
3961
		$sql .= " WHERE entity IN (".getEntity('c_paiement').")";
3962
3963
		$resql = $this->db->query($sql);
3964
		if ($resql) {
3965
			$num = $this->db->num_rows($resql);
3966
			$i = 0;
3967
			while ($i < $num) {
3968
				$obj = $this->db->fetch_object($resql);
3969
3970
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3971
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3972
				$this->cache_types_paiements[$obj->id]['id'] = $obj->id;
3973
				$this->cache_types_paiements[$obj->id]['code'] = $obj->code;
3974
				$this->cache_types_paiements[$obj->id]['label'] = $label;
3975
				$this->cache_types_paiements[$obj->id]['type'] = $obj->type;
3976
				$this->cache_types_paiements[$obj->id]['active'] = $obj->active;
3977
				$i++;
3978
			}
3979
3980
			$this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
3981
3982
			return $num;
3983
		} else {
3984
			dol_print_error($this->db);
3985
			return -1;
3986
		}
3987
	}
3988
3989
3990
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3991
	/**
3992
	 *      print list of payment modes.
3993
	 *      Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want.
3994
	 *      See instead to force the default value by the caller.
3995
	 *
3996
	 *      @param	int		$selected		Id of payment term to preselect by default
3997
	 *      @param	string	$htmlname		Nom de la zone select
3998
	 *      @param	int		$filtertype		Not used
3999
	 *		@param	int		$addempty		Add an empty entry
4000
	 * 		@param	int		$noinfoadmin	0=Add admin info, 1=Disable admin info
4001
	 * 		@param	string	$morecss		Add more CSS on select tag
4002
	 *		@return	void
4003
	 */
4004
	public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '')
4005
	{
4006
		// phpcs:enable
4007
		print $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getSelectConditio...$noinfoadmin, $morecss) targeting Form::getSelectConditionsPaiements() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
4008
	}
4009
4010
4011
	/**
4012
	 *      Return list of payment modes.
4013
	 *      Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want.
4014
	 *      See instead to force the default value by the caller.
4015
	 *
4016
	 *      @param	int		$selected		Id of payment term to preselect by default
4017
	 *      @param	string	$htmlname		Nom de la zone select
4018
	 *      @param	int		$filtertype		Not used
4019
	 *		@param	int		$addempty		Add an empty entry
4020
	 * 		@param	int		$noinfoadmin	0=Add admin info, 1=Disable admin info
4021
	 * 		@param	string	$morecss		Add more CSS on select tag
4022
	 *		@return	void
4023
	 */
4024
	public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '')
4025
	{
4026
4027
		global $langs, $user, $conf;
4028
		$out = '';
4029
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
4030
4031
		$this->load_cache_conditions_paiements();
4032
4033
		// Set default value if not already set by caller
4034
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
4035
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
4036
		}
4037
4038
		$out.=  '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4039
		if ($addempty) {
4040
			$out.=  '<option value="0">&nbsp;</option>';
4041
		}
4042
		foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4043
			if ($selected == $id) {
4044
				$out.=  '<option value="'.$id.'" selected>';
4045
			} else {
4046
				$out.=  '<option value="'.$id.'">';
4047
			}
4048
			$out.=  $arrayconditions['label'];
4049
			$out.=  '</option>';
4050
		}
4051
		$out.=  '</select>';
4052
		if ($user->admin && empty($noinfoadmin)) {
4053
			$out.=  info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4054
		}
4055
		$out.=  ajax_combobox($htmlname);
4056
		return $out;
4057
	}
4058
4059
4060
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4061
	/**
4062
	 *      Return list of payment methods
4063
	 *      Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want.
4064
	 *
4065
	 *      @param	string	$selected       Id or code or preselected payment mode
4066
	 *      @param  string	$htmlname       Name of select field
4067
	 *      @param  string	$filtertype     To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
4068
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
4069
	 *      @param  int		$empty			1=can be empty, 0 otherwise
4070
	 * 		@param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
4071
	 *      @param  int		$maxlength      Max length of label
4072
	 *      @param  int     $active         Active or not, -1 = all
4073
	 *      @param  string  $morecss        Add more CSS on select tag
4074
	 *      @param	int		$nooutput		1=Return string, do not send to output
4075
	 * 		@return	void
4076
	 */
4077
	public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4078
	{
4079
		// phpcs:enable
4080
		global $langs, $user, $conf;
4081
4082
		$out = '';
4083
4084
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
4085
4086
		$filterarray = array();
4087
		if ($filtertype == 'CRDT') {
4088
			$filterarray = array(0, 2, 3);
4089
		} elseif ($filtertype == 'DBIT') {
4090
			$filterarray = array(1, 2, 3);
4091
		} elseif ($filtertype != '' && $filtertype != '-1') {
4092
			$filterarray = explode(',', $filtertype);
4093
		}
4094
4095
		$this->load_cache_types_paiements();
4096
4097
		// Set default value if not already set by caller
4098
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
4099
			$selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
4100
		}
4101
4102
		$out .= '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4103
		if ($empty) {
4104
			$out .= '<option value="">&nbsp;</option>';
4105
		}
4106
		foreach ($this->cache_types_paiements as $id => $arraytypes) {
4107
			// If not good status
4108
			if ($active >= 0 && $arraytypes['active'] != $active) {
4109
				continue;
4110
			}
4111
4112
			// On passe si on a demande de filtrer sur des modes de paiments particuliers
4113
			if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4114
				continue;
4115
			}
4116
4117
			// We discard empty line if showempty is on because an empty line has already been output.
4118
			if ($empty && empty($arraytypes['code'])) {
4119
				continue;
4120
			}
4121
4122
			if ($format == 0) {
4123
				$out .= '<option value="'.$id.'"';
4124
			} elseif ($format == 1) {
4125
				$out .= '<option value="'.$arraytypes['code'].'"';
4126
			} elseif ($format == 2) {
4127
				$out .= '<option value="'.$arraytypes['code'].'"';
4128
			} elseif ($format == 3) {
4129
				$out .= '<option value="'.$id.'"';
4130
			}
4131
			// Print attribute selected or not
4132
			if ($format == 1 || $format == 2) {
4133
				if ($selected == $arraytypes['code']) {
4134
					$out .= ' selected';
4135
				}
4136
			} else {
4137
				if ($selected == $id) {
4138
					$out .= ' selected';
4139
				}
4140
			}
4141
			$out .= '>';
4142
			if ($format == 0) {
4143
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4144
			} elseif ($format == 1) {
4145
				$value = $arraytypes['code'];
4146
			} elseif ($format == 2) {
4147
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4148
			} elseif ($format == 3) {
4149
				$value = $arraytypes['code'];
4150
			}
4151
			$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...
4152
			$out .= '</option>';
4153
		}
4154
		$out .= '</select>';
4155
		if ($user->admin && !$noadmininfo) {
4156
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4157
		}
4158
		$out .= ajax_combobox('select'.$htmlname);
4159
4160
		if (empty($nooutput)) {
4161
			print $out;
4162
		} else {
4163
			return $out;
4164
		}
4165
	}
4166
4167
4168
	/**
4169
	 *  Selection HT or TTC
4170
	 *
4171
	 *  @param	string	$selected       Id pre-selectionne
4172
	 *  @param  string	$htmlname       Nom de la zone select
4173
	 *  @param	string	$addjscombo		Add js combo
4174
	 * 	@return	string					Code of HTML select to chose tax or not
4175
	 */
4176
	public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4177
	{
4178
		global $langs;
4179
4180
		$return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4181
		$options = array(
4182
			'HT'=>$langs->trans("HT"),
4183
			'TTC'=>$langs->trans("TTC")
4184
		);
4185
		foreach ($options as $id => $value) {
4186
			if ($selected == $id) {
4187
				$return .= '<option value="'.$id.'" selected>'.$value;
4188
			} else {
4189
				$return .= '<option value="'.$id.'">'.$value;
4190
			}
4191
			$return .= '</option>';
4192
		}
4193
		$return .= '</select>';
4194
		if ($addjscombo) {
4195
			$return .= ajax_combobox('select_'.$htmlname);
4196
		}
4197
4198
		return $return;
4199
	}
4200
4201
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4202
	/**
4203
	 *      Load in cache list of transport mode
4204
	 *
4205
	 *      @return     int                 Nb of lines loaded, <0 if KO
4206
	 */
4207
	public function load_cache_transport_mode()
4208
	{
4209
		// phpcs:enable
4210
		global $langs;
4211
4212
		$num = count($this->cache_transport_mode);		// TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4213
		if ($num > 0) {
4214
			return $num; // Cache already loaded
4215
		}
4216
4217
		dol_syslog(__METHOD__, LOG_DEBUG);
4218
4219
		$this->cache_transport_mode = array();
4220
4221
		$sql = "SELECT rowid, code, label, active";
4222
		$sql .= " FROM ".$this->db->prefix()."c_transport_mode";
4223
		$sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4224
4225
		$resql = $this->db->query($sql);
4226
		if ($resql) {
4227
			$num = $this->db->num_rows($resql);
4228
			$i = 0;
4229
			while ($i < $num) {
4230
				$obj = $this->db->fetch_object($resql);
4231
4232
				// If traduction exist, we use it else we take the default label
4233
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4234
				$this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4235
				$this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4236
				$this->cache_transport_mode[$obj->rowid]['label'] = $label;
4237
				$this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4238
				$i++;
4239
			}
4240
4241
			$this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4242
4243
			return $num;
4244
		} else {
4245
			dol_print_error($this->db);
4246
			return -1;
4247
		}
4248
	}
4249
4250
	/**
4251
	 *      Return list of transport mode for intracomm report
4252
	 *
4253
	 *      @param	string	$selected       Id of the transport mode pre-selected
4254
	 *      @param  string	$htmlname       Name of the select field
4255
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
4256
	 *      @param  int		$empty			1=can be empty, 0 else
4257
	 *      @param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
4258
	 *      @param  int		$maxlength      Max length of label
4259
	 *      @param  int     $active         Active or not, -1 = all
4260
	 *      @param  string  $morecss        Add more CSS on select tag
4261
	 * 		@return	void
4262
	 */
4263
	public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4264
	{
4265
		global $langs, $user;
4266
4267
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4268
4269
		$this->load_cache_transport_mode();
4270
4271
		print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4272
		if ($empty) {
4273
			print '<option value="">&nbsp;</option>';
4274
		}
4275
		foreach ($this->cache_transport_mode as $id => $arraytypes) {
4276
			// If not good status
4277
			if ($active >= 0 && $arraytypes['active'] != $active) {
4278
				continue;
4279
			}
4280
4281
			// We discard empty line if showempty is on because an empty line has already been output.
4282
			if ($empty && empty($arraytypes['code'])) {
4283
				continue;
4284
			}
4285
4286
			if ($format == 0) {
4287
				print '<option value="'.$id.'"';
4288
			} elseif ($format == 1) {
4289
				print '<option value="'.$arraytypes['code'].'"';
4290
			} elseif ($format == 2) {
4291
				print '<option value="'.$arraytypes['code'].'"';
4292
			} elseif ($format == 3) {
4293
				print '<option value="'.$id.'"';
4294
			}
4295
			// If text is selected, we compare with code, else with id
4296
			if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4297
				print ' selected';
4298
			} elseif ($selected == $id) {
4299
				print ' selected';
4300
			}
4301
			print '>';
4302
			if ($format == 0) {
4303
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4304
			} elseif ($format == 1) {
4305
				$value = $arraytypes['code'];
4306
			} elseif ($format == 2) {
4307
				$value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4308
			} elseif ($format == 3) {
4309
				$value = $arraytypes['code'];
4310
			}
4311
			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...
4312
			print '</option>';
4313
		}
4314
		print '</select>';
4315
		if ($user->admin && !$noadmininfo) {
4316
			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4317
		}
4318
	}
4319
4320
	/**
4321
	 *  Return a HTML select list of shipping mode
4322
	 *
4323
	 *  @param	string	$selected           Id shipping mode pre-selected
4324
	 *  @param  string	$htmlname           Name of select zone
4325
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4326
	 *  @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.
4327
	 *  @param  string	$moreattrib         To add more attribute on select
4328
	 *	@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
4329
	 *  @param	string	$morecss			More CSS
4330
	 * 	@return	void
4331
	 */
4332
	public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4333
	{
4334
		global $langs, $conf, $user;
4335
4336
		$langs->load("admin");
4337
		$langs->load("deliveries");
4338
4339
		$sql = "SELECT rowid, code, libelle as label";
4340
		$sql .= " FROM ".$this->db->prefix()."c_shipment_mode";
4341
		$sql .= " WHERE active > 0";
4342
		if ($filtre) {
4343
			$sql .= " AND ".$filtre;
4344
		}
4345
		$sql .= " ORDER BY libelle ASC";
4346
4347
		dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4348
		$result = $this->db->query($sql);
4349
		if ($result) {
4350
			$num = $this->db->num_rows($result);
4351
			$i = 0;
4352
			if ($num) {
4353
				print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4354
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4355
					print '<option value="-1">&nbsp;</option>';
4356
				}
4357
				while ($i < $num) {
4358
					$obj = $this->db->fetch_object($result);
4359
					if ($selected == $obj->rowid) {
4360
						print '<option value="'.$obj->rowid.'" selected>';
4361
					} else {
4362
						print '<option value="'.$obj->rowid.'">';
4363
					}
4364
					print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4365
					print '</option>';
4366
					$i++;
4367
				}
4368
				print "</select>";
4369
				if ($user->admin  && empty($noinfoadmin)) {
4370
					print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4371
				}
4372
4373
				print ajax_combobox('select'.$htmlname);
4374
			} else {
4375
				print $langs->trans("NoShippingMethodDefined");
4376
			}
4377
		} else {
4378
			dol_print_error($this->db);
4379
		}
4380
	}
4381
4382
	/**
4383
	 *    Display form to select shipping mode
4384
	 *
4385
	 *    @param	string	$page        Page
4386
	 *    @param    int		$selected    Id of shipping mode
4387
	 *    @param    string	$htmlname    Name of select html field
4388
	 *    @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.
4389
	 *    @return	void
4390
	 */
4391
	public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4392
	{
4393
		global $langs;
4394
4395
		$langs->load("deliveries");
4396
4397
		if ($htmlname != "none") {
4398
			print '<form method="POST" action="'.$page.'">';
4399
			print '<input type="hidden" name="action" value="setshippingmethod">';
4400
			print '<input type="hidden" name="token" value="'.newToken().'">';
4401
			$this->selectShippingMethod($selected, $htmlname, '', $addempty);
4402
			print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4403
			print '</form>';
4404
		} else {
4405
			if ($selected) {
4406
				$code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4407
				print $langs->trans("SendingMethod".strtoupper($code));
4408
			} else {
4409
				print "&nbsp;";
4410
			}
4411
		}
4412
	}
4413
4414
	/**
4415
	 * Creates HTML last in cycle situation invoices selector
4416
	 *
4417
	 * @param     string  $selected   		Preselected ID
4418
	 * @param     int     $socid      		Company ID
4419
	 *
4420
	 * @return    string                     HTML select
4421
	 */
4422
	public function selectSituationInvoices($selected = '', $socid = 0)
4423
	{
4424
		global $langs;
4425
4426
		$langs->load('bills');
4427
4428
		$opt = '<option value="" selected></option>';
4429
		$sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4430
		$sql .= ' FROM '.$this->db->prefix().'facture';
4431
		$sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4432
		$sql .= ' AND situation_counter >= 1';
4433
		$sql .= ' AND fk_soc = '.(int) $socid;
4434
		$sql .= ' AND type <> 2';
4435
		$sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4436
		$resql = $this->db->query($sql);
4437
4438
		if ($resql && $this->db->num_rows($resql) > 0) {
4439
			// Last seen cycle
4440
			$ref = 0;
4441
			while ($obj = $this->db->fetch_object($resql)) {
4442
				//Same cycle ?
4443
				if ($obj->situation_cycle_ref != $ref) {
4444
					// Just seen this cycle
4445
					$ref = $obj->situation_cycle_ref;
4446
					//not final ?
4447
					if ($obj->situation_final != 1) {
4448
						//Not prov?
4449
						if (substr($obj->ref, 1, 4) != 'PROV') {
4450
							if ($selected == $obj->rowid) {
4451
								$opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4452
							} else {
4453
								$opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4454
							}
4455
						}
4456
					}
4457
				}
4458
			}
4459
		} else {
4460
				dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4461
		}
4462
		if ($opt == '<option value ="" selected></option>') {
4463
			$opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4464
		}
4465
		return $opt;
4466
	}
4467
4468
	/**
4469
	 *      Creates HTML units selector (code => label)
4470
	 *
4471
	 *      @param	string	$selected       Preselected Unit ID
4472
	 *      @param  string	$htmlname       Select name
4473
	 *      @param	int		$showempty		Add a nempty line
4474
	 *      @param  string  $unit_type      Restrict to one given unit type
4475
	 * 		@return	string                  HTML select
4476
	 */
4477
	public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4478
	{
4479
		global $langs;
4480
4481
		$langs->load('products');
4482
4483
		$return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4484
4485
		$sql = "SELECT rowid, label, code FROM ".$this->db->prefix()."c_units";
4486
		$sql .= ' WHERE active > 0';
4487
		if (!empty($unit_type)) {
4488
			$sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4489
		}
4490
		$sql .= " ORDER BY sortorder";
4491
4492
		$resql = $this->db->query($sql);
4493
		if ($resql && $this->db->num_rows($resql) > 0) {
4494
			if ($showempty) {
4495
				$return .= '<option value="none"></option>';
4496
			}
4497
4498
			while ($res = $this->db->fetch_object($resql)) {
4499
				$unitLabel = $res->label;
4500
				if (!empty($langs->tab_translate['unit'.$res->code])) {	// check if Translation is available before
4501
					$unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4502
				}
4503
4504
				if ($selected == $res->rowid) {
4505
					$return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4506
				} else {
4507
					$return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4508
				}
4509
			}
4510
			$return .= '</select>';
4511
		}
4512
		return $return;
4513
	}
4514
4515
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4516
	/**
4517
	 *  Return a HTML select list of bank accounts
4518
	 *
4519
	 *  @param	string	$selected           Id account pre-selected
4520
	 *  @param  string	$htmlname           Name of select zone
4521
	 *  @param  int		$status             Status of searched accounts (0=open, 1=closed, 2=both)
4522
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4523
	 *  @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.
4524
	 *  @param  string	$moreattrib         To add more attribute on select
4525
	 *  @param	int		$showcurrency		Show currency in label
4526
	 *  @param	string	$morecss			More CSS
4527
	 *  @param	int		$nooutput			1=Return string, do not send to output
4528
	 * 	@return	int							<0 if error, Num of bank account found if OK (0, 1, 2, ...)
4529
	 */
4530
	public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4531
	{
4532
		// phpcs:enable
4533
		global $langs, $conf;
4534
4535
		$out = '';
4536
4537
		$langs->load("admin");
4538
		$num = 0;
4539
4540
		$sql = "SELECT rowid, label, bank, clos as status, currency_code";
4541
		$sql .= " FROM ".$this->db->prefix()."bank_account";
4542
		$sql .= " WHERE entity IN (".getEntity('bank_account').")";
4543
		if ($status != 2) {
4544
			$sql .= " AND clos = ".(int) $status;
4545
		}
4546
		if ($filtre) {
4547
			$sql .= " AND ".$filtre;
4548
		}
4549
		$sql .= " ORDER BY label";
4550
4551
		dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4552
		$result = $this->db->query($sql);
4553
		if ($result) {
4554
			$num = $this->db->num_rows($result);
4555
			$i = 0;
4556
			if ($num) {
4557
				$out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4558
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4559
					$out .= '<option value="-1">&nbsp;</option>';
4560
				}
4561
4562
				while ($i < $num) {
4563
					$obj = $this->db->fetch_object($result);
4564
					if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4565
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4566
					} else {
4567
						$out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4568
					}
4569
					$out .= trim($obj->label);
4570
					if ($showcurrency) {
4571
						$out .= ' ('.$obj->currency_code.')';
4572
					}
4573
					if ($status == 2 && $obj->status == 1) {
4574
						$out .= ' ('.$langs->trans("Closed").')';
4575
					}
4576
					$out .= '</option>';
4577
					$i++;
4578
				}
4579
				$out .= "</select>";
4580
				$out .= ajax_combobox('select'.$htmlname);
4581
			} else {
4582
				if ($status == 0) {
4583
					$out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4584
				} else {
4585
					$out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4586
				}
4587
			}
4588
		} else {
4589
			dol_print_error($this->db);
4590
		}
4591
4592
		// Output or return
4593
		if (empty($nooutput)) {
4594
			print $out;
4595
		} else {
4596
			return $out;
4597
		}
4598
4599
		return $num;
4600
	}
4601
4602
	/**
4603
	 *  Return a HTML select list of establishment
4604
	 *
4605
	 *  @param	string	$selected           Id establishment pre-selected
4606
	 *  @param  string	$htmlname           Name of select zone
4607
	 *  @param  int		$status             Status of searched establishment (0=open, 1=closed, 2=both)
4608
	 *  @param  string	$filtre             To filter list. This parameter must not come from input of users
4609
	 *  @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.
4610
	 *  @param  string	$moreattrib         To add more attribute on select
4611
	 * 	@return	int							<0 if error, Num of establishment found if OK (0, 1, 2, ...)
4612
	 */
4613
	public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4614
	{
4615
		global $langs, $conf;
4616
4617
		$langs->load("admin");
4618
		$num = 0;
4619
4620
		$sql = "SELECT rowid, name, fk_country, status, entity";
4621
		$sql .= " FROM ".$this->db->prefix()."establishment";
4622
		$sql .= " WHERE 1=1";
4623
		if ($status != 2) {
4624
			$sql .= " AND status = ".(int) $status;
4625
		}
4626
		if ($filtre) {
4627
			$sql .= " AND ".$filtre;
4628
		}
4629
		$sql .= " ORDER BY name";
4630
4631
		dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4632
		$result = $this->db->query($sql);
4633
		if ($result) {
4634
			$num = $this->db->num_rows($result);
4635
			$i = 0;
4636
			if ($num) {
4637
				print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4638
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4639
					print '<option value="-1">&nbsp;</option>';
4640
				}
4641
4642
				while ($i < $num) {
4643
					$obj = $this->db->fetch_object($result);
4644
					if ($selected == $obj->rowid) {
4645
						print '<option value="'.$obj->rowid.'" selected>';
4646
					} else {
4647
						print '<option value="'.$obj->rowid.'">';
4648
					}
4649
					print trim($obj->name);
4650
					if ($status == 2 && $obj->status == 1) {
4651
						print ' ('.$langs->trans("Closed").')';
4652
					}
4653
					print '</option>';
4654
					$i++;
4655
				}
4656
				print "</select>";
4657
			} else {
4658
				if ($status == 0) {
4659
					print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4660
				} else {
4661
					print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4662
				}
4663
			}
4664
		} else {
4665
			dol_print_error($this->db);
4666
		}
4667
	}
4668
4669
	/**
4670
	 *    Display form to select bank account
4671
	 *
4672
	 *    @param	string	$page        Page
4673
	 *    @param    int		$selected    Id of bank account
4674
	 *    @param    string	$htmlname    Name of select html field
4675
	 *    @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.
4676
	 *    @return	void
4677
	 */
4678
	public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4679
	{
4680
		global $langs;
4681
		if ($htmlname != "none") {
4682
			print '<form method="POST" action="'.$page.'">';
4683
			print '<input type="hidden" name="action" value="setbankaccount">';
4684
			print '<input type="hidden" name="token" value="'.newToken().'">';
4685
			print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4686
			$nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4687
			if ($nbaccountfound > 0) {
4688
				print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4689
			}
4690
			print '</form>';
4691
		} else {
4692
			$langs->load('banks');
4693
4694
			if ($selected) {
4695
				require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4696
				$bankstatic = new Account($this->db);
4697
				$result = $bankstatic->fetch($selected);
4698
				if ($result) {
4699
					print $bankstatic->getNomUrl(1);
4700
				}
4701
			} else {
4702
				print "&nbsp;";
4703
			}
4704
		}
4705
	}
4706
4707
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4708
	/**
4709
	 *    Return list of categories having choosed type
4710
	 *
4711
	 *    @param	string|int	            $type				Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
4712
	 *    @param    string		            $selected    		Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1.
4713
	 *    @param    string		            $htmlname			HTML field name
4714
	 *    @param    int			            $maxlength      	Maximum length for labels
4715
	 *    @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.
4716
	 *                                                          $markafterid can be an :
4717
	 *                                                          - int (id of category)
4718
	 *                                                          - string (categories ids seprated by comma)
4719
	 *                                                          - array (list of categories ids)
4720
	 *    @param	int			            $outputmode			0=HTML select string, 1=Array
4721
	 *    @param	int			            $include			[=0] Removed or 1=Keep only
4722
	 *    @param	string					$morecss			More CSS
4723
	 *    @return	string
4724
	 *    @see select_categories()
4725
	 */
4726
	public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4727
	{
4728
		// phpcs:enable
4729
		global $conf, $langs;
4730
		$langs->load("categories");
4731
4732
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4733
4734
		// For backward compatibility
4735
		if (is_numeric($type)) {
4736
			dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4737
		}
4738
4739
		if ($type === Categorie::TYPE_BANK_LINE) {
4740
			// TODO Move this into common category feature
4741
			$cate_arbo = array();
4742
			$sql = "SELECT c.label, c.rowid";
4743
			$sql .= " FROM ".$this->db->prefix()."bank_categ as c";
4744
			$sql .= " WHERE entity = ".$conf->entity;
4745
			$sql .= " ORDER BY c.label";
4746
			$result = $this->db->query($sql);
4747
			if ($result) {
4748
				$num = $this->db->num_rows($result);
4749
				$i = 0;
4750
				while ($i < $num) {
4751
					$objp = $this->db->fetch_object($result);
4752
					if ($objp) {
4753
						$cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label);
4754
					}
4755
					$i++;
4756
				}
4757
				$this->db->free($result);
4758
			} else {
4759
				dol_print_error($this->db);
4760
			}
4761
		} else {
4762
			$cat = new Categorie($this->db);
4763
			$cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4764
		}
4765
4766
		$output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4767
		$outarray = array();
4768
		if (is_array($cate_arbo)) {
4769
			if (!count($cate_arbo)) {
4770
				$output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4771
			} else {
4772
				$output .= '<option value="-1">&nbsp;</option>';
4773
				foreach ($cate_arbo as $key => $value) {
4774
					if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4775
						$add = 'selected ';
4776
					} else {
4777
						$add = '';
4778
					}
4779
					$output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'">'.dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle').'</option>';
4780
4781
					$outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4782
				}
4783
			}
4784
		}
4785
		$output .= '</select>';
4786
		$output .= "\n";
4787
4788
		if ($outputmode) {
4789
			return $outarray;
4790
		}
4791
		return $output;
4792
	}
4793
4794
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4795
	/**
4796
	 *     Show a confirmation HTML form or AJAX popup
4797
	 *
4798
	 *     @param	string		$page        	   	Url of page to call if confirmation is OK
4799
	 *     @param	string		$title       	   	Title
4800
	 *     @param	string		$question    	   	Question
4801
	 *     @param 	string		$action      	   	Action
4802
	 *	   @param	array		$formquestion	   	An array with forms complementary inputs
4803
	 * 	   @param	string		$selectedchoice		"" or "no" or "yes"
4804
	 * 	   @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
4805
	 *     @param	int			$height          	Force height of box
4806
	 *     @param	int			$width				Force width of box
4807
	 *     @return 	void
4808
	 *     @deprecated
4809
	 *     @see formconfirm()
4810
	 */
4811
	public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4812
	{
4813
		// phpcs:enable
4814
		dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4815
		print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4816
	}
4817
4818
	/**
4819
	 *     Show a confirmation HTML form or AJAX popup.
4820
	 *     Easiest way to use this is with useajax=1.
4821
	 *     If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters)
4822
	 *     just after calling this method. For example:
4823
	 *       print '<script type="text/javascript">'."\n";
4824
	 *       print 'jQuery(document).ready(function() {'."\n";
4825
	 *       print 'jQuery(".xxxlink").click(function(e) { jQuery("#aparamid").val(jQuery(this).attr("rel")); jQuery("#dialog-confirm-xxx").dialog("open"); return false; });'."\n";
4826
	 *       print '});'."\n";
4827
	 *       print '</script>'."\n";
4828
	 *
4829
	 *     @param  	string			$page        	   	Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated)
4830
	 *     @param	string			$title       	   	Title
4831
	 *     @param	string			$question    	   	Question
4832
	 *     @param 	string			$action      	   	Action
4833
	 *	   @param  	array|string	$formquestion	   	An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>))
4834
	 *													type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', 'other' or 'onecolumn'...
4835
	 * 	   @param  	string			$selectedchoice  	'' or 'no', or 'yes' or '1' or '0'
4836
	 * 	   @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
4837
	 *     @param  	int|string		$height          	Force height of box (0 = auto)
4838
	 *     @param	int				$width				Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones.
4839
	 *     @param	int				$disableformtag		1=Disable form tag. Can be used if we are already inside a <form> section.
4840
	 *     @return 	string      		    			HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form
4841
	 */
4842
	public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0)
4843
	{
4844
		global $langs, $conf;
4845
4846
		$more = '<!-- formconfirm before calling page='.dol_escape_htmltag($page).' -->';
4847
		$formconfirm = '';
4848
		$inputok = array();
4849
		$inputko = array();
4850
4851
		// Clean parameters
4852
		$newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
4853
		if ($conf->browser->layout == 'phone') {
4854
			$width = '95%';
4855
		}
4856
4857
		// Set height automatically if not defined
4858
		if (empty($height)) {
4859
			$height = 220;
4860
			if (is_array($formquestion) && count($formquestion) > 2) {
4861
				$height += ((count($formquestion) - 2) * 24);
4862
			}
4863
		}
4864
4865
		if (is_array($formquestion) && !empty($formquestion)) {
4866
			// First add hidden fields and value
4867
			foreach ($formquestion as $key => $input) {
4868
				if (is_array($input) && !empty($input)) {
4869
					if ($input['type'] == 'hidden') {
4870
						$more .= '<input type="hidden" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'" value="'.dol_escape_htmltag($input['value']).'">'."\n";
4871
					}
4872
				}
4873
			}
4874
4875
			// Now add questions
4876
			$moreonecolumn = '';
4877
			$more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
4878
			foreach ($formquestion as $key => $input) {
4879
				if (is_array($input) && !empty($input)) {
4880
					$size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : '');	// deprecated. Use morecss instead.
4881
					$moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4882
					$morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4883
4884
					if ($input['type'] == 'text') {
4885
						$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";
4886
					} elseif ($input['type'] == 'password')	{
4887
						$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";
4888
					} elseif ($input['type'] == 'textarea') {
4889
						/*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
4890
						$more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
4891
						$more .= $input['value'];
4892
						$more .= '</textarea>';
4893
						$more .= '</div></div>'."\n";*/
4894
						$moreonecolumn .= '<div class="margintoponly">';
4895
						$moreonecolumn .= $input['label'].'<br>';
4896
						$moreonecolumn .= '<textarea name="'.dol_escape_htmltag($input['name']).'" id="'.dol_escape_htmltag($input['name']).'" class="'.$morecss.'"'.$moreattr.'>';
4897
						$moreonecolumn .= $input['value'];
4898
						$moreonecolumn .= '</textarea>';
4899
						$moreonecolumn .= '</div>';
4900
					} elseif ($input['type'] == 'select') {
4901
						if (empty($morecss)) {
4902
							$morecss = 'minwidth100';
4903
						}
4904
4905
						$show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
4906
						$key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
4907
						$value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
4908
						$translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
4909
						$maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
4910
						$disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
4911
						$sort = isset($input['select_sort']) ? $input['select_sort'] : '';
4912
4913
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4914
						if (!empty($input['label'])) {
4915
							$more .= $input['label'].'</div><div class="tagtd left">';
4916
						}
4917
						$more .= $this->selectarray($input['name'], $input['values'], $input['default'], $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
4918
						$more .= '</div></div>'."\n";
4919
					} elseif ($input['type'] == 'checkbox') {
4920
						$more .= '<div class="tagtr">';
4921
						$more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
4922
						$more .= '<input type="checkbox" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$moreattr;
4923
						if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
4924
							$more .= ' checked';
4925
						}
4926
						if (is_bool($input['value']) && $input['value']) {
4927
							$more .= ' checked';
4928
						}
4929
						if (isset($input['disabled'])) {
4930
							$more .= ' disabled';
4931
						}
4932
						$more .= ' /></div>';
4933
						$more .= '</div>'."\n";
4934
					} elseif ($input['type'] == 'radio') {
4935
						$i = 0;
4936
						foreach ($input['values'] as $selkey => $selval) {
4937
							$more .= '<div class="tagtr">';
4938
							if ($i == 0) {
4939
								$more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
4940
							} else {
4941
								$more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
4942
							}
4943
							$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;
4944
							if ($input['disabled']) {
4945
								$more .= ' disabled';
4946
							}
4947
							if (isset($input['default']) && $input['default'] === $selkey) {
4948
								$more .= ' checked="checked"';
4949
							}
4950
							$more .= ' /> ';
4951
							$more .= '<label for="'.dol_escape_htmltag($input['name'].$selkey).'">'.$selval.'</label>';
4952
							$more .= '</div></div>'."\n";
4953
							$i++;
4954
						}
4955
					} elseif ($input['type'] == 'date') {
4956
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
4957
						$more .= '<div class="tagtd">';
4958
						$addnowlink = (empty($input['datenow']) ? 0 : 1);
4959
						$more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, $addnowlink);
4960
						$more .= '</div></div>'."\n";
4961
						$formquestion[] = array('name'=>$input['name'].'day');
4962
						$formquestion[] = array('name'=>$input['name'].'month');
4963
						$formquestion[] = array('name'=>$input['name'].'year');
4964
						$formquestion[] = array('name'=>$input['name'].'hour');
4965
						$formquestion[] = array('name'=>$input['name'].'min');
4966
					} elseif ($input['type'] == 'other') {
4967
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4968
						if (!empty($input['label'])) {
4969
							$more .= $input['label'].'</div><div class="tagtd">';
4970
						}
4971
						$more .= $input['value'];
4972
						$more .= '</div></div>'."\n";
4973
					} elseif ($input['type'] == 'onecolumn') {
4974
						$moreonecolumn .= '<div class="margintoponly">';
4975
						$moreonecolumn .= $input['value'];
4976
						$moreonecolumn .= '</div>'."\n";
4977
					} elseif ($input['type'] == 'hidden') {
4978
						// Do nothing more, already added by a previous loop
4979
					} elseif ($input['type'] == 'separator') {
4980
						$more .= '<br>';
4981
					} else {
4982
						$more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
4983
					}
4984
				}
4985
			}
4986
			$more .= '</div>'."\n";
4987
			$more .= $moreonecolumn;
4988
		}
4989
4990
		// JQUERY method dialog is broken with smartphone, we use standard HTML.
4991
		// 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
4992
		// See page product/card.php for example
4993
		if (!empty($conf->dol_use_jmobile)) {
4994
			$useajax = 0;
4995
		}
4996
		if (empty($conf->use_javascript_ajax)) {
4997
			$useajax = 0;
4998
		}
4999
5000
		if ($useajax) {
5001
			$autoOpen = true;
5002
			$dialogconfirm = 'dialog-confirm';
5003
			$button = '';
5004
			if (!is_numeric($useajax)) {
5005
				$button = $useajax;
5006
				$useajax = 1;
5007
				$autoOpen = false;
5008
				$dialogconfirm .= '-'.$button;
5009
			}
5010
			$pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes';
5011
			$pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : '');
5012
5013
			// Add input fields into list of fields to read during submit (inputok and inputko)
5014
			if (is_array($formquestion)) {
5015
				foreach ($formquestion as $key => $input) {
5016
					//print "xx ".$key." rr ".is_array($input)."<br>\n";
5017
					// Add name of fields to propagate with the GET when submitting the form with button OK.
5018
					if (is_array($input) && isset($input['name'])) {
5019
						if (strpos($input['name'], ',') > 0) {
5020
							$inputok = array_merge($inputok, explode(',', $input['name']));
5021
						} else {
5022
							array_push($inputok, $input['name']);
5023
						}
5024
					}
5025
					// Add name of fields to propagate with the GET when submitting the form with button KO.
5026
					if (isset($input['inputko']) && $input['inputko'] == 1) {
5027
						array_push($inputko, $input['name']);
5028
					}
5029
				}
5030
			}
5031
5032
			// Show JQuery confirm box.
5033
			$formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
5034
			if (is_array($formquestion) && !empty($formquestion['text'])) {
5035
				$formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
5036
			}
5037
			if (!empty($more)) {
5038
				$formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
5039
			}
5040
			$formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
5041
			$formconfirm .= '</div>'."\n";
5042
5043
			$formconfirm .= "\n<!-- begin code of popup for formconfirm page=".$page." -->\n";
5044
			$formconfirm .= '<script type="text/javascript">'."\n";
5045
			$formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5046
			$formconfirm .= 'jQuery(document).ready(function() {
5047
            $(function() {
5048
            	$( "#'.$dialogconfirm.'" ).dialog(
5049
            	{
5050
                    autoOpen: '.($autoOpen ? "true" : "false").',';
5051
			if ($newselectedchoice == 'no') {
5052
				$formconfirm .= '
5053
						open: function() {
5054
            				$(this).parent().find("button.ui-button:eq(2)").focus();
5055
						},';
5056
			}
5057
			$formconfirm .= '
5058
                    resizable: false,
5059
                    height: "'.$height.'",
5060
                    width: "'.$width.'",
5061
                    modal: true,
5062
                    closeOnEscape: false,
5063
                    buttons: {
5064
                        "'.dol_escape_js($langs->transnoentities("Yes")).'": function() {
5065
                        	var options = "&token='.urlencode(newToken()).'";
5066
                        	var inputok = '.json_encode($inputok).';	/* List of fields into form */
5067
                         	var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
5068
                         	if (inputok.length>0) {
5069
                         		$.each(inputok, function(i, inputname) {
5070
                         			var more = "";
5071
									var inputvalue;
5072
                         			if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5073
										inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5074
									} else {
5075
                         		    	if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5076
                         				inputvalue = $("#" + inputname + more).val();
5077
									}
5078
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
5079
									console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5080
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5081
                         		});
5082
                         	}
5083
                         	var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "") + options;
5084
            				if (pageyes.length > 0) { location.href = urljump; }
5085
                            $(this).dialog("close");
5086
                        },
5087
                        "'.dol_escape_js($langs->transnoentities("No")).'": function() {
5088
                        	var options = "&token='.urlencode(newToken()).'";
5089
                         	var inputko = '.json_encode($inputko).';	/* List of fields into form */
5090
                         	var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
5091
                         	if (inputko.length>0) {
5092
                         		$.each(inputko, function(i, inputname) {
5093
                         			var more = "";
5094
                         			if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5095
                         			var inputvalue = $("#" + inputname + more).val();
5096
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
5097
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5098
                         		});
5099
                         	}
5100
                         	var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "") + options;
5101
                         	//alert(urljump);
5102
            				if (pageno.length > 0) { location.href = urljump; }
5103
                            $(this).dialog("close");
5104
                        }
5105
                    }
5106
                }
5107
                );
5108
5109
            	var button = "'.$button.'";
5110
            	if (button.length > 0) {
5111
                	$( "#" + button ).click(function() {
5112
                		$("#'.$dialogconfirm.'").dialog("open");
5113
        			});
5114
                }
5115
            });
5116
            });
5117
            </script>';
5118
			$formconfirm .= "<!-- end ajax formconfirm -->\n";
5119
		} else {
5120
			$formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
5121
5122
			if (empty($disableformtag)) {
5123
				$formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
5124
			}
5125
5126
			$formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
5127
			$formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
5128
5129
			$formconfirm .= '<table class="valid centpercent">'."\n";
5130
5131
			// Line title
5132
			$formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5133
			$formconfirm .= img_picto('', 'recent').' '.$title;
5134
			$formconfirm .= '</td></tr>'."\n";
5135
5136
			// Line text
5137
			if (is_array($formquestion) && !empty($formquestion['text'])) {
5138
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
5139
			}
5140
5141
			// Line form fields
5142
			if ($more) {
5143
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
5144
				$formconfirm .= $more;
5145
				$formconfirm .= '</td></tr>'."\n";
5146
			}
5147
5148
			// Line with question
5149
			$formconfirm .= '<tr class="valid">';
5150
			$formconfirm .= '<td class="valid">'.$question.'</td>';
5151
			$formconfirm .= '<td class="valid center">';
5152
			$formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly');
5153
			$formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="'.$langs->trans("Validate").'">';
5154
			$formconfirm .= '</td>';
5155
			$formconfirm .= '</tr>'."\n";
5156
5157
			$formconfirm .= '</table>'."\n";
5158
5159
			if (empty($disableformtag)) {
5160
				$formconfirm .= "</form>\n";
5161
			}
5162
			$formconfirm .= '<br>';
5163
5164
			if (!empty($conf->use_javascript_ajax)) {
5165
				$formconfirm .= '<!-- code to disable button to avoid double clic -->';
5166
				$formconfirm .= '<script type="text/javascript">'."\n";
5167
				$formconfirm .= '
5168
				$(document).ready(function () {
5169
					$(".confirmvalidatebutton").on("click", function() {
5170
						console.log("We click on button");
5171
						$(this).attr("disabled", "disabled");
5172
						setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5173
						//console.log($(this).closest("form"));
5174
						$(this).closest("form").submit();
5175
					});
5176
				});
5177
				';
5178
				$formconfirm .= '</script>'."\n";
5179
			}
5180
5181
			$formconfirm .= "<!-- end formconfirm -->\n";
5182
		}
5183
5184
		return $formconfirm;
5185
	}
5186
5187
5188
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5189
	/**
5190
	 *    Show a form to select a project
5191
	 *
5192
	 *    @param	int		$page        		Page
5193
	 *    @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)
5194
	 *    @param    int		$selected    		Id pre-selected project
5195
	 *    @param    string	$htmlname    		Name of select field
5196
	 *    @param	int		$discard_closed		Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable)
5197
	 *    @param	int		$maxlength			Max length
5198
	 *    @param	int		$forcefocus			Force focus on field (works with javascript only)
5199
	 *    @param    int     $nooutput           No print is done. String is returned.
5200
	 *    @return	string                      Return html content
5201
	 */
5202
	public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0)
5203
	{
5204
		// phpcs:enable
5205
		global $langs;
5206
5207
		require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5208
		require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5209
5210
		$out = '';
5211
5212
		$formproject = new FormProjets($this->db);
5213
5214
		$langs->load("project");
5215
		if ($htmlname != "none") {
5216
			$out .= "\n";
5217
			$out .= '<form method="post" action="'.$page.'">';
5218
			$out .= '<input type="hidden" name="action" value="classin">';
5219
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5220
			$out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1);
5221
			$out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5222
			$out .= '</form>';
5223
		} else {
5224
			$out .= '<span class="project_head_block">';
5225
			if ($selected) {
5226
				$projet = new Project($this->db);
5227
				$projet->fetch($selected);
5228
				$out .= $projet->getNomUrl(1, '', 1);
5229
			} else {
5230
				$out .= "&nbsp;";
5231
			}
5232
			$out .= '</span>';
5233
		}
5234
5235
		if (empty($nooutput)) {
5236
			print $out;
5237
			return '';
5238
		}
5239
		return $out;
5240
	}
5241
5242
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5243
	/**
5244
	 *	Show a form to select payment conditions
5245
	 *
5246
	 *  @param	int		$page        	Page
5247
	 *  @param  string	$selected    	Id condition pre-selectionne
5248
	 *  @param  string	$htmlname    	Name of select html field
5249
	 *	@param	int		$addempty		Add empty entry
5250
	 *  @param	string	$type			Type ('direct-debit' or 'bank-transfer')
5251
	 *  @return	void
5252
	 */
5253
	public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '')
5254
	{
5255
		// phpcs:enable
5256
		global $langs;
5257
		if ($htmlname != "none") {
5258
			print '<form method="POST" action="'.$page.'">';
5259
			print '<input type="hidden" name="action" value="setconditions">';
5260
			print '<input type="hidden" name="token" value="'.newToken().'">';
5261
			if ($type) {
5262
				print '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5263
			}
5264
			$this->select_conditions_paiements($selected, $htmlname, -1, $addempty, 0, '');
5265
			print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5266
			print '</form>';
5267
		} else {
5268
			if ($selected) {
5269
				$this->load_cache_conditions_paiements();
5270
				if (isset($this->cache_conditions_paiements[$selected])) {
5271
					print $this->cache_conditions_paiements[$selected]['label'];
5272
				} else {
5273
					$langs->load('errors');
5274
					print $langs->trans('ErrorNotInDictionaryPaymentConditions');
5275
				}
5276
			} else {
5277
				print "&nbsp;";
5278
			}
5279
		}
5280
	}
5281
5282
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5283
	/**
5284
	 *  Show a form to select a delivery delay
5285
	 *
5286
	 *  @param  int		$page        	Page
5287
	 *  @param  string	$selected    	Id condition pre-selectionne
5288
	 *  @param  string	$htmlname    	Name of select html field
5289
	 *	@param	int		$addempty		Ajoute entree vide
5290
	 *  @return	void
5291
	 */
5292
	public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5293
	{
5294
		// phpcs:enable
5295
		global $langs;
5296
		if ($htmlname != "none") {
5297
			print '<form method="post" action="'.$page.'">';
5298
			print '<input type="hidden" name="action" value="setavailability">';
5299
			print '<input type="hidden" name="token" value="'.newToken().'">';
5300
			$this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5301
			print '<input type="submit" name="modify" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5302
			print '<input type="submit" name="cancel" class="button smallpaddingimp" value="'.$langs->trans("Cancel").'">';
5303
			print '</form>';
5304
		} else {
5305
			if ($selected) {
5306
				$this->load_cache_availability();
5307
				print $this->cache_availability[$selected]['label'];
5308
			} else {
5309
				print "&nbsp;";
5310
			}
5311
		}
5312
	}
5313
5314
	/**
5315
	 *  Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
5316
	 *  List found into table c_input_reason loaded by loadCacheInputReason
5317
	 *
5318
	 *  @param  string	$page        	Page
5319
	 *  @param  string	$selected    	Id condition pre-selectionne
5320
	 *  @param  string	$htmlname    	Name of select html field
5321
	 *  @param	int		$addempty		Add empty entry
5322
	 *  @return	void
5323
	 */
5324
	public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $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="setdemandreason">';
5330
			print '<input type="hidden" name="token" value="'.newToken().'">';
5331
			$this->selectInputReason($selected, $htmlname, -1, $addempty);
5332
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5333
			print '</form>';
5334
		} else {
5335
			if ($selected) {
5336
				$this->loadCacheInputReason();
5337
				foreach ($this->cache_demand_reason as $key => $val) {
5338
					if ($val['id'] == $selected) {
5339
						print $val['label'];
5340
						break;
5341
					}
5342
				}
5343
			} else {
5344
				print "&nbsp;";
5345
			}
5346
		}
5347
	}
5348
5349
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5350
	/**
5351
	 *    Show a form + html select a date
5352
	 *
5353
	 *    @param	string		$page        	Page
5354
	 *    @param	string		$selected    	Date preselected
5355
	 *    @param    string		$htmlname    	Html name of date input fields or 'none'
5356
	 *    @param    int			$displayhour 	Display hour selector
5357
	 *    @param    int			$displaymin		Display minutes selector
5358
	 *    @param	int			$nooutput		1=No print output, return string
5359
	 *    @param	string		$type			'direct-debit' or 'bank-transfer'
5360
	 *    @return	string
5361
	 *    @see		selectDate()
5362
	 */
5363
	public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
5364
	{
5365
		// phpcs:enable
5366
		global $langs;
5367
5368
		$ret = '';
5369
5370
		if ($htmlname != "none") {
5371
			$ret .= '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5372
			$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5373
			$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5374
			if ($type) {
5375
				$ret .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5376
			}
5377
			$ret .= '<table class="nobordernopadding">';
5378
			$ret .= '<tr><td>';
5379
			$ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5380
			$ret .= '</td>';
5381
			$ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5382
			$ret .= '</tr></table></form>';
5383
		} else {
5384
			if ($displayhour) {
5385
				$ret .= dol_print_date($selected, 'dayhour');
5386
			} else {
5387
				$ret .= dol_print_date($selected, 'day');
5388
			}
5389
		}
5390
5391
		if (empty($nooutput)) {
5392
			print $ret;
5393
		}
5394
		return $ret;
5395
	}
5396
5397
5398
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5399
	/**
5400
	 *  Show a select form to choose a user
5401
	 *
5402
	 *  @param	string	$page        	Page
5403
	 *  @param  string	$selected    	Id of user preselected
5404
	 *  @param  string	$htmlname    	Name of input html field. If 'none', we just output the user link.
5405
	 *  @param  array	$exclude		List of users id to exclude
5406
	 *  @param  array	$include        List of users id to include
5407
	 *  @return	void
5408
	 */
5409
	public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5410
	{
5411
		// phpcs:enable
5412
		global $langs;
5413
5414
		if ($htmlname != "none") {
5415
			print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5416
			print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5417
			print '<input type="hidden" name="token" value="'.newToken().'">';
5418
			print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5419
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5420
			print '</form>';
5421
		} else {
5422
			if ($selected) {
5423
				require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5424
				$theuser = new User($this->db);
5425
				$theuser->fetch($selected);
5426
				print $theuser->getNomUrl(1);
5427
			} else {
5428
				print "&nbsp;";
5429
			}
5430
		}
5431
	}
5432
5433
5434
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5435
	/**
5436
	 *    Show form with payment mode
5437
	 *
5438
	 *    @param	string	$page        	Page
5439
	 *    @param    int		$selected    	Id mode pre-selectionne
5440
	 *    @param    string	$htmlname    	Name of select html field
5441
	 *    @param  	string	$filtertype		To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
5442
	 *    @param    int     $active         Active or not, -1 = all
5443
	 *    @param   	int     $addempty       1=Add empty entry
5444
	 *    @param	string	$type			Type ('direct-debit' or 'bank-transfer')
5445
	 *    @return	void
5446
	 */
5447
	public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '')
5448
	{
5449
		// phpcs:enable
5450
		global $langs;
5451
		if ($htmlname != "none") {
5452
			print '<form method="POST" action="'.$page.'">';
5453
			print '<input type="hidden" name="action" value="setmode">';
5454
			print '<input type="hidden" name="token" value="'.newToken().'">';
5455
			if ($type) {
5456
				print '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5457
			}
5458
			print $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->select_types_paie..., 0, 0, $active, '', 1) targeting Form::select_types_paiements() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
5459
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5460
			print '</form>';
5461
		} else {
5462
			if ($selected) {
5463
				$this->load_cache_types_paiements();
5464
				print $this->cache_types_paiements[$selected]['label'];
5465
			} else {
5466
				print "&nbsp;";
5467
			}
5468
		}
5469
	}
5470
5471
	/**
5472
	 *    Show form with transport mode
5473
	 *
5474
	 *    @param	string	$page        	Page
5475
	 *    @param    int		$selected    	Id mode pre-select
5476
	 *    @param    string	$htmlname    	Name of select html field
5477
	 *    @param    int     $active         Active or not, -1 = all
5478
	 *    @param    int     $addempty       1=Add empty entry
5479
	 *    @return	void
5480
	 */
5481
	public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5482
	{
5483
		global $langs;
5484
		if ($htmlname != "none") {
5485
			print '<form method="POST" action="'.$page.'">';
5486
			print '<input type="hidden" name="action" value="settransportmode">';
5487
			print '<input type="hidden" name="token" value="'.newToken().'">';
5488
			$this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5489
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5490
			print '</form>';
5491
		} else {
5492
			if ($selected) {
5493
				$this->load_cache_transport_mode();
5494
				print $this->cache_transport_mode[$selected]['label'];
5495
			} else {
5496
				print "&nbsp;";
5497
			}
5498
		}
5499
	}
5500
5501
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5502
	/**
5503
	 *    Show form with multicurrency code
5504
	 *
5505
	 *    @param	string	$page        	Page
5506
	 *    @param    string	$selected    	code pre-selectionne
5507
	 *    @param    string	$htmlname    	Name of select html field
5508
	 *    @return	void
5509
	 */
5510
	public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5511
	{
5512
		// phpcs:enable
5513
		global $langs;
5514
		if ($htmlname != "none") {
5515
			print '<form method="POST" action="'.$page.'">';
5516
			print '<input type="hidden" name="action" value="setmulticurrencycode">';
5517
			print '<input type="hidden" name="token" value="'.newToken().'">';
5518
			print $this->selectMultiCurrency($selected, $htmlname, 0);
5519
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5520
			print '</form>';
5521
		} else {
5522
			dol_include_once('/core/lib/company.lib.php');
5523
			print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5524
		}
5525
	}
5526
5527
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5528
	/**
5529
	 *    Show form with multicurrency rate
5530
	 *
5531
	 *    @param	string	$page        	Page
5532
	 *    @param    double	$rate	    	Current rate
5533
	 *    @param    string	$htmlname    	Name of select html field
5534
	 *    @param    string  $currency       Currency code to explain the rate
5535
	 *    @return	void
5536
	 */
5537
	public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5538
	{
5539
		// phpcs:enable
5540
		global $langs, $mysoc, $conf;
5541
5542
		if ($htmlname != "none") {
5543
			print '<form method="POST" action="'.$page.'">';
5544
			print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5545
			print '<input type="hidden" name="token" value="'.newToken().'">';
5546
			print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5547
			print '<select name="calculation_mode">';
5548
			print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5549
			print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5550
			print '</select> ';
5551
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5552
			print '</form>';
5553
		} else {
5554
			if (!empty($rate)) {
5555
				print price($rate, 1, $langs, 1, 0);
5556
				if ($currency && $rate != 1) {
5557
					print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5558
				}
5559
			} else {
5560
				print 1;
5561
			}
5562
		}
5563
	}
5564
5565
5566
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5567
	/**
5568
	 *	Show a select box with available absolute discounts
5569
	 *
5570
	 *  @param  string	$page        	Page URL where form is shown
5571
	 *  @param  int		$selected    	Value pre-selected
5572
	 *	@param  string	$htmlname    	Name of SELECT component. If 'none', not changeable. Example 'remise_id'.
5573
	 *	@param	int		$socid			Third party id
5574
	 * 	@param	float	$amount			Total amount available
5575
	 * 	@param	string	$filter			SQL filter on discounts
5576
	 * 	@param	int		$maxvalue		Max value for lines that can be selected
5577
	 *  @param  string	$more           More string to add
5578
	 *  @param  int     $hidelist       1=Hide list
5579
	 *  @param	int		$discount_type	0 => customer discount, 1 => supplier discount
5580
	 *  @return	void
5581
	 */
5582
	public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5583
	{
5584
		// phpcs:enable
5585
		global $conf, $langs;
5586
		if ($htmlname != "none") {
5587
			print '<form method="post" action="'.$page.'">';
5588
			print '<input type="hidden" name="action" value="setabsolutediscount">';
5589
			print '<input type="hidden" name="token" value="'.newToken().'">';
5590
			print '<div class="inline-block">';
5591
			if (!empty($discount_type)) {
5592
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5593
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5594
						$translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5595
					} else {
5596
						$translationKey = 'HasCreditNoteFromSupplier';
5597
					}
5598
				} else {
5599
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5600
						$translationKey = 'HasAbsoluteDiscountFromSupplier';
5601
					} else {
5602
						$translationKey = 'HasCreditNoteFromSupplier';
5603
					}
5604
				}
5605
			} else {
5606
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5607
					if (!$filter || $filter == "fk_facture_source IS NULL") {
5608
						$translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5609
					} else {
5610
						$translationKey = 'CompanyHasCreditNote';
5611
					}
5612
				} else {
5613
					if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5614
						$translationKey = 'CompanyHasAbsoluteDiscount';
5615
					} else {
5616
						$translationKey = 'CompanyHasCreditNote';
5617
					}
5618
				}
5619
			}
5620
			print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5621
			if (empty($hidelist)) {
5622
				print ' ';
5623
			}
5624
			print '</div>';
5625
			if (empty($hidelist)) {
5626
				print '<div class="inline-block" style="padding-right: 10px">';
5627
				$newfilter = 'discount_type='.intval($discount_type);
5628
				if (!empty($discount_type)) {
5629
					$newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5630
				} else {
5631
					$newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5632
				}
5633
				if ($filter) {
5634
					$newfilter .= ' AND ('.$filter.')';
5635
				}
5636
				$nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5637
				if ($nbqualifiedlines > 0) {
5638
					print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5639
					if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5640
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5641
					}
5642
					if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5643
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5644
					}
5645
5646
					print '>';
5647
				}
5648
				print '</div>';
5649
			}
5650
			if ($more) {
5651
				print '<div class="inline-block">';
5652
				print $more;
5653
				print '</div>';
5654
			}
5655
			print '</form>';
5656
		} else {
5657
			if ($selected) {
5658
				print $selected;
5659
			} else {
5660
				print "0";
5661
			}
5662
		}
5663
	}
5664
5665
5666
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5667
	/**
5668
	 *  Show forms to select a contact
5669
	 *
5670
	 *  @param	string		$page        	Page
5671
	 *  @param	Societe		$societe		Filter on third party
5672
	 *  @param    int			$selected    	Id contact pre-selectionne
5673
	 *  @param    string		$htmlname    	Name of HTML select. If 'none', we just show contact link.
5674
	 *  @return	void
5675
	 */
5676
	public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5677
	{
5678
		// phpcs:enable
5679
		global $langs, $conf;
5680
5681
		if ($htmlname != "none") {
5682
			print '<form method="post" action="'.$page.'">';
5683
			print '<input type="hidden" name="action" value="set_contact">';
5684
			print '<input type="hidden" name="token" value="'.newToken().'">';
5685
			print '<table class="nobordernopadding">';
5686
			print '<tr><td>';
5687
			print $this->selectcontacts($societe->id, $selected, $htmlname);
5688
			$num = $this->num;
5689
			if ($num == 0) {
5690
				$addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5691
				print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5692
			}
5693
			print '</td>';
5694
			print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5695
			print '</tr></table></form>';
5696
		} else {
5697
			if ($selected) {
5698
				require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5699
				$contact = new Contact($this->db);
5700
				$contact->fetch($selected);
5701
				print $contact->getFullName($langs);
5702
			} else {
5703
				print "&nbsp;";
5704
			}
5705
		}
5706
	}
5707
5708
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5709
	/**
5710
	 *  Output html select to select thirdparty
5711
	 *
5712
	 *  @param	string	$page       	Page
5713
	 *  @param  string	$selected   	Id preselected
5714
	 *  @param  string	$htmlname		Name of HTML select
5715
	 *  @param  string	$filter         Optional filters criteras. Do not use a filter coming from input of users.
5716
	 *	@param	int		$showempty		Add an empty field
5717
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
5718
	 * 	@param	int		$forcecombo		Force to use combo box
5719
	 *  @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')))
5720
	 *  @param  int     $nooutput       No print output. Return it only.
5721
	 *  @param	array	$excludeids		Exclude IDs from the select combo
5722
	 *  @return	void|string
5723
	 */
5724
	public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array())
5725
	{
5726
		// phpcs:enable
5727
		global $langs;
5728
5729
		$out = '';
5730
		if ($htmlname != "none") {
5731
			$out .= '<form method="post" action="'.$page.'">';
5732
			$out .= '<input type="hidden" name="action" value="set_thirdparty">';
5733
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5734
			$out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
5735
			$out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5736
			$out .= '</form>';
5737
		} else {
5738
			if ($selected) {
5739
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5740
				$soc = new Societe($this->db);
5741
				$soc->fetch($selected);
5742
				$out .= $soc->getNomUrl($langs);
5743
			} else {
5744
				$out .= "&nbsp;";
5745
			}
5746
		}
5747
5748
		if ($nooutput) {
5749
			return $out;
5750
		} else {
5751
			print $out;
5752
		}
5753
	}
5754
5755
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5756
	/**
5757
	 *    Retourne la liste des devises, dans la langue de l'utilisateur
5758
	 *
5759
	 *    @param	string	$selected    preselected currency code
5760
	 *    @param    string	$htmlname    name of HTML select list
5761
	 *    @deprecated
5762
	 *    @return	void
5763
	 */
5764
	public function select_currency($selected = '', $htmlname = 'currency_id')
5765
	{
5766
		// phpcs:enable
5767
		print $this->selectCurrency($selected, $htmlname);
5768
	}
5769
5770
	/**
5771
	 *  Retourne la liste des devises, dans la langue de l'utilisateur
5772
	 *
5773
	 *  @param	string	$selected    preselected currency code
5774
	 *  @param  string	$htmlname    name of HTML select list
5775
	 *  @param  string  $mode        0 = Add currency symbol into label, 1 = Add 3 letter iso code
5776
	 * 	@return	string
5777
	 */
5778
	public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0)
5779
	{
5780
		global $conf, $langs, $user;
5781
5782
		$langs->loadCacheCurrencies('');
5783
5784
		$out = '';
5785
5786
		if ($selected == 'euro' || $selected == 'euros') {
5787
			$selected = 'EUR'; // Pour compatibilite
5788
		}
5789
5790
		$out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
5791
		foreach ($langs->cache_currencies as $code_iso => $currency) {
5792
			$labeltoshow = $currency['label'];
5793
			if ($mode == 1) {
5794
				$labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
5795
			} else {
5796
				$labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
5797
			}
5798
5799
			if ($selected && $selected == $code_iso) {
5800
				$out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
5801
			} else {
5802
				$out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
5803
			}
5804
			$out .= $labeltoshow;
5805
			$out .= '</option>';
5806
		}
5807
		$out .= '</select>';
5808
		if ($user->admin) {
5809
			$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5810
		}
5811
5812
		// Make select dynamic
5813
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5814
		$out .= ajax_combobox($htmlname);
5815
5816
		return $out;
5817
	}
5818
5819
	/**
5820
	 *	Return array of currencies in user language
5821
	 *
5822
	 *  @param	string	$selected    preselected currency code
5823
	 *  @param  string	$htmlname    name of HTML select list
5824
	 *  @param  integer	$useempty    1=Add empty line
5825
	 *  @param string $filter Optional filters criteras (example: 'code <> x', ' in (1,3)')
5826
	 *  @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
5827
	 * 	@return	string
5828
	 */
5829
	public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false)
5830
	{
5831
		global $conf, $langs;
5832
5833
		$langs->loadCacheCurrencies(''); // Load ->cache_currencies
5834
5835
		$TCurrency = array();
5836
5837
		$sql = "SELECT code FROM ".$this->db->prefix()."multicurrency";
5838
		$sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
5839
		if ($filter) {
5840
			$sql .= " AND ".$filter;
5841
		}
5842
		$resql = $this->db->query($sql);
5843
		if ($resql) {
5844
			while ($obj = $this->db->fetch_object($resql)) {
5845
				$TCurrency[$obj->code] = $obj->code;
5846
			}
5847
		}
5848
5849
		$out = '';
5850
		$out .= '<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">';
5851
		if ($useempty) {
5852
			$out .= '<option value="">&nbsp;</option>';
5853
		}
5854
		// If company current currency not in table, we add it into list. Should always be available.
5855
		if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
5856
			$TCurrency[$conf->currency] = $conf->currency;
5857
		}
5858
		if (count($TCurrency) > 0) {
5859
			foreach ($langs->cache_currencies as $code_iso => $currency) {
5860
				if (isset($TCurrency[$code_iso])) {
5861
					if (!empty($selected) && $selected == $code_iso) {
5862
						$out .= '<option value="'.$code_iso.'" selected="selected">';
5863
					} else {
5864
						$out .= '<option value="'.$code_iso.'">';
5865
					}
5866
5867
					$out .= $currency['label'];
5868
					$out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
5869
					$out .= '</option>';
5870
				}
5871
			}
5872
		}
5873
5874
		$out .= '</select>';
5875
5876
		// Make select dynamic
5877
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5878
		$out .= ajax_combobox($htmlname);
5879
5880
		return $out;
5881
	}
5882
5883
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5884
	/**
5885
	 *  Load into the cache vat rates of a country
5886
	 *
5887
	 *  @param	string	$country_code		Country code with quotes ("'CA'", or "'CA,IN,...'")
5888
	 *  @return	int							Nb of loaded lines, 0 if already loaded, <0 if KO
5889
	 */
5890
	public function load_cache_vatrates($country_code)
5891
	{
5892
		// phpcs:enable
5893
		global $langs;
5894
5895
		$num = count($this->cache_vatrates);
5896
		if ($num > 0) {
5897
			return $num; // Cache already loaded
5898
		}
5899
5900
		dol_syslog(__METHOD__, LOG_DEBUG);
5901
5902
		$sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
5903
		$sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
5904
		$sql .= " WHERE t.fk_pays = c.rowid";
5905
		$sql .= " AND t.active > 0";
5906
		$sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
5907
		$sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
5908
5909
		$resql = $this->db->query($sql);
5910
		if ($resql) {
5911
			$num = $this->db->num_rows($resql);
5912
			if ($num) {
5913
				for ($i = 0; $i < $num; $i++) {
5914
					$obj = $this->db->fetch_object($resql);
5915
					$this->cache_vatrates[$i]['rowid']	= $obj->rowid;
5916
					$this->cache_vatrates[$i]['code'] = $obj->code;
5917
					$this->cache_vatrates[$i]['txtva']	= $obj->taux;
5918
					$this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
5919
					$this->cache_vatrates[$i]['localtax1']	    = $obj->localtax1;
5920
					$this->cache_vatrates[$i]['localtax1_type']	= $obj->localtax1_type;
5921
					$this->cache_vatrates[$i]['localtax2']	    = $obj->localtax2;
5922
					$this->cache_vatrates[$i]['localtax2_type']	= $obj->localtax1_type;
5923
5924
					$this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
5925
					$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
5926
					$positiverates = '';
5927
					if ($obj->taux) {
5928
						$positiverates .= ($positiverates ? '/' : '').$obj->taux;
5929
					}
5930
					if ($obj->localtax1) {
5931
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
5932
					}
5933
					if ($obj->localtax2) {
5934
						$positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
5935
					}
5936
					if (empty($positiverates)) {
5937
						$positiverates = '0';
5938
					}
5939
					$this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
5940
				}
5941
5942
				return $num;
5943
			} else {
5944
				$this->error = '<span class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</span>';
5945
				return -1;
5946
			}
5947
		} else {
5948
			$this->error = '<span class="error">'.$this->db->error().'</span>';
5949
			return -2;
5950
		}
5951
	}
5952
5953
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5954
	/**
5955
	 *  Output an HTML select vat rate.
5956
	 *  The name of this function should be selectVat. We keep bad name for compatibility purpose.
5957
	 *
5958
	 *  @param	string	      $htmlname           Name of HTML select field
5959
	 *  @param  float|string  $selectedrate       Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing.
5960
	 *  @param  Societe	      $societe_vendeuse   Thirdparty seller
5961
	 *  @param  Societe	      $societe_acheteuse  Thirdparty buyer
5962
	 *  @param  int		      $idprod             Id product. O if unknown of NA.
5963
	 *  @param  int		      $info_bits          Miscellaneous information on line (1 for NPR)
5964
	 *  @param  int|string    $type               ''=Unknown, 0=Product, 1=Service (Used if idprod not defined)
5965
	 *                  		                  Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5966
	 *                  					      Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5967
	 *                  					      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.
5968
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle.
5969
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle.
5970
	 *                  					      Sinon la TVA proposee par defaut=0. Fin de regle.
5971
	 *  @param	bool	     $options_only		  Return HTML options lines only (for ajax treatment)
5972
	 *  @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
5973
	 *  @return	string
5974
	 */
5975
	public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
5976
	{
5977
		// phpcs:enable
5978
		global $langs, $conf, $mysoc;
5979
5980
		$langs->load('errors');
5981
5982
		$return = '';
5983
5984
		// Define defaultnpr, defaultttx and defaultcode
5985
		$defaultnpr = ($info_bits & 0x01);
5986
		$defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
5987
		$defaulttx = str_replace('*', '', $selectedrate);
5988
		$defaultcode = '';
5989
		$reg = array();
5990
		if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5991
			$defaultcode = $reg[1];
5992
			$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5993
		}
5994
		//var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
5995
5996
		// Check parameters
5997
		if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
5998
			if ($societe_vendeuse->id == $mysoc->id) {
5999
				$return .= '<span class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</span>';
6000
			} else {
6001
				$return .= '<span class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</span>';
6002
			}
6003
			return $return;
6004
		}
6005
6006
		//var_dump($societe_acheteuse);
6007
		//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";
6008
		//exit;
6009
6010
		// Define list of countries to use to search VAT rates to show
6011
		// First we defined code_country to use to find list
6012
		if (is_object($societe_vendeuse)) {
6013
			$code_country = "'".$societe_vendeuse->country_code."'";
6014
		} else {
6015
			$code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
6016
		}
6017
		if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {    // If option to have vat for end customer for services is on
6018
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6019
			if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
6020
				// We also add the buyer
6021
				if (is_numeric($type)) {
6022
					if ($type == 1) { // We know product is a service
6023
						$code_country .= ",'".$societe_acheteuse->country_code."'";
6024
					}
6025
				} elseif (!$idprod) {  // We don't know type of product
6026
					$code_country .= ",'".$societe_acheteuse->country_code."'";
6027
				} else {
6028
					$prodstatic = new Product($this->db);
6029
					$prodstatic->fetch($idprod);
6030
					if ($prodstatic->type == Product::TYPE_SERVICE) {   // We know product is a service
6031
						$code_country .= ",'".$societe_acheteuse->country_code."'";
6032
					}
6033
				}
6034
			}
6035
		}
6036
6037
		// Now we get list
6038
		$num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
6039
6040
		if ($num > 0) {
6041
			// Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
6042
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6043
				$tmpthirdparty = new Societe($this->db);
6044
				$defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6045
				$defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6046
				if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6047
					$defaultcode = $reg[1];
6048
					$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6049
				}
6050
				if (empty($defaulttx)) {
6051
					$defaultnpr = 0;
6052
				}
6053
			}
6054
6055
			// Si taux par defaut n'a pu etre determine, on prend dernier de la liste.
6056
			// Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant
6057
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6058
				if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6059
					$defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
6060
				} else {
6061
					$defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS);
6062
				}
6063
			}
6064
6065
			// Disabled if seller is not subject to VAT
6066
			$disabled = false;
6067
			$title = '';
6068
			if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
6069
				// Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
6070
				// of using supplier invoices (this is a very bad idea !)
6071
				if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
6072
					$title = ' title="'.$langs->trans('VATIsNotUsed').'"';
6073
					$disabled = true;
6074
				}
6075
			}
6076
6077
			if (!$options_only) {
6078
				$return .= '<select class="flat minwidth75imp" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
6079
			}
6080
6081
			$selectedfound = false;
6082
			foreach ($this->cache_vatrates as $rate) {
6083
				// Keep only 0 if seller is not subject to VAT
6084
				if ($disabled && $rate['txtva'] != 0) {
6085
					continue;
6086
				}
6087
6088
				// Define key to use into select list
6089
				$key = $rate['txtva'];
6090
				$key .= $rate['nprtva'] ? '*' : '';
6091
				if ($mode > 0 && $rate['code']) {
6092
					$key .= ' ('.$rate['code'].')';
6093
				}
6094
				if ($mode < 0) {
6095
					$key = $rate['rowid'];
6096
				}
6097
6098
				$return .= '<option value="'.$key.'"';
6099
				if (!$selectedfound) {
6100
					if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
6101
						if ($defaultcode == $rate['code']) {
6102
							$return .= ' selected';
6103
							$selectedfound = true;
6104
						}
6105
					} elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
6106
						$return .= ' selected';
6107
						$selectedfound = true;
6108
					}
6109
				}
6110
				$return .= '>';
6111
				//if (! empty($conf->global->MAIN_VAT_SHOW_POSITIVE_RATES))
6112
				if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
6113
					$return .= $rate['labelpositiverates'];
6114
				} else {
6115
					$return .= vatrate($rate['label']);
6116
				}
6117
				//$return.=($rate['code']?' '.$rate['code']:'');
6118
				$return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the *  (old behaviour only if new vat code is not used)
6119
6120
				$return .= '</option>';
6121
			}
6122
6123
			if (!$options_only) {
6124
				$return .= '</select>';
6125
			}
6126
		} else {
6127
			$return .= $this->error;
6128
		}
6129
6130
		$this->num = $num;
6131
		return $return;
6132
	}
6133
6134
6135
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6136
	/**
6137
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6138
	 *  Fields are preselected with :
6139
	 *            	- set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6140
	 *            	- local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6141
	 *            	- Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6142
	 *
6143
	 *	@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).
6144
	 *	@param	string		$prefix			Prefix for fields name
6145
	 *	@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
6146
	 *	@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
6147
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6148
	 *	@param	string		$form_name 		Not used
6149
	 *	@param	int			$d				1=Show days, month, years
6150
	 * 	@param	int			$addnowlink		Add a link "Now"
6151
	 * 	@param	int			$nooutput		Do not output html string but return it
6152
	 * 	@param 	int			$disabled		Disable input fields
6153
	 *  @param  int			$fullday        When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59
6154
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another select_date field.
6155
	 *  @param  datetime    $adddateof      Add a link "Date of invoice" using the following date.
6156
	 *  @return	string|void					Nothing or string if nooutput is 1
6157
	 *  @deprecated
6158
	 *  @see    selectDate(), form_date(), select_month(), select_year(), select_dayofweek()
6159
	 */
6160
	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 = '')
6161
	{
6162
		// phpcs:enable
6163
		$retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6164
		if (!empty($nooutput)) {
6165
			return $retstring;
6166
		}
6167
		print $retstring;
6168
		return;
6169
	}
6170
6171
	/**
6172
	 *  Show 2 HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6173
	 *  Fields are preselected with :
6174
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6175
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6176
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6177
	 *
6178
	 *  @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).
6179
	 *  @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).
6180
	 *  @param	string		$prefix				Prefix for fields name
6181
	 *  @param	string		$empty				0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6182
	 *  @param	string		$forcenewline		Force new line between the 2 dates.
6183
	 * 	@return string                   	    Html for selectDate
6184
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6185
	 */
6186
	public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
6187
	{
6188
		global $langs;
6189
6190
		$ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6191
		if ($forcenewline) {
6192
			$ret .= '<br>';
6193
		}
6194
		$ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6195
		return $ret;
6196
	}
6197
6198
	/**
6199
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
6200
	 *  Fields are preselected with :
6201
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6202
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6203
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6204
	 *
6205
	 *  @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).
6206
	 *  @param	string		$prefix			Prefix for fields name
6207
	 *  @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
6208
	 *	@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
6209
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6210
	 *	@param	string		$form_name 		Not used
6211
	 *	@param	int			$d				1=Show days, month, years
6212
	 * 	@param	int			$addnowlink		Add a link "Now", 1 with server time, 2 with local computer time
6213
	 * 	@param 	int			$disabled		Disable input fields
6214
	 *  @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')
6215
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another selectDate field.
6216
	 *  @param  datetime    $adddateof      Add a link "Date of ..." using the following date. See also $labeladddateof for the label used.
6217
	 *  @param  string      $openinghours   Specify hour start and hour end for the select ex 8,20
6218
	 *  @param  int         $stepminutes    Specify step for minutes between 1 and 30
6219
	 *  @param	string		$labeladddateof Label to use for the $adddateof parameter.
6220
	 *  @param	string 		$placeholder    Placeholder
6221
	 *  @param	mixed		$gm				'auto' (for backward compatibility, avoid this), 'gmt' or 'tzserver' or 'tzuserrel'
6222
	 * 	@return string                      Html for selectDate
6223
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
6224
	 */
6225
	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')
6226
	{
6227
		global $conf, $langs;
6228
6229
		if ($gm === 'auto') {
6230
			$gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6231
		}
6232
6233
		$retstring = '';
6234
6235
		if ($prefix == '') {
6236
			$prefix = 're';
6237
		}
6238
		if ($h == '') {
6239
			$h = 0;
6240
		}
6241
		if ($m == '') {
6242
			$m = 0;
6243
		}
6244
		$emptydate = 0;
6245
		$emptyhours = 0;
6246
		if ($stepminutes <= 0 || $stepminutes > 30) {
6247
			$stepminutes = 1;
6248
		}
6249
		if ($empty == 1) {
6250
			$emptydate = 1;
6251
			$emptyhours = 1;
6252
		}
6253
		if ($empty == 2) {
6254
			$emptydate = 0;
6255
			$emptyhours = 1;
6256
		}
6257
		$orig_set_time = $set_time;
6258
6259
		if ($set_time === '' && $emptydate == 0) {
6260
			include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6261
			if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6262
				$set_time = dol_now($gm);
6263
			} else {
6264
				$set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6265
			}
6266
		}
6267
6268
		// Analysis of the pre-selection date
6269
		$reg = array();
6270
		if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) {	// deprecated usage
6271
			// Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6272
			$syear	= (!empty($reg[1]) ? $reg[1] : '');
6273
			$smonth = (!empty($reg[2]) ? $reg[2] : '');
6274
			$sday	= (!empty($reg[3]) ? $reg[3] : '');
6275
			$shour	= (!empty($reg[4]) ? $reg[4] : '');
6276
			$smin	= (!empty($reg[5]) ? $reg[5] : '');
6277
		} elseif (strval($set_time) != '' && $set_time != -1) {
6278
			// set_time est un timestamps (0 possible)
6279
			$syear = dol_print_date($set_time, "%Y", $gm);
6280
			$smonth = dol_print_date($set_time, "%m", $gm);
6281
			$sday = dol_print_date($set_time, "%d", $gm);
6282
			if ($orig_set_time != '') {
6283
				$shour = dol_print_date($set_time, "%H", $gm);
6284
				$smin = dol_print_date($set_time, "%M", $gm);
6285
				$ssec = dol_print_date($set_time, "%S", $gm);
6286
			} else {
6287
				$shour = '';
6288
				$smin = '';
6289
				$ssec = '';
6290
			}
6291
		} else {
6292
			// Date est '' ou vaut -1
6293
			$syear = '';
6294
			$smonth = '';
6295
			$sday = '';
6296
			$shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6297
			$smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6298
			$ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6299
		}
6300
		if ($h == 3) {
6301
			$shour = '';
6302
		}
6303
		if ($m == 3) {
6304
			$smin = '';
6305
		}
6306
6307
		$nowgmt = dol_now('gmt');
6308
		//var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6309
6310
		// You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6311
		$usecalendar = 'combo';
6312
		if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6313
			$usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6314
		}
6315
6316
		if ($d) {
6317
			// Show date with popup
6318
			if ($usecalendar != 'combo') {
6319
				$formated_date = '';
6320
				//print "e".$set_time." t ".$conf->format_date_short;
6321
				if (strval($set_time) != '' && $set_time != -1) {
6322
					//$formated_date=dol_print_date($set_time,$conf->format_date_short);
6323
					$formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6324
				}
6325
6326
				// Calendrier popup version eldy
6327
				if ($usecalendar == "eldy") {
6328
					// Input area to enter date manually
6329
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6330
					$retstring .= ($disabled ? ' disabled' : '');
6331
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6332
					$retstring .= '>';
6333
6334
					// Icon calendar
6335
					$retstringbuttom = '';
6336
					if (!$disabled) {
6337
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
6338
						$base = DOL_URL_ROOT.'/core/';
6339
						$retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
6340
						$retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
6341
					} else {
6342
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6343
					}
6344
					$retstring = $retstringbuttom.$retstring;
6345
6346
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6347
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6348
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6349
				} elseif ($usecalendar == 'jquery') {
6350
					if (!$disabled) {
6351
						// Output javascript for datepicker
6352
						$retstring .= "<script type='text/javascript'>";
6353
						$retstring .= "$(function(){ $('#".$prefix."').datepicker({
6354
							dateFormat: '".$langs->trans("FormatDateShortJQueryInput")."',
6355
							autoclose: true,
6356
							todayHighlight: true,";
6357
						if (!empty($conf->dol_use_jmobile)) {
6358
							$retstring .= "
6359
								beforeShow: function (input, datePicker) {
6360
									input.disabled = true;
6361
								},
6362
								onClose: function (dateText, datePicker) {
6363
									this.disabled = false;
6364
								},
6365
								";
6366
						}
6367
						// Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
6368
						if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS)) {
6369
							$retstring .= "
6370
								showOn: 'button',	/* both has problem with autocompletion */
6371
								buttonImage: '".DOL_URL_ROOT."/theme/".dol_escape_js($conf->theme)."/img/object_calendarday.png',
6372
								buttonImageOnly: true";
6373
						}
6374
						$retstring .= "
6375
							}) });";
6376
						$retstring .= "</script>";
6377
					}
6378
6379
					// Zone de saisie manuelle de la date
6380
					$retstring .= '<div class="nowrap inline-block divfordateinput">';
6381
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6382
					$retstring .= ($disabled ? ' disabled' : '');
6383
					$retstring .= ($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '');
6384
					$retstring .= ' onChange="dpChangeDay(\''.dol_escape_js($prefix).'\',\''.dol_escape_js($langs->trans("FormatDateShortJavaInput")).'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6385
					$retstring .= '>';
6386
6387
					// Icone calendrier
6388
					if (!$disabled) {
6389
						/* Not required. Managed by option buttonImage of jquery
6390
						$retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
6391
						$retstring.="<script type='text/javascript'>";
6392
						$retstring.="jQuery(document).ready(function() {";
6393
						$retstring.='	jQuery("#'.$prefix.'id").click(function() {';
6394
						$retstring.="    	jQuery('#".$prefix."').focus();";
6395
						$retstring.='    });';
6396
						$retstring.='});';
6397
						$retstring.="</script>";*/
6398
					} else {
6399
						$retstringbutton = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6400
						$retsring = $retstringbutton.$retstring;
6401
					}
6402
6403
					$retstring .= '</div>';
6404
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
6405
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6406
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
6407
				} else {
6408
					$retstring .= "Bad value of MAIN_POPUP_CALENDAR";
6409
				}
6410
			} else {
6411
				// Show date with combo selects
6412
				// Day
6413
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
6414
6415
				if ($emptydate || $set_time == -1) {
6416
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6417
				}
6418
6419
				for ($day = 1; $day <= 31; $day++) {
6420
					$retstring .= '<option value="'.$day.'"'.($day == $sday ? ' selected' : '').'>'.$day.'</option>';
6421
				}
6422
6423
				$retstring .= "</select>";
6424
6425
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
6426
				if ($emptydate || $set_time == -1) {
6427
					$retstring .= '<option value="0" selected>&nbsp;</option>';
6428
				}
6429
6430
				// Month
6431
				for ($month = 1; $month <= 12; $month++) {
6432
					$retstring .= '<option value="'.$month.'"'.($month == $smonth ? ' selected' : '').'>';
6433
					$retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
6434
					$retstring .= "</option>";
6435
				}
6436
				$retstring .= "</select>";
6437
6438
				// Year
6439
				if ($emptydate || $set_time == -1) {
6440
					$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.'">';
6441
				} else {
6442
					$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
6443
6444
					for ($year = $syear - 10; $year < $syear + 10; $year++) {
6445
						$retstring .= '<option value="'.$year.'"'.($year == $syear ? ' selected' : '').'>'.$year.'</option>';
6446
					}
6447
					$retstring .= "</select>\n";
6448
				}
6449
			}
6450
		}
6451
6452
		if ($d && $h) {
6453
			$retstring .= ($h == 2 ? '<br>' : ' ');
6454
			$retstring .= '<span class="nowraponall">';
6455
		}
6456
6457
		if ($h) {
6458
			$hourstart = 0;
6459
			$hourend = 24;
6460
			if ($openinghours != '') {
6461
				$openinghours = explode(',', $openinghours);
6462
				$hourstart = $openinghours[0];
6463
				$hourend = $openinghours[1];
6464
				if ($hourend < $hourstart) {
6465
					$hourend = $hourstart;
6466
				}
6467
			}
6468
			// Show hour
6469
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'hour' : '').'" id="'.$prefix.'hour" name="'.$prefix.'hour">';
6470
			if ($emptyhours) {
6471
				$retstring .= '<option value="-1">&nbsp;</option>';
6472
			}
6473
			for ($hour = $hourstart; $hour < $hourend; $hour++) {
6474
				if (strlen($hour) < 2) {
6475
					$hour = "0".$hour;
6476
				}
6477
				$retstring .= '<option value="'.$hour.'"'.(($hour == $shour) ? ' selected' : '').'>'.$hour;
6478
				//$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
6479
				$retstring .= '</option>';
6480
			}
6481
			$retstring .= '</select>';
6482
			//if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
6483
			if ($m) {
6484
				$retstring .= ":";
6485
			}
6486
		}
6487
6488
		if ($m) {
6489
			// Show minutes
6490
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'min' : '').'" id="'.$prefix.'min" name="'.$prefix.'min">';
6491
			if ($emptyhours) {
6492
				$retstring .= '<option value="-1">&nbsp;</option>';
6493
			}
6494
			for ($min = 0; $min < 60; $min += $stepminutes) {
6495
				if (strlen($min) < 2) {
6496
					$min = "0".$min;
6497
				}
6498
				$retstring .= '<option value="'.$min.'"'.(($min == $smin) ? ' selected' : '').'>'.$min.(empty($conf->dol_optimize_smallscreen) ? '' : '').'</option>';
6499
			}
6500
			$retstring .= '</select>';
6501
6502
			$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...
6503
		}
6504
6505
		if ($d && $h) {
6506
			$retstring .= '</span>';
6507
		}
6508
6509
		// Add a "Now" link
6510
		if ($conf->use_javascript_ajax && $addnowlink) {
6511
			// Script which will be inserted in the onClick of the "Now" link
6512
			$reset_scripts = "";
6513
			if ($addnowlink == 2) { // local computer time
6514
				// pad add leading 0 on numbers
6515
				$reset_scripts .= "Number.prototype.pad = function(size) {
6516
                        var s = String(this);
6517
                        while (s.length < (size || 2)) {s = '0' + s;}
6518
                        return s;
6519
                    };
6520
                    var d = new Date();";
6521
			}
6522
6523
			// Generate the date part, depending on the use or not of the javascript calendar
6524
			if ($addnowlink == 1) { // server time expressed in user time setup
6525
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6526
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6527
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6528
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6529
			} elseif ($addnowlink == 2) {
6530
				/* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
6531
				 * This break application for foreign languages.
6532
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
6533
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
6534
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
6535
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
6536
				*/
6537
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'day', 'tzuserrel').'\');';
6538
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6539
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6540
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6541
			}
6542
			/*if ($usecalendar == "eldy")
6543
			{
6544
				$base=DOL_URL_ROOT.'/core/';
6545
				$reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
6546
			}
6547
			else
6548
			{
6549
				$reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
6550
				$reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
6551
				$reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
6552
			}*/
6553
			// Update the hour part
6554
			if ($h) {
6555
				if ($fullday) {
6556
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6557
				}
6558
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
6559
				if ($addnowlink == 1) {
6560
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6561
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6562
				} elseif ($addnowlink == 2) {
6563
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());';
6564
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
6565
				}
6566
6567
				if ($fullday) {
6568
					$reset_scripts .= ' } ';
6569
				}
6570
			}
6571
			// Update the minute part
6572
			if ($m) {
6573
				if ($fullday) {
6574
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6575
				}
6576
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
6577
				if ($addnowlink == 1) {
6578
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6579
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6580
				} elseif ($addnowlink == 2) {
6581
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());';
6582
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
6583
				}
6584
				if ($fullday) {
6585
					$reset_scripts .= ' } ';
6586
				}
6587
			}
6588
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6589
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6590
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonNow" type="button" name="_useless" value="now" onClick="'.$reset_scripts.'">';
6591
				$retstring .= $langs->trans("Now");
6592
				$retstring .= '</button> ';
6593
			}
6594
		}
6595
6596
		// Add a "Plus one hour" link
6597
		if ($conf->use_javascript_ajax && $addplusone) {
6598
			// Script which will be inserted in the onClick of the "Add plusone" link
6599
			$reset_scripts = "";
6600
6601
			// Generate the date part, depending on the use or not of the javascript calendar
6602
			$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel').'\');';
6603
			$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date($nowgmt, '%d', 'tzuserrel').'\');';
6604
			$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date($nowgmt, '%m', 'tzuserrel').'\');';
6605
			$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date($nowgmt, '%Y', 'tzuserrel').'\');';
6606
			// Update the hour part
6607
			if ($h) {
6608
				if ($fullday) {
6609
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6610
				}
6611
				$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date($nowgmt, '%H', 'tzuserrel').'\');';
6612
				if ($fullday) {
6613
					$reset_scripts .= ' } ';
6614
				}
6615
			}
6616
			// Update the minute part
6617
			if ($m) {
6618
				if ($fullday) {
6619
					$reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6620
				}
6621
				$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date($nowgmt, '%M', 'tzuserrel').'\');';
6622
				if ($fullday) {
6623
					$reset_scripts .= ' } ';
6624
				}
6625
			}
6626
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6627
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
6628
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="'.$reset_scripts.'">';
6629
				$retstring .= $langs->trans("DateStartPlusOne");
6630
				$retstring .= '</button> ';
6631
			}
6632
		}
6633
6634
		// Add a link to set data
6635
		if ($conf->use_javascript_ajax && $adddateof) {
6636
			$tmparray = dol_getdate($adddateof);
6637
			if (empty($labeladddateof)) {
6638
				$labeladddateof = $langs->trans("DateInvoice");
6639
			}
6640
			$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>';
6641
		}
6642
6643
		return $retstring;
6644
	}
6645
6646
	/**
6647
	 * selectTypeDuration
6648
	 *
6649
	 * @param   string   	$prefix     	Prefix
6650
	 * @param   string   	$selected   	Selected duration type
6651
	 * @param	array		$excludetypes	Array of duration types to exclude. Example array('y', 'm')
6652
	 * @return  string      	         	HTML select string
6653
	 */
6654
	public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
6655
	{
6656
		global $langs;
6657
6658
		$TDurationTypes = array(
6659
			'y'=>$langs->trans('Years'),
6660
			'm'=>$langs->trans('Month'),
6661
			'w'=>$langs->trans('Weeks'),
6662
			'd'=>$langs->trans('Days'),
6663
			'h'=>$langs->trans('Hours'),
6664
			'i'=>$langs->trans('Minutes')
6665
		);
6666
6667
		// Removed undesired duration types
6668
		foreach ($excludetypes as $value) {
6669
			unset($TDurationTypes[$value]);
6670
		}
6671
6672
		$retstring = '<select class="flat minwidth75 maxwidth100" id="select_'.$prefix.'type_duration" name="'.$prefix.'type_duration">';
6673
		foreach ($TDurationTypes as $key => $typeduration) {
6674
			$retstring .= '<option value="'.$key.'"';
6675
			if ($key == $selected) {
6676
				$retstring .= " selected";
6677
			}
6678
			$retstring .= ">".$typeduration."</option>";
6679
		}
6680
		$retstring .= "</select>";
6681
6682
		$retstring .= ajax_combobox('select_'.$prefix.'type_duration');
6683
6684
		return $retstring;
6685
	}
6686
6687
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6688
	/**
6689
	 *  Function to show a form to select a duration on a page
6690
	 *
6691
	 *	@param	string		$prefix   		Prefix for input fields
6692
	 *	@param  int			$iSecond  		Default preselected duration (number of seconds or '')
6693
	 * 	@param	int			$disabled       Disable the combo box
6694
	 * 	@param	string		$typehour		If 'select' then input hour and input min is a combo,
6695
	 *						            	If 'text' input hour is in text and input min is a text,
6696
	 *						            	If 'textselect' input hour is in text and input min is a combo
6697
	 *  @param	integer		$minunderhours	If 1, show minutes selection under the hours
6698
	 * 	@param	int			$nooutput		Do not output html string but return it
6699
	 *  @return	string|void
6700
	 */
6701
	public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
6702
	{
6703
		// phpcs:enable
6704
		global $langs;
6705
6706
		$retstring = '<span class="nowraponall">';
6707
6708
		$hourSelected = 0;
6709
		$minSelected = 0;
6710
6711
		// Hours
6712
		if ($iSecond != '') {
6713
			require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6714
6715
			$hourSelected = convertSecondToTime($iSecond, 'allhour');
6716
			$minSelected = convertSecondToTime($iSecond, 'min');
6717
		}
6718
6719
		if ($typehour == 'select') {
6720
			$retstring .= '<select class="flat" id="select_'.$prefix.'hour" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').'>';
6721
			for ($hour = 0; $hour < 25; $hour++) {	// For a duration, we allow 24 hours
6722
				$retstring .= '<option value="'.$hour.'"';
6723
				if ($hourSelected == $hour) {
6724
					$retstring .= " selected";
6725
				}
6726
				$retstring .= ">".$hour."</option>";
6727
			}
6728
			$retstring .= "</select>";
6729
		} elseif ($typehour == 'text' || $typehour == 'textselect') {
6730
			$retstring .= '<input placeholder="'.$langs->trans('HourShort').'" type="number" min="0" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputhour" value="'.(($hourSelected != '') ? ((int) $hourSelected) : '').'">';
6731
		} else {
6732
			return 'BadValueForParameterTypeHour';
6733
		}
6734
6735
		if ($typehour != 'text') {
6736
			$retstring .= ' '.$langs->trans('HourShort');
6737
		} else {
6738
			$retstring .= '<span class="">:</span>';
6739
		}
6740
6741
		// Minutes
6742
		if ($minunderhours) {
6743
			$retstring .= '<br>';
6744
		} else {
6745
			$retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
6746
		}
6747
6748
		if ($typehour == 'select' || $typehour == 'textselect') {
6749
			$retstring .= '<select class="flat" id="select_'.$prefix.'min" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').'>';
6750
			for ($min = 0; $min <= 55; $min = $min + 5) {
6751
				$retstring .= '<option value="'.$min.'"';
6752
				if ($minSelected == $min) {
6753
					$retstring .= ' selected';
6754
				}
6755
				$retstring .= '>'.$min.'</option>';
6756
			}
6757
			$retstring .= "</select>";
6758
		} elseif ($typehour == 'text') {
6759
			$retstring .= '<input placeholder="'.$langs->trans('MinuteShort').'" type="number" min="0" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputminute" value="'.(($minSelected != '') ? ((int) $minSelected) : '').'">';
6760
		}
6761
6762
		if ($typehour != 'text') {
6763
			$retstring .= ' '.$langs->trans('MinuteShort');
6764
		}
6765
6766
		$retstring.="</span>";
6767
6768
		if (!empty($nooutput)) {
6769
			return $retstring;
6770
		}
6771
6772
		print $retstring;
6773
		return;
6774
	}
6775
6776
	/**
6777
	 *  Return list of tickets in Ajax if Ajax activated or go to selectTicketsList
6778
	 *
6779
	 *  @param		int			$selected				Preselected tickets
6780
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
6781
	 *  @param  	string		$filtertype     		To add a filter
6782
	 *  @param		int			$limit					Limit on number of returned lines
6783
	 *  @param		int			$status					Ticket status
6784
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
6785
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
6786
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
6787
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
6788
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6789
	 * 	@param		int			$forcecombo				Force to use combo box
6790
	 *  @param      string      $morecss                Add more css on select
6791
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
6792
	 *  @param		string		$nooutput				No print, return the output into a string
6793
	 *  @return		void|string
6794
	 */
6795
	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)
6796
	{
6797
		global $langs, $conf;
6798
6799
		$out = '';
6800
6801
		// check parameters
6802
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
6803
6804
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6805
			$placeholder = '';
6806
6807
			if ($selected && empty($selected_input_value)) {
6808
				require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6809
				$tickettmpselect = new Ticket($this->db);
6810
				$tickettmpselect->fetch($selected);
6811
				$selected_input_value = $tickettmpselect->ref;
6812
				unset($tickettmpselect);
6813
			}
6814
6815
			$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...
6816
6817
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
6818
			elseif ($hidelabel > 1) {
6819
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
6820
				if ($hidelabel == 2) {
6821
					$out .= img_picto($langs->trans("Search"), 'search');
6822
				}
6823
			}
6824
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
6825
			if ($hidelabel == 3) {
6826
				$out .= img_picto($langs->trans("Search"), 'search');
6827
			}
6828
		} else {
6829
			$out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
6830
		}
6831
6832
		if (empty($nooutput)) print $out;
6833
		else return $out;
6834
	}
6835
6836
6837
	/**
6838
	 *	Return list of tickets.
6839
	 *  Called by selectTickets.
6840
	 *
6841
	 *	@param      int		$selected           Preselected ticket
6842
	 *	@param      string	$htmlname           Name of select html
6843
	 *  @param		string	$filtertype         Filter on ticket type
6844
	 *	@param      int		$limit              Limit on number of returned lines
6845
	 * 	@param      string	$filterkey          Filter on ticket ref or subject
6846
	 *	@param		int		$status             Ticket status
6847
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
6848
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6849
	 * 	@param		int		$forcecombo		    Force to use combo box
6850
	 *  @param      string  $morecss            Add more css on select
6851
	 *  @return     array    				    Array of keys for json
6852
	 */
6853
	public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
6854
	{
6855
		global $langs, $conf;
6856
6857
		$out = '';
6858
		$outarray = array();
6859
6860
		$selectFields = " p.rowid, p.ref, p.message";
6861
6862
		$sql = "SELECT ";
6863
		$sql .= $selectFields;
6864
		$sql .= " FROM ".$this->db->prefix()."ticket as p";
6865
		$sql .= ' WHERE p.entity IN ('.getEntity('ticket').')';
6866
6867
		// Add criteria on ref/label
6868
		if ($filterkey != '') {
6869
			$sql .= ' AND (';
6870
			$prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
6871
			// For natural search
6872
			$scrit = explode(' ', $filterkey);
6873
			$i = 0;
6874
			if (count($scrit) > 1) $sql .= "(";
6875
			foreach ($scrit as $crit) {
6876
				if ($i > 0) $sql .= " AND ";
6877
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.subject LIKE '".$this->db->escape($prefix.$crit)."%'";
6878
				$sql .= ")";
6879
				$i++;
6880
			}
6881
			if (count($scrit) > 1) $sql .= ")";
6882
			$sql .= ')';
6883
		}
6884
6885
		$sql .= $this->db->plimit($limit, 0);
6886
6887
		// Build output string
6888
		dol_syslog(get_class($this)."::selectTicketsList search tickets", LOG_DEBUG);
6889
		$result = $this->db->query($sql);
6890
		if ($result) {
6891
			require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
6892
			require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
6893
6894
			$num = $this->db->num_rows($result);
6895
6896
			$events = null;
6897
6898
			if (!$forcecombo) {
6899
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6900
				$out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT);
6901
			}
6902
6903
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
6904
6905
			$textifempty = '';
6906
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
6907
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
6908
			if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
6909
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6910
				else $textifempty .= $langs->trans("All");
6911
			} else {
6912
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6913
			}
6914
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
6915
6916
			$i = 0;
6917
			while ($num && $i < $num) {
6918
				$opt = '';
6919
				$optJson = array();
6920
				$objp = $this->db->fetch_object($result);
6921
6922
				$this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
6923
				// Add new entry
6924
				// "key" value of json key array is used by jQuery automatically as selected value
6925
				// "label" value of json key array is used by jQuery automatically as text for combo box
6926
				$out .= $opt;
6927
				array_push($outarray, $optJson);
6928
6929
				$i++;
6930
			}
6931
6932
			$out .= '</select>';
6933
6934
			$this->db->free($result);
6935
6936
			if (empty($outputmode)) return $out;
6937
			return $outarray;
6938
		} else {
6939
			dol_print_error($this->db);
6940
		}
6941
	}
6942
6943
	/**
6944
	 * constructTicketListOption.
6945
	 * This define value for &$opt and &$optJson.
6946
	 *
6947
	 * @param 	resource	$objp			    Result set of fetch
6948
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
6949
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
6950
	 * @param 	string		$selected		    Preselected value
6951
	 * @param   string      $filterkey          Filter key to highlight
6952
	 * @return	void
6953
	 */
6954
	protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
6955
	{
6956
		$outkey = '';
6957
		$outval = '';
6958
		$outref = '';
6959
		$outlabel = '';
6960
		$outtype = '';
6961
6962
		$label = $objp->label;
6963
6964
		$outkey = $objp->rowid;
6965
		$outref = $objp->ref;
6966
		$outlabel = $objp->label;
6967
		$outtype = $objp->fk_product_type;
6968
6969
		$opt = '<option value="'.$objp->rowid.'"';
6970
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
6971
		$opt .= '>';
6972
		$opt .= $objp->ref;
6973
		$objRef = $objp->ref;
6974
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
6975
		$outval .= $objRef;
6976
6977
		$opt .= "</option>\n";
6978
		$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...
6979
	}
6980
6981
	/**
6982
	 *  Return list of projects in Ajax if Ajax activated or go to selectTicketsList
6983
	 *
6984
	 *  @param		int			$selected				Preselected tickets
6985
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
6986
	 *  @param  	string		$filtertype     		To add a filter
6987
	 *  @param		int			$limit					Limit on number of returned lines
6988
	 *  @param		int			$status					Ticket status
6989
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
6990
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
6991
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
6992
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
6993
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
6994
	 * 	@param		int			$forcecombo				Force to use combo box
6995
	 *  @param      string      $morecss                Add more css on select
6996
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
6997
	 *  @param		string		$nooutput				No print, return the output into a string
6998
	 *  @return		void|string
6999
	 */
7000
	public function selectProjects($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7001
	{
7002
		global $langs, $conf;
7003
7004
		$out = '';
7005
7006
		// check parameters
7007
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
7008
7009
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
7010
			$placeholder = '';
7011
7012
			if ($selected && empty($selected_input_value)) {
7013
				require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
7014
				$projecttmpselect = new Project($this->db);
7015
				$projecttmpselect->fetch($selected);
7016
				$selected_input_value = $projecttmpselect->ref;
7017
				unset($projecttmpselect);
7018
			}
7019
7020
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/projet/ajax/projects.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...
7021
7022
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
7023
			elseif ($hidelabel > 1) {
7024
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
7025
				if ($hidelabel == 2) {
7026
					$out .= img_picto($langs->trans("Search"), 'search');
7027
				}
7028
			}
7029
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
7030
			if ($hidelabel == 3) {
7031
				$out .= img_picto($langs->trans("Search"), 'search');
7032
			}
7033
		} else {
7034
			$out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
7035
		}
7036
7037
		if (empty($nooutput)) print $out;
7038
		else return $out;
7039
	}
7040
7041
	/**
7042
	 *	Return list of projects.
7043
	 *  Called by selectProjects.
7044
	 *
7045
	 *	@param      int		$selected           Preselected project
7046
	 *	@param      string	$htmlname           Name of select html
7047
	 *  @param		string	$filtertype         Filter on project type
7048
	 *	@param      int		$limit              Limit on number of returned lines
7049
	 * 	@param      string	$filterkey          Filter on project ref or subject
7050
	 *	@param		int		$status             Ticket status
7051
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
7052
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7053
	 * 	@param		int		$forcecombo		    Force to use combo box
7054
	 *  @param      string  $morecss            Add more css on select
7055
	 *  @return     array    				    Array of keys for json
7056
	 */
7057
	public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7058
	{
7059
		global $langs, $conf;
7060
7061
		$out = '';
7062
		$outarray = array();
7063
7064
		$selectFields = " p.rowid, p.ref";
7065
7066
		$sql = "SELECT ";
7067
		$sql .= $selectFields;
7068
		$sql .= " FROM ".$this->db->prefix()."projet as p";
7069
		$sql .= ' WHERE p.entity IN ('.getEntity('project').')';
7070
7071
		// Add criteria on ref/label
7072
		if ($filterkey != '') {
7073
			$sql .= ' AND (';
7074
			$prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7075
			// For natural search
7076
			$scrit = explode(' ', $filterkey);
7077
			$i = 0;
7078
			if (count($scrit) > 1) $sql .= "(";
7079
			foreach ($scrit as $crit) {
7080
				if ($i > 0) $sql .= " AND ";
7081
				$sql .= "p.ref LIKE '".$this->db->escape($prefix.$crit)."%'";
7082
				$sql .= "";
7083
				$i++;
7084
			}
7085
			if (count($scrit) > 1) $sql .= ")";
7086
			$sql .= ')';
7087
		}
7088
7089
		$sql .= $this->db->plimit($limit, 0);
7090
7091
		// Build output string
7092
		dol_syslog(get_class($this)."::selectProjectsList search projects", LOG_DEBUG);
7093
		$result = $this->db->query($sql);
7094
		if ($result) {
7095
			require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
7096
			require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
7097
7098
			$num = $this->db->num_rows($result);
7099
7100
			$events = null;
7101
7102
			if (!$forcecombo) {
7103
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7104
				$out .= ajax_combobox($htmlname, $events, $conf->global->PROJECT_USE_SEARCH_TO_SELECT);
7105
			}
7106
7107
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
7108
7109
			$textifempty = '';
7110
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7111
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7112
			if (!empty($conf->global->PROJECT_USE_SEARCH_TO_SELECT)) {
7113
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7114
				else $textifempty .= $langs->trans("All");
7115
			} else {
7116
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7117
			}
7118
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
7119
7120
			$i = 0;
7121
			while ($num && $i < $num) {
7122
				$opt = '';
7123
				$optJson = array();
7124
				$objp = $this->db->fetch_object($result);
7125
7126
				$this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
7127
				// Add new entry
7128
				// "key" value of json key array is used by jQuery automatically as selected value
7129
				// "label" value of json key array is used by jQuery automatically as text for combo box
7130
				$out .= $opt;
7131
				array_push($outarray, $optJson);
7132
7133
				$i++;
7134
			}
7135
7136
			$out .= '</select>';
7137
7138
			$this->db->free($result);
7139
7140
			if (empty($outputmode)) return $out;
7141
			return $outarray;
7142
		} else {
7143
			dol_print_error($this->db);
7144
		}
7145
	}
7146
7147
	/**
7148
	 * constructProjectListOption.
7149
	 * This define value for &$opt and &$optJson.
7150
	 *
7151
	 * @param 	resource	$objp			    Result set of fetch
7152
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
7153
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
7154
	 * @param 	string		$selected		    Preselected value
7155
	 * @param   string      $filterkey          Filter key to highlight
7156
	 * @return	void
7157
	 */
7158
	protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7159
	{
7160
		$outkey = '';
7161
		$outval = '';
7162
		$outref = '';
7163
		$outlabel = '';
7164
		$outtype = '';
7165
7166
		$label = $objp->label;
7167
7168
		$outkey = $objp->rowid;
7169
		$outref = $objp->ref;
7170
		$outlabel = $objp->label;
7171
		$outtype = $objp->fk_product_type;
7172
7173
		$opt = '<option value="'.$objp->rowid.'"';
7174
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
7175
		$opt .= '>';
7176
		$opt .= $objp->ref;
7177
		$objRef = $objp->ref;
7178
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
7179
		$outval .= $objRef;
7180
7181
		$opt .= "</option>\n";
7182
		$optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtype);
7183
	}
7184
7185
7186
	/**
7187
	 *  Return list of members in Ajax if Ajax activated or go to selectTicketsList
7188
	 *
7189
	 *  @param		int			$selected				Preselected tickets
7190
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
7191
	 *  @param  	string		$filtertype     		To add a filter
7192
	 *  @param		int			$limit					Limit on number of returned lines
7193
	 *  @param		int			$status					Ticket status
7194
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
7195
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon before and placeholder, 3 search icon after)
7196
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
7197
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
7198
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7199
	 * 	@param		int			$forcecombo				Force to use combo box
7200
	 *  @param      string      $morecss                Add more css on select
7201
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
7202
	 *  @param		string		$nooutput				No print, return the output into a string
7203
	 *  @return		void|string
7204
	 */
7205
	public function selectMembers($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7206
	{
7207
		global $langs, $conf;
7208
7209
		$out = '';
7210
7211
		// check parameters
7212
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
7213
7214
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
7215
			$placeholder = '';
7216
7217
			if ($selected && empty($selected_input_value)) {
7218
				require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
7219
				$adherenttmpselect = new Member($this->db);
7220
				$adherenttmpselect->fetch($selected);
7221
				$selected_input_value = $adherenttmpselect->ref;
7222
				unset($adherenttmpselect);
7223
			}
7224
7225
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/adherents/ajax/adherents.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...
7226
7227
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
7228
			elseif ($hidelabel > 1) {
7229
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
7230
				if ($hidelabel == 2) {
7231
					$out .= img_picto($langs->trans("Search"), 'search');
7232
				}
7233
			}
7234
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
7235
			if ($hidelabel == 3) {
7236
				$out .= img_picto($langs->trans("Search"), 'search');
7237
			}
7238
		} else {
7239
			$out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
7240
		}
7241
7242
		if (empty($nooutput)) print $out;
7243
		else return $out;
7244
	}
7245
7246
	/**
7247
	 *	Return list of adherents.
7248
	 *  Called by selectMembers.
7249
	 *
7250
	 *	@param      int		$selected           Preselected adherent
7251
	 *	@param      string	$htmlname           Name of select html
7252
	 *  @param		string	$filtertype         Filter on adherent type
7253
	 *	@param      int		$limit              Limit on number of returned lines
7254
	 * 	@param      string	$filterkey          Filter on adherent ref or subject
7255
	 *	@param		int		$status             Ticket status
7256
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
7257
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7258
	 * 	@param		int		$forcecombo		    Force to use combo box
7259
	 *  @param      string  $morecss            Add more css on select
7260
	 *  @return     array    				    Array of keys for json
7261
	 */
7262
	public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7263
	{
7264
		global $langs, $conf;
7265
7266
		$out = '';
7267
		$outarray = array();
7268
7269
		$selectFields = " p.rowid, p.ref";
7270
7271
		$sql = "SELECT ";
7272
		$sql .= $selectFields;
7273
		$sql .= " FROM ".$this->db->prefix()."adherent as p";
7274
		$sql .= ' WHERE p.entity IN ('.getEntity('adherent').')';
7275
7276
		// Add criteria on ref/label
7277
		if ($filterkey != '') {
7278
			$sql .= ' AND (';
7279
			$prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7280
			// For natural search
7281
			$scrit = explode(' ', $filterkey);
7282
			$i = 0;
7283
			if (count($scrit) > 1) $sql .= "(";
7284
			foreach ($scrit as $crit) {
7285
				if ($i > 0) $sql .= " AND ";
7286
				$sql .= "p.ref LIKE '".$this->db->escape($prefix.$crit)."%'";
7287
				$sql .= "";
7288
				$i++;
7289
			}
7290
			if (count($scrit) > 1) $sql .= ")";
7291
			$sql .= ')';
7292
		}
7293
7294
		$sql .= $this->db->plimit($limit, 0);
7295
7296
		// Build output string
7297
		dol_syslog(get_class($this)."::selectMembersList search adherents", LOG_DEBUG);
7298
		$result = $this->db->query($sql);
7299
		if ($result) {
7300
			require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
7301
			require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
7302
7303
			$num = $this->db->num_rows($result);
7304
7305
			$events = null;
7306
7307
			if (!$forcecombo) {
7308
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7309
				$out .= ajax_combobox($htmlname, $events, $conf->global->PROJECT_USE_SEARCH_TO_SELECT);
7310
			}
7311
7312
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
7313
7314
			$textifempty = '';
7315
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7316
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7317
			if (!empty($conf->global->PROJECT_USE_SEARCH_TO_SELECT)) {
7318
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7319
				else $textifempty .= $langs->trans("All");
7320
			} else {
7321
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7322
			}
7323
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
7324
7325
			$i = 0;
7326
			while ($num && $i < $num) {
7327
				$opt = '';
7328
				$optJson = array();
7329
				$objp = $this->db->fetch_object($result);
7330
7331
				$this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
7332
				// Add new entry
7333
				// "key" value of json key array is used by jQuery automatically as selected value
7334
				// "label" value of json key array is used by jQuery automatically as text for combo box
7335
				$out .= $opt;
7336
				array_push($outarray, $optJson);
7337
7338
				$i++;
7339
			}
7340
7341
			$out .= '</select>';
7342
7343
			$this->db->free($result);
7344
7345
			if (empty($outputmode)) return $out;
7346
			return $outarray;
7347
		} else {
7348
			dol_print_error($this->db);
7349
		}
7350
	}
7351
7352
	/**
7353
	 * constructMemberListOption.
7354
	 * This define value for &$opt and &$optJson.
7355
	 *
7356
	 * @param 	resource	$objp			    Result set of fetch
7357
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
7358
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
7359
	 * @param 	string		$selected		    Preselected value
7360
	 * @param   string      $filterkey          Filter key to highlight
7361
	 * @return	void
7362
	 */
7363
	protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7364
	{
7365
		$outkey = '';
7366
		$outval = '';
7367
		$outref = '';
7368
		$outlabel = '';
7369
		$outtype = '';
7370
7371
		$label = $objp->label;
7372
7373
		$outkey = $objp->rowid;
7374
		$outref = $objp->ref;
7375
		$outlabel = $objp->label;
7376
		$outtype = $objp->fk_product_type;
7377
7378
		$opt = '<option value="'.$objp->rowid.'"';
7379
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
7380
		$opt .= '>';
7381
		$opt .= $objp->ref;
7382
		$objRef = $objp->ref;
7383
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
7384
		$outval .= $objRef;
7385
7386
		$opt .= "</option>\n";
7387
		$optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtype);
7388
	}
7389
7390
	/**
7391
	 * Generic method to select a component from a combo list.
7392
	 * Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.
7393
	 * This is the generic method that will replace all specific existing methods.
7394
	 *
7395
	 * @param 	string			$objectdesc			ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]
7396
	 * @param	string			$htmlname			Name of HTML select component
7397
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
7398
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
7399
	 * @param	string			$searchkey			Search criteria
7400
	 * @param	string			$placeholder		Place holder
7401
	 * @param	string			$morecss			More CSS
7402
	 * @param	string			$moreparams			More params provided to ajax call
7403
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
7404
	 * @param	int				$disabled			1=Html component is disabled
7405
	 * @param	string	        $selected_input_value	Value of preselected input text (for use with ajax)
7406
	 * @return	string								Return HTML string
7407
	 * @see selectForFormsList() select_thirdparty_list()
7408
	 */
7409
	public function selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '')
7410
	{
7411
		global $conf, $user;
7412
7413
		$objecttmp = null;
7414
7415
		// Example of value for $objectdec:
7416
		// Bom:bom/class/bom.class.php:0:t.status=1
7417
		// Bom:bom/class/bom.class.php:0:t.status=1:ref
7418
		// Bom:bom/class/bom.class.php:0:(t.status:=:1):ref
7419
		$InfoFieldList = explode(":", $objectdesc, 4);
7420
		$vartmp = $InfoFieldList[3];
7421
		$reg = array();
7422
		if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
7423
			$InfoFieldList[4] = $reg[1];	// take the sort field
7424
		}
7425
		$InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp);	// take the filter field
7426
7427
		$classname = $InfoFieldList[0];
7428
		$classpath = $InfoFieldList[1];
7429
		$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
7430
		$filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
7431
		$sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
7432
7433
		if (!empty($classpath)) {
7434
			dol_include_once($classpath);
7435
7436
			if ($classname && class_exists($classname)) {
7437
				$objecttmp = new $classname($this->db);
7438
				// Make some replacement
7439
				$sharedentities = getEntity(strtolower($classname));
7440
				$objecttmp->filter = str_replace(
7441
					array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
7442
					array($conf->entity, $sharedentities, $user->id),
7443
					$filter
7444
				);
7445
			}
7446
		}
7447
		if (!is_object($objecttmp)) {
7448
			dol_syslog('Error bad setup of type for field '.$InfoFieldList, LOG_WARNING);
7449
			return 'Error bad setup of type for field '.join(',', $InfoFieldList);
7450
		}
7451
7452
		//var_dump($objecttmp->filter);
7453
		$prefixforautocompletemode = $objecttmp->element;
7454
		if ($prefixforautocompletemode == 'societe') {
7455
			$prefixforautocompletemode = 'company';
7456
		}
7457
		if ($prefixforautocompletemode == 'product') {
7458
			$prefixforautocompletemode = 'produit';
7459
		}
7460
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
7461
7462
		dol_syslog(get_class($this)."::selectForForms object->filter=".$objecttmp->filter, LOG_DEBUG);
7463
		$out = '';
7464
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->$confkeyforautocompletemode) && !$forcecombo) {
7465
			// No immediate load of all database
7466
			$placeholder = '';
7467
			if ($preselectedvalue && empty($selected_input_value)) {
7468
				$objecttmp->fetch($preselectedvalue);
7469
				$selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
7470
				//unset($objecttmp);
7471
			}
7472
7473
			$objectdesc = $classname.':'.$classpath.':'.$addcreatebuttonornot.':'.$filter;
7474
			$urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php';
7475
7476
			// No immediate load of all database
7477
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&objectdesc='.urlencode($objectdesc).'&filter='.urlencode($objecttmp->filter).($sortfield ? '&sortfield='.urlencode($sortfield) : '');
7478
			// Activate the auto complete using ajax call.
7479
			$out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
7480
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
7481
			$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).'"' : '') .' />';
7482
		} else {
7483
			// Immediate load of table record. Note: filter is inside $objecttmp->filter
7484
			$out .= $this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield);
7485
		}
7486
7487
		return $out;
7488
	}
7489
7490
	/**
7491
	 * Function to forge a SQL criteria
7492
	 *
7493
	 * @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"
7494
	 * @return string                  Forged criteria. Example: "t.field like 'abc%'"
7495
	 */
7496
	protected static function forgeCriteriaCallback($matches)
7497
	{
7498
		global $db;
7499
7500
		//dol_syslog("Convert matches ".$matches[1]);
7501
		if (empty($matches[1])) {
7502
			return '';
7503
		}
7504
		$tmp = explode(':', $matches[1]);
7505
		if (count($tmp) < 3) {
7506
			return '';
7507
		}
7508
7509
		$tmpescaped = $tmp[2];
7510
		$regbis = array();
7511
		if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
7512
			$tmpescaped = "'".$db->escape($regbis[1])."'";
7513
		} else {
7514
			$tmpescaped = $db->escape($tmpescaped);
7515
		}
7516
		return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped;
7517
	}
7518
7519
	/**
7520
	 * Output html form to select an object.
7521
	 * Note, this function is called by selectForForms or by ajax selectobject.php
7522
	 *
7523
	 * @param 	Object			$objecttmp			Object to knwo the table to scan for combo.
7524
	 * @param	string			$htmlname			Name of HTML select component
7525
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
7526
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
7527
	 * @param	string			$searchkey			Search value
7528
	 * @param	string			$placeholder		Place holder
7529
	 * @param	string			$morecss			More CSS
7530
	 * @param	string			$moreparams			More params provided to ajax call
7531
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
7532
	 * @param	int				$outputmode			0=HTML select string, 1=Array
7533
	 * @param	int				$disabled			1=Html component is disabled
7534
	 * @param	string			$sortfield			Sort field
7535
	 * @return	string|array						Return HTML string
7536
	 * @see selectForForms()
7537
	 */
7538
	public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '')
7539
	{
7540
		global $conf, $langs, $user, $hookmanager;
7541
7542
		//print "$objecttmp->filter, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled";
7543
7544
		$prefixforautocompletemode = $objecttmp->element;
7545
		if ($prefixforautocompletemode == 'societe') {
7546
			$prefixforautocompletemode = 'company';
7547
		}
7548
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
7549
7550
		if (!empty($objecttmp->fields)) {	// For object that declare it, it is better to use declared fields (like societe, contact, ...)
7551
			$tmpfieldstoshow = '';
7552
			foreach ($objecttmp->fields as $key => $val) {
7553
				if (!dol_eval($val['enabled'], 1, 1, 1, '1')) {
0 ignored issues
show
Unused Code introduced by
The call to dol_eval() has too many arguments starting with '1'. ( Ignorable by Annotation )

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

7553
				if (!/** @scrutinizer ignore-call */ dol_eval($val['enabled'], 1, 1, 1, '1')) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
7554
					continue;
7555
				}
7556
				if (!empty($val['showoncombobox'])) {
7557
					$tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '').'t.'.$key;
7558
				}
7559
			}
7560
			if ($tmpfieldstoshow) {
7561
				$fieldstoshow = $tmpfieldstoshow;
7562
			}
7563
		} else {
7564
			// For backward compatibility
7565
			$objecttmp->fields['ref'] = array('type'=>'varchar(30)', 'label'=>'Ref', 'showoncombobox'=>1);
7566
		}
7567
7568
		if (empty($fieldstoshow)) {
7569
			if (isset($objecttmp->fields['ref'])) {
7570
				$fieldstoshow = 't.ref';
7571
			} else {
7572
				$langs->load("errors");
7573
				$this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
7574
				return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
7575
			}
7576
		}
7577
7578
		$out = '';
7579
		$outarray = array();
7580
7581
		$num = 0;
7582
7583
		// Search data
7584
		$sql = "SELECT t.rowid, ".$fieldstoshow." FROM ".$this->db->prefix().$objecttmp->table_element." as t";
7585
		if (isset($objecttmp->ismultientitymanaged)) {
7586
			if (!is_numeric($objecttmp->ismultientitymanaged)) {
7587
				$tmparray = explode('@', $objecttmp->ismultientitymanaged);
7588
				$sql .= " INNER JOIN ".$this->db->prefix().$tmparray[1]." as parenttable ON parenttable.rowid = t.".$tmparray[0];
7589
			}
7590
			if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
7591
				if (empty($user->rights->societe->client->voir) && !$user->socid) {
7592
					$sql .= ", ".$this->db->prefix()."societe_commerciaux as sc";
7593
				}
7594
			}
7595
		}
7596
7597
		// Add where from hooks
7598
		$parameters = array();
7599
		$reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
7600
		if (!empty($hookmanager->resPrint)) {
7601
			$sql .= $hookmanager->resPrint;
7602
		} else {
7603
			$sql .= " WHERE 1=1";
7604
			if (isset($objecttmp->ismultientitymanaged)) {
7605
				if ($objecttmp->ismultientitymanaged == 1) {
7606
					$sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
7607
				}
7608
				if (!is_numeric($objecttmp->ismultientitymanaged)) {
7609
					$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...
7610
				}
7611
				if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
7612
					if ($objecttmp->element == 'societe') {
7613
						$sql .= " AND t.rowid = ".((int) $user->socid);
7614
					} else {
7615
						$sql .= " AND t.fk_soc = ".((int) $user->socid);
7616
					}
7617
				}
7618
				if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
7619
					if (empty($user->rights->societe->client->voir) && !$user->socid) {
7620
						$sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
7621
					}
7622
				}
7623
			}
7624
			if ($searchkey != '') {
7625
				$sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
7626
			}
7627
			if ($objecttmp->filter) {	 // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
7628
				/*if (! DolibarrApi::_checkFilters($objecttmp->filter))
7629
				{
7630
					throw new RestException(503, 'Error when validating parameter sqlfilters '.$objecttmp->filter);
7631
				}*/
7632
				$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
7633
				$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'Form::forgeCriteriaCallback', $objecttmp->filter).")";
7634
			}
7635
		}
7636
		$sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, "ASC");
7637
		//$sql.=$this->db->plimit($limit, 0);
7638
		//print $sql;
7639
7640
		// Build output string
7641
		$resql = $this->db->query($sql);
7642
		if ($resql) {
7643
			// Construct $out and $outarray
7644
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').($moreparams ? ' '.$moreparams : '').' name="'.$htmlname.'">'."\n";
7645
7646
			// 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
7647
			$textifempty = '&nbsp;';
7648
7649
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7650
			if (!empty($conf->global->$confkeyforautocompletemode)) {
7651
				if ($showempty && !is_numeric($showempty)) {
7652
					$textifempty = $langs->trans($showempty);
7653
				} else {
7654
					$textifempty .= $langs->trans("All");
7655
				}
7656
			}
7657
			if ($showempty) {
7658
				$out .= '<option value="-1">'.$textifempty.'</option>'."\n";
7659
			}
7660
7661
			$num = $this->db->num_rows($resql);
7662
			$i = 0;
7663
			if ($num) {
7664
				while ($i < $num) {
7665
					$obj = $this->db->fetch_object($resql);
7666
					$label = '';
7667
					$tmparray = explode(',', $fieldstoshow);
7668
					$oldvalueforshowoncombobox = 0;
7669
					foreach ($tmparray as $key => $val) {
7670
						$val = preg_replace('/t\./', '', $val);
7671
						$label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
7672
						$label .= $obj->$val;
7673
						$oldvalueforshowoncombobox = $objecttmp->fields[$val]['showoncombobox'];
7674
					}
7675
					if (empty($outputmode)) {
7676
						if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
7677
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
7678
						} else {
7679
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
7680
						}
7681
					} else {
7682
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
7683
					}
7684
7685
					$i++;
7686
					if (($i % 10) == 0) {
7687
						$out .= "\n";
7688
					}
7689
				}
7690
			}
7691
7692
			$out .= '</select>'."\n";
7693
7694
			if (!$forcecombo) {
7695
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7696
				$out .= ajax_combobox($htmlname, null, (!empty($conf->global->$confkeyforautocompletemode) ? $conf->global->$confkeyforautocompletemode : 0));
7697
			}
7698
		} else {
7699
			dol_print_error($this->db);
7700
		}
7701
7702
		$this->result = array('nbofelement'=>$num);
7703
7704
		if ($outputmode) {
7705
			return $outarray;
7706
		}
7707
		return $out;
7708
	}
7709
7710
7711
	/**
7712
	 *	Return a HTML select string, built from an array of key+value.
7713
	 *  Note: Do not apply langs->trans function on returned content, content may be entity encoded twice.
7714
	 *
7715
	 *	@param	string			$htmlname			Name of html select area. Must start with "multi" if this is a multiselect
7716
	 *	@param	array			$array				Array like array(key => value) or array(key=>array('label'=>..., 'data-...'=>..., 'disabled'=>..., 'css'=>...))
7717
	 *	@param	string|string[]	$id					Preselected key or preselected keys for multiselect
7718
	 *	@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.
7719
	 *	@param	int				$key_in_label		1 to show key into label with format "[key] value"
7720
	 *	@param	int				$value_as_key		1 to use value as key
7721
	 *	@param  string			$moreparam			Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
7722
	 *	@param  int				$translate			1=Translate and encode value
7723
	 * 	@param	int				$maxlen				Length maximum for labels
7724
	 * 	@param	int				$disabled			Html select box is disabled
7725
	 *  @param	string			$sort				'ASC' or 'DESC' = Sort on label, '' or 'NONE' or 'POS' = Do not sort, we keep original order
7726
	 *  @param	string			$morecss			Add more class to css styles
7727
	 *  @param	int				$addjscombo			Add js combo
7728
	 *  @param  string          $moreparamonempty	Add more param on the empty option line. Not used if show_empty not set
7729
	 *  @param  int             $disablebademail	1=Check if a not valid email, 2=Check string '---', and if found into value, disable and colorize entry
7730
	 *  @param  int             $nohtmlescape		No html escaping.
7731
	 * 	@return	string								HTML select string.
7732
	 *  @see multiselectarray(), selectArrayAjax(), selectArrayFilter()
7733
	 */
7734
	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)
7735
	{
7736
		global $conf, $langs;
7737
7738
		// Do we want a multiselect ?
7739
		//$jsbeautify = 0;
7740
		//if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
7741
		$jsbeautify = 1;
7742
7743
		if ($value_as_key) {
7744
			$array = array_combine($array, $array);
7745
		}
7746
7747
		$out = '';
7748
7749
		if ($addjscombo < 0) {
7750
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7751
				$addjscombo = 1;
7752
			} else {
7753
				$addjscombo = 0;
7754
			}
7755
		}
7756
7757
		$out .= '<select id="'.preg_replace('/^\./', '', $htmlname).'" '.($disabled ? 'disabled="disabled" ' : '').'class="flat '.(preg_replace('/^\./', '', $htmlname)).($morecss ? ' '.$morecss : '').'"';
7758
		$out .= ' name="'.preg_replace('/^\./', '', $htmlname).'" '.($moreparam ? $moreparam : '');
7759
		$out .= '>';
7760
7761
		if ($show_empty) {
7762
			$textforempty = ' ';
7763
			if (!empty($conf->use_javascript_ajax)) {
7764
				$textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
7765
			}
7766
			if (!is_numeric($show_empty)) {
7767
				$textforempty = $show_empty;
7768
			}
7769
			$out .= '<option class="optiongrey" '.($moreparamonempty ? $moreparamonempty.' ' : '').'value="'.($show_empty < 0 ? $show_empty : -1).'"'.($id == $show_empty ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
7770
		}
7771
7772
		if (is_array($array)) {
7773
			// Translate
7774
			if ($translate) {
7775
				foreach ($array as $key => $value) {
7776
					if (!is_array($value)) {
7777
						$array[$key] = $langs->trans($value);
7778
					} else {
7779
						$array[$key]['label'] = $langs->trans($value['label']);
7780
					}
7781
				}
7782
			}
7783
7784
			// Sort
7785
			if ($sort == 'ASC') {
7786
				asort($array);
7787
			} elseif ($sort == 'DESC') {
7788
				arsort($array);
7789
			}
7790
7791
			foreach ($array as $key => $tmpvalue) {
7792
				if (is_array($tmpvalue)) {
7793
					$value = $tmpvalue['label'];
7794
					$disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
7795
					$style = empty($tmpvalue['css']) ? '' : ' class="'.$tmpvalue['css'].'"';
7796
				} else {
7797
					$value = $tmpvalue;
7798
					$disabled = '';
7799
					$style = '';
7800
				}
7801
				if (!empty($disablebademail)) {
7802
					if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
7803
						|| ($disablebademail == 2 && preg_match('/---/', $value))) {
7804
						$disabled = ' disabled';
7805
						$style = ' class="warning"';
7806
					}
7807
				}
7808
7809
				if ($key_in_label) {
7810
					if (empty($nohtmlescape)) {
7811
						$selectOptionValue = dol_escape_htmltag($key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value));
7812
					} else {
7813
						$selectOptionValue = $key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value);
7814
					}
7815
				} else {
7816
					if (empty($nohtmlescape)) {
7817
						$selectOptionValue = dol_escape_htmltag($maxlen ?dol_trunc($value, $maxlen) : $value);
7818
					} else {
7819
						$selectOptionValue = $maxlen ?dol_trunc($value, $maxlen) : $value;
7820
					}
7821
					if ($value == '' || $value == '-') {
7822
						$selectOptionValue = '&nbsp;';
7823
					}
7824
				}
7825
7826
				$out .= '<option value="'.$key.'"';
7827
				$out .= $style.$disabled;
7828
				if (is_array($id)) {
7829
					if (in_array($key, $id) && !$disabled) {
7830
						$out .= ' selected'; // To preselect a value
7831
					}
7832
				} else {
7833
					$id = (string) $id; // if $id = 0, then $id = '0'
7834
					if ($id != '' && $id == $key && !$disabled) {
7835
						$out .= ' selected'; // To preselect a value
7836
					}
7837
				}
7838
				if ($nohtmlescape) {
7839
					$out .= ' data-html="'.dol_escape_htmltag($selectOptionValue).'"';
7840
				}
7841
				if (is_array($tmpvalue)) {
7842
					foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
7843
						if (preg_match('/^data-/', $keyforvalue)) {
7844
							$out .= ' '.$keyforvalue.'="'.$valueforvalue.'"';
7845
						}
7846
					}
7847
				}
7848
				$out .= '>';
7849
				//var_dump($selectOptionValue);
7850
				$out .= $selectOptionValue;
7851
				$out .= "</option>\n";
7852
			}
7853
		}
7854
7855
		$out .= "</select>";
7856
7857
		// Add code for jquery to use multiselect
7858
		if ($addjscombo && $jsbeautify) {
7859
			// Enhance with select2
7860
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7861
			$out .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', $show_empty < 0 ? (string) $show_empty : '-1');
7862
		}
7863
7864
		return $out;
7865
	}
7866
7867
7868
	/**
7869
	 *	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.
7870
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7871
	 *
7872
	 *	@param	string	$htmlname       		Name of html select area
7873
	 *	@param	string	$url					Url. Must return a json_encode of array(key=>array('text'=>'A text', 'url'=>'An url'), ...)
7874
	 *	@param	string	$id             		Preselected key
7875
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7876
	 *	@param  string	$moreparamtourl 		Add more parameters onto the Ajax called URL
7877
	 * 	@param	int		$disabled				Html select box is disabled
7878
	 *  @param	int		$minimumInputLength		Minimum Input Length
7879
	 *  @param	string	$morecss				Add more class to css styles
7880
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7881
	 *  @param  string  $placeholder            String to use as placeholder
7882
	 *  @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)
7883
	 * 	@return	string   						HTML select string
7884
	 *  @see selectArrayFilter(), ajax_combobox() in ajax.lib.php
7885
	 */
7886
	public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7887
	{
7888
		global $conf, $langs;
7889
		global $delayedhtmlcontent;	// Will be used later outside of this function
7890
7891
		// TODO Use an internal dolibarr component instead of select2
7892
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7893
			return '';
7894
		}
7895
7896
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"></select>';
7897
7898
		$outdelayed = '';
7899
		if (!empty($conf->use_javascript_ajax)) {
7900
			$tmpplugin = 'select2';
7901
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
7902
		    	<script>
7903
		    	$(document).ready(function () {
7904
7905
	    	        '.($callurlonselect ? 'var saveRemoteData = [];' : '').'
7906
7907
	                $(".'.$htmlname.'").select2({
7908
				    	ajax: {
7909
					    	dir: "ltr",
7910
					    	url: "'.$url.'",
7911
					    	dataType: \'json\',
7912
					    	delay: 250,
7913
					    	data: function (params) {
7914
					    		return {
7915
							    	q: params.term, 	// search term
7916
					    			page: params.page
7917
					    		};
7918
				    		},
7919
				    		processResults: function (data) {
7920
				    			// parse the results into the format expected by Select2.
7921
				    			// since we are using custom formatting functions we do not need to alter the remote JSON data
7922
				    			//console.log(data);
7923
								saveRemoteData = data;
7924
					    	    /* format json result for select2 */
7925
					    	    result = []
7926
					    	    $.each( data, function( key, value ) {
7927
					    	       result.push({id: key, text: value.text});
7928
	                            });
7929
				    			//return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
7930
				    			//console.log(result);
7931
				    			return {results: result, more: false}
7932
				    		},
7933
				    		cache: true
7934
				    	},
7935
		 				language: select2arrayoflanguage,
7936
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
7937
					    placeholder: "'.dol_escape_js($placeholder).'",
7938
				    	escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
7939
				    	minimumInputLength: '.$minimumInputLength.',
7940
				        formatResult: function(result, container, query, escapeMarkup) {
7941
	                        return escapeMarkup(result.text);
7942
	                    },
7943
				    });
7944
7945
	                '.($callurlonselect ? '
7946
	                /* Code to execute a GET when we select a value */
7947
	                $(".'.$htmlname.'").change(function() {
7948
				    	var selected = $(".'.$htmlname.'").val();
7949
	                	console.log("We select in selectArrayAjax the entry "+selected)
7950
				        $(".'.$htmlname.'").val("");  /* reset visible combo value */
7951
	    			    $.each( saveRemoteData, function( key, value ) {
7952
	    				        if (key == selected)
7953
	    			            {
7954
	    			                 console.log("selectArrayAjax - Do a redirect to "+value.url)
7955
	    			                 location.assign(value.url);
7956
	    			            }
7957
	                    });
7958
	    			});' : '').'
7959
7960
	    	   });
7961
		       </script>';
7962
		}
7963
7964
		if ($acceptdelayedhtml) {
7965
			$delayedhtmlcontent .= $outdelayed;
7966
		} else {
7967
			$out .= $outdelayed;
7968
		}
7969
		return $out;
7970
	}
7971
7972
	/**
7973
	 *  Return a HTML select string, built from an array of key+value, but content returned into select is defined into $array parameter.
7974
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
7975
	 *
7976
	 *  @param  string	$htmlname               Name of html select area
7977
	 *	@param	array	$array					Array (key=>array('text'=>'A text', 'url'=>'An url'), ...)
7978
	 *	@param	string	$id             		Preselected key
7979
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
7980
	 *	@param	int		$disableFiltering		If set to 1, results are not filtered with searched string
7981
	 * 	@param	int		$disabled				Html select box is disabled
7982
	 *  @param	int		$minimumInputLength		Minimum Input Length
7983
	 *  @param	string	$morecss				Add more class to css styles
7984
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
7985
	 *  @param  string  $placeholder            String to use as placeholder
7986
	 *  @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)
7987
	 *  @return	string   						HTML select string
7988
	 *  @see selectArrayAjax(), ajax_combobox() in ajax.lib.php
7989
	 */
7990
	public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
7991
	{
7992
		global $conf, $langs;
7993
		global $delayedhtmlcontent;	// Will be used later outside of this function
7994
7995
		// TODO Use an internal dolibarr component instead of select2
7996
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
7997
			return '';
7998
		}
7999
8000
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"><option></option></select>';
8001
8002
		$formattedarrayresult = array();
8003
8004
		foreach ($array as $key => $value) {
8005
			$o = new stdClass();
8006
			$o->id = $key;
8007
			$o->text = $value['text'];
8008
			$o->url = $value['url'];
8009
			$formattedarrayresult[] = $o;
8010
		}
8011
8012
		$outdelayed = '';
8013
		if (!empty($conf->use_javascript_ajax)) {
8014
			$tmpplugin = 'select2';
8015
			$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
8016
				<script>
8017
				$(document).ready(function () {
8018
					var data = '.json_encode($formattedarrayresult).';
8019
8020
					'.($callurlonselect ? 'var saveRemoteData = '.json_encode($array).';' : '').'
8021
8022
					$(".'.$htmlname.'").select2({
8023
						data: data,
8024
						language: select2arrayoflanguage,
8025
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
8026
						placeholder: "'.dol_escape_js($placeholder).'",
8027
						escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
8028
						minimumInputLength: '.$minimumInputLength.',
8029
						formatResult: function(result, container, query, escapeMarkup) {
8030
							return escapeMarkup(result.text);
8031
						},
8032
						matcher: function (params, data) {
8033
8034
							if(! data.id) return null;';
8035
8036
			if ($callurlonselect) {
8037
				$outdelayed .= '
8038
8039
							var urlBase = data.url;
8040
							var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
8041
							/* console.log("params.term="+params.term); */
8042
							/* console.log("params.term encoded="+encodeURIComponent(params.term)); */
8043
							saveRemoteData[data.id].url = urlBase + separ + "sall=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
8044
			}
8045
8046
			if (!$disableFiltering) {
8047
				$outdelayed .= '
8048
8049
							if(data.text.match(new RegExp(params.term))) {
8050
								return data;
8051
							}
8052
8053
							return null;';
8054
			} else {
8055
				$outdelayed .= '
8056
8057
							return data;';
8058
			}
8059
8060
			$outdelayed .= '
8061
						}
8062
					});
8063
8064
					'.($callurlonselect ? '
8065
					/* Code to execute a GET when we select a value */
8066
					$(".'.$htmlname.'").change(function() {
8067
						var selected = $(".'.$htmlname.'").val();
8068
						console.log("We select "+selected)
8069
8070
						$(".'.$htmlname.'").val("");  /* reset visible combo value */
8071
						$.each( saveRemoteData, function( key, value ) {
8072
							if (key == selected)
8073
							{
8074
								console.log("selectArrayFilter - Do a redirect to "+value.url)
8075
								location.assign(value.url);
8076
							}
8077
						});
8078
					});' : '').'
8079
8080
				});
8081
				</script>';
8082
		}
8083
8084
		if ($acceptdelayedhtml) {
8085
			$delayedhtmlcontent .= $outdelayed;
8086
		} else {
8087
			$out .= $outdelayed;
8088
		}
8089
		return $out;
8090
	}
8091
8092
	/**
8093
	 *	Show a multiselect form from an array. WARNING: Use this only for short lists.
8094
	 *
8095
	 *	@param	string		$htmlname		Name of select
8096
	 *	@param	array		$array			Array with key+value
8097
	 *	@param	array		$selected		Array with key+value preselected
8098
	 *	@param	int			$key_in_label   1 to show key like in "[key] value"
8099
	 *	@param	int			$value_as_key   1 to use value as key
8100
	 *	@param  string		$morecss        Add more css style
8101
	 *	@param  int			$translate		Translate and encode value
8102
	 *  @param	int|string	$width			Force width of select box. May be used only when using jquery couch. Example: 250, '95%'
8103
	 *  @param	string		$moreattrib		Add more options on select component. Example: 'disabled'
8104
	 *  @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.
8105
	 *  @param	string		$placeholder	String to use as placeholder
8106
	 *  @param	int			$addjscombo		Add js combo
8107
	 *	@return	string						HTML multiselect string
8108
	 *  @see selectarray(), selectArrayAjax(), selectArrayFilter()
8109
	 */
8110
	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)
8111
	{
8112
		global $conf, $langs;
8113
8114
		$out = '';
8115
8116
		if ($addjscombo < 0) {
8117
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
8118
				$addjscombo = 1;
8119
			} else {
8120
				$addjscombo = 0;
8121
			}
8122
		}
8123
8124
		// Try also magic suggest
8125
		$out .= '<select id="'.$htmlname.'" class="multiselect'.($morecss ? ' '.$morecss : '').'" multiple name="'.$htmlname.'[]"'.($moreattrib ? ' '.$moreattrib : '').($width ? ' style="width: '.(preg_match('/%/', $width) ? $width : $width.'px').'"' : '').'>'."\n";
8126
		if (is_array($array) && !empty($array)) {
8127
			if ($value_as_key) {
8128
				$array = array_combine($array, $array);
8129
			}
8130
8131
			if (!empty($array)) {
8132
				foreach ($array as $key => $value) {
8133
					$newval = ($translate ? $langs->trans($value) : $value);
8134
					$newval = ($key_in_label ? $key.' - '.$newval : $newval);
8135
8136
					$out .= '<option value="'.$key.'"';
8137
					if (is_array($selected) && !empty($selected) && in_array((string) $key, $selected) && ((string) $key != '')) {
8138
						$out .= ' selected';
8139
					}
8140
					$out .= ' data-html="'.dol_escape_htmltag($newval).'"';
8141
					$out .= '>';
8142
					$out .= dol_htmlentitiesbr($newval);
8143
					$out .= '</option>'."\n";
8144
				}
8145
			}
8146
		}
8147
		$out .= '</select>'."\n";
8148
8149
		// Add code for jquery to use multiselect
8150
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
8151
			$out .= "\n".'<!-- JS CODE TO ENABLE select for id '.$htmlname.', addjscombo='.$addjscombo.' -->';
8152
			$out .= "\n".'<script>'."\n";
8153
			if ($addjscombo == 1) {
8154
				$tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
8155
				$out .= 'function formatResult(record, container) {'."\n";
8156
				$out .= '	if ($(record.element).attr("data-html") != undefined) return htmlEntityDecodeJs($(record.element).attr("data-html"));		// If property html set, we decode html entities and use this'."\n";
8157
				$out .= '	return record.text;';
8158
				$out .= '};'."\n";
8159
				$out .= 'function formatSelection(record) {'."\n";
8160
				if ($elemtype == 'category') {
8161
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
8162
				} else {
8163
					$out .= 'return record.text;';
8164
				}
8165
				$out .= '};'."\n";
8166
				$out .= '$(document).ready(function () {
8167
							$(\'#'.$htmlname.'\').'.$tmpplugin.'({';
8168
				if ($placeholder) {
8169
					$out .= '
8170
								placeholder: {
8171
								    id: \'-1\',
8172
								    text: \''.dol_escape_js($placeholder).'\'
8173
								  },';
8174
				}
8175
				$out .= '		dir: \'ltr\',
8176
								// Specify format function for dropdown item
8177
								formatResult: formatResult,
8178
							 	templateResult: formatResult,		/* For 4.0 */
8179
								escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
8180
								// Specify format function for selected item
8181
								formatSelection: formatSelection,
8182
							 	templateSelection: formatSelection		/* For 4.0 */
8183
							});
8184
8185
							/* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
8186
								 the size only if component is not hidden by default on load */
8187
							$(\'#'.$htmlname.' + .select2\').addClass(\''.$morecss.'\');
8188
						});'."\n";
8189
			} elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
8190
				// Add other js lib
8191
				// TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
8192
				// ...
8193
				$out .= 'console.log(\'addjscombo=2 for htmlname='.$htmlname.'\');';
8194
				$out .= '$(document).ready(function () {
8195
							$(\'#'.$htmlname.'\').multiSelect({
8196
								containerHTML: \'<div class="multi-select-container">\',
8197
								menuHTML: \'<div class="multi-select-menu">\',
8198
								buttonHTML: \'<span class="multi-select-button '.$morecss.'">\',
8199
								menuItemHTML: \'<label class="multi-select-menuitem">\',
8200
								activeClass: \'multi-select-container--open\',
8201
								noneText: \''.$placeholder.'\'
8202
							});
8203
						})';
8204
			}
8205
			$out .= '</script>';
8206
		}
8207
8208
		return $out;
8209
	}
8210
8211
8212
	/**
8213
	 *	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.
8214
	 *
8215
	 *	@param	string	$htmlname		Name of HTML field
8216
	 *	@param	array	$array			Array with array of fields we could show. This array may be modified according to setup of user.
8217
	 *  @param  string  $varpage        Id of context for page. Can be set by caller with $varpage=(empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage);
8218
	 *	@return	string					HTML multiselect string
8219
	 *  @see selectarray()
8220
	 */
8221
	public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage)
8222
	{
8223
		global $conf, $langs, $user, $extrafields;
8224
8225
		if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
8226
			return '';
8227
		}
8228
8229
		$tmpvar = "MAIN_SELECTEDFIELDS_".$varpage; // To get list of saved selected fields to show
8230
8231
		if (!empty($user->conf->$tmpvar)) {		// A list of fields was already customized for user
8232
			$tmparray = explode(',', $user->conf->$tmpvar);
8233
			foreach ($array as $key => $val) {
8234
				//var_dump($key);
8235
				//var_dump($tmparray);
8236
				if (in_array($key, $tmparray)) {
8237
					$array[$key]['checked'] = 1;
8238
				} else {
8239
					$array[$key]['checked'] = 0;
8240
				}
8241
			}
8242
		} else {								// There is no list of fields already customized for user
8243
			foreach ($array as $key => $val) {
8244
				if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
8245
					$array[$key]['checked'] = 0;
8246
				}
8247
			}
8248
		}
8249
8250
		$listoffieldsforselection = '';
8251
		$listcheckedstring = '';
8252
8253
		foreach ($array as $key => $val) {
8254
			/* var_dump($val);
8255
			var_dump(array_key_exists('enabled', $val));
8256
			var_dump(!$val['enabled']);*/
8257
			if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
8258
				unset($array[$key]); // We don't want this field
8259
				continue;
8260
			}
8261
			if (!empty($val['type']) && $val['type'] == 'separate') {
8262
				// Field remains in array but we don't add it into $listoffieldsforselection
8263
				//$listoffieldsforselection .= '<li>-----</li>';
8264
				continue;
8265
			}
8266
			if ($val['label']) {
8267
				if (!empty($val['langfile']) && is_object($langs)) {
8268
					$langs->load($val['langfile']);
8269
				}
8270
8271
				// Note: $val['checked'] <> 0 means we must show the field into the combo list
8272
				$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>';
8273
				$listcheckedstring .= (empty($val['checked']) ? '' : $key.',');
8274
			}
8275
		}
8276
8277
		$out = '<!-- Component multiSelectArrayWithCheckbox '.$htmlname.' -->
8278
8279
        <dl class="dropdown">
8280
            <dt>
8281
            <a href="#'.$htmlname.'">
8282
              '.img_picto('', 'list').'
8283
            </a>
8284
            <input type="hidden" class="'.$htmlname.'" name="'.$htmlname.'" value="'.$listcheckedstring.'">
8285
            </dt>
8286
            <dd class="dropdowndd">
8287
                <div class="multiselectcheckbox'.$htmlname.'">
8288
                    <ul class="ul'.$htmlname.'">
8289
                    '.$listoffieldsforselection.'
8290
                    </ul>
8291
                </div>
8292
            </dd>
8293
        </dl>
8294
8295
        <script type="text/javascript">
8296
          jQuery(document).ready(function () {
8297
              $(\'.multiselectcheckbox'.$htmlname.' input[type="checkbox"]\').on(\'click\', function () {
8298
                  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
8299
8300
                  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\');	// Update field so we know we changed something on selected fields after POST
8301
8302
                  var title = $(this).val() + ",";
8303
                  if ($(this).is(\':checked\')) {
8304
                      $(\'.'.$htmlname.'\').val(title + $(\'.'.$htmlname.'\').val());
8305
                  }
8306
                  else {
8307
                      $(\'.'.$htmlname.'\').val( $(\'.'.$htmlname.'\').val().replace(title, \'\') )
8308
                  }
8309
                  // Now, we submit page
8310
                  //$(this).parents(\'form:first\').submit();
8311
              });
8312
8313
8314
           });
8315
        </script>
8316
8317
        ';
8318
		return $out;
8319
	}
8320
8321
	/**
8322
	 * 	Render list of categories linked to object with id $id and type $type
8323
	 *
8324
	 * 	@param		int		$id				Id of object
8325
	 * 	@param		string	$type			Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
8326
	 *  @param		int		$rendermode		0=Default, use multiselect. 1=Emulate multiselect (recommended)
8327
	 *  @param		int		$nolink			1=Do not add html links
8328
	 * 	@return		string					String with categories
8329
	 */
8330
	public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
8331
	{
8332
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8333
8334
		$cat = new Categorie($this->db);
8335
		$categories = $cat->containing($id, $type);
8336
8337
		if ($rendermode == 1) {
8338
			$toprint = array();
8339
			foreach ($categories as $c) {
8340
				$ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text
8341
				foreach ($ways as $way) {
8342
					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.$way.'</li>';
8343
				}
8344
			}
8345
			return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8346
		}
8347
8348
		if ($rendermode == 0) {
8349
			$arrayselected = array();
8350
			$cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 1);
8351
			foreach ($categories as $c) {
8352
				$arrayselected[] = $c->id;
8353
			}
8354
8355
			return $this->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, '', 0, '100%', 'disabled', 'category');
8356
		}
8357
8358
		return 'ErrorBadValueForParameterRenderMode'; // Should not happened
8359
	}
8360
8361
	/**
8362
	 *  Show linked object block.
8363
	 *
8364
	 *  @param	CommonObject	$object		      Object we want to show links to
8365
	 *  @param  string          $morehtmlright    More html to show on right of title
8366
	 *  @param  array           $compatibleImportElementsList  Array of compatibles elements object for "import from" action
8367
	 *  @return	int							      <0 if KO, >=0 if OK
8368
	 */
8369
	public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = false)
8370
	{
8371
		global $conf, $langs, $hookmanager;
8372
		global $bc, $action;
8373
8374
		$object->fetchObjectLinked();
8375
8376
		// Bypass the default method
8377
		$hookmanager->initHooks(array('commonobject'));
8378
		$parameters = array(
8379
			'morehtmlright' => $morehtmlright,
8380
			'compatibleImportElementsList' => &$compatibleImportElementsList,
8381
		);
8382
		$reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
8383
8384
		if (empty($reshook)) {
8385
			$nbofdifferenttypes = count($object->linkedObjects);
8386
8387
			print '<!-- showLinkedObjectBlock -->';
8388
			print load_fiche_titre($langs->trans('RelatedObjects'), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
8389
8390
8391
			print '<div class="div-table-responsive-no-min">';
8392
			print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="'.$object->element.'"  data-elementid="'.$object->id.'"   >';
8393
8394
			print '<tr class="liste_titre">';
8395
			print '<td>'.$langs->trans("Type").'</td>';
8396
			print '<td>'.$langs->trans("Ref").'</td>';
8397
			print '<td class="center"></td>';
8398
			print '<td class="center">'.$langs->trans("Date").'</td>';
8399
			print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
8400
			print '<td class="right">'.$langs->trans("Status").'</td>';
8401
			print '<td></td>';
8402
			print '</tr>';
8403
8404
			$nboftypesoutput = 0;
8405
8406
			foreach ($object->linkedObjects as $objecttype => $objects) {
8407
				$tplpath = $element = $subelement = $objecttype;
8408
8409
				// to display inport button on tpl
8410
				$showImportButton = false;
8411
				if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
8412
					$showImportButton = true;
8413
				}
8414
8415
				$regs = array();
8416
				if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
8417
					$element = $regs[1];
8418
					$subelement = $regs[2];
8419
					$tplpath = $element.'/'.$subelement;
8420
				}
8421
				$tplname = 'linkedobjectblock';
8422
8423
				// To work with non standard path
8424
				if ($objecttype == 'facture') {
8425
					$tplpath = 'compta/'.$element;
8426
					if (empty($conf->facture->enabled)) {
8427
						continue; // Do not show if module disabled
8428
					}
8429
				} elseif ($objecttype == 'facturerec') {
8430
					$tplpath = 'compta/facture';
8431
					$tplname = 'linkedobjectblockForRec';
8432
					if (empty($conf->facture->enabled)) {
8433
						continue; // Do not show if module disabled
8434
					}
8435
				} elseif ($objecttype == 'propal') {
8436
					$tplpath = 'comm/'.$element;
8437
					if (empty($conf->propal->enabled)) {
8438
						continue; // Do not show if module disabled
8439
					}
8440
				} elseif ($objecttype == 'supplier_proposal') {
8441
					if (empty($conf->supplier_proposal->enabled)) {
8442
						continue; // Do not show if module disabled
8443
					}
8444
				} elseif ($objecttype == 'shipping' || $objecttype == 'shipment') {
8445
					$tplpath = 'expedition';
8446
					if (empty($conf->expedition->enabled)) {
8447
						continue; // Do not show if module disabled
8448
					}
8449
				} elseif ($objecttype == 'reception') {
8450
					$tplpath = 'reception';
8451
					if (empty($conf->reception->enabled)) {
8452
						continue; // Do not show if module disabled
8453
					}
8454
				} elseif ($objecttype == 'delivery') {
8455
					$tplpath = 'delivery';
8456
					if (empty($conf->expedition->enabled)) {
8457
						continue; // Do not show if module disabled
8458
					}
8459
				} elseif ($objecttype == 'mo') {
8460
					$tplpath = 'mrp/mo';
8461
					if (empty($conf->mrp->enabled)) {
8462
						continue; // Do not show if module disabled
8463
					}
8464
				} elseif ($objecttype == 'ficheinter') {
8465
					$tplpath = 'fichinter';
8466
					if (empty($conf->ficheinter->enabled)) {
8467
						continue; // Do not show if module disabled
8468
					}
8469
				} elseif ($objecttype == 'invoice_supplier') {
8470
					$tplpath = 'fourn/facture';
8471
				} elseif ($objecttype == 'order_supplier') {
8472
					$tplpath = 'fourn/commande';
8473
				} elseif ($objecttype == 'expensereport') {
8474
					$tplpath = 'expensereport';
8475
				} elseif ($objecttype == 'subscription') {
8476
					$tplpath = 'adherents';
8477
				} elseif ($objecttype == 'conferenceorbooth') {
8478
					$tplpath = 'eventorganization';
8479
				} elseif ($objecttype == 'conferenceorboothattendee') {
8480
					$tplpath = 'eventorganization';
8481
				} elseif ($objecttype == 'mo') {
8482
					$tplpath = 'mrp';
8483
					if (empty($conf->mrp->enabled)) {
8484
						continue; // Do not show if module disabled
8485
					}
8486
				}
8487
8488
				global $linkedObjectBlock;
8489
				$linkedObjectBlock = $objects;
8490
8491
				// Output template part (modules that overwrite templates must declare this into descriptor)
8492
				$dirtpls = array_merge($conf->modules_parts['tpl'], array('/'.$tplpath.'/tpl'));
8493
				foreach ($dirtpls as $reldir) {
8494
					if ($nboftypesoutput == ($nbofdifferenttypes - 1)) {    // No more type to show after
8495
						global $noMoreLinkedObjectBlockAfter;
8496
						$noMoreLinkedObjectBlockAfter = 1;
8497
					}
8498
8499
					$res = @include dol_buildpath($reldir.'/'.$tplname.'.tpl.php');
8500
					if ($res) {
8501
						$nboftypesoutput++;
8502
						break;
8503
					}
8504
				}
8505
			}
8506
8507
			if (!$nboftypesoutput) {
8508
				print '<tr><td class="impair" colspan="7"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
8509
			}
8510
8511
			print '</table>';
8512
8513
			if (!empty($compatibleImportElementsList)) {
8514
				$res = @include dol_buildpath('core/tpl/ajax/objectlinked_lineimport.tpl.php');
8515
			}
8516
8517
8518
			print '</div>';
8519
8520
			return $nbofdifferenttypes;
8521
		}
8522
	}
8523
8524
	/**
8525
	 *  Show block with links to link to other objects.
8526
	 *
8527
	 *  @param	CommonObject	$object				Object we want to show links to
8528
	 *  @param	array			$restrictlinksto	Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
8529
	 *  @param	array			$excludelinksto		Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
8530
	 *  @return	string								<0 if KO, >0 if OK
8531
	 */
8532
	public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
8533
	{
8534
		global $conf, $langs, $hookmanager;
8535
		global $action;
8536
8537
		$linktoelem = '';
8538
		$linktoelemlist = '';
8539
		$listofidcompanytoscan = '';
8540
8541
		if (!is_object($object->thirdparty)) {
8542
			$object->fetch_thirdparty();
8543
		}
8544
8545
		$possiblelinks = array();
8546
		if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
8547
			$listofidcompanytoscan = $object->thirdparty->id;
8548
			if (($object->thirdparty->parent > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PARENT_IN_LINKTO)) {
8549
				$listofidcompanytoscan .= ','.$object->thirdparty->parent;
8550
			}
8551
			if (($object->fk_project > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO)) {
8552
				include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
8553
				$tmpproject = new Project($this->db);
8554
				$tmpproject->fetch($object->fk_project);
8555
				if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) {
8556
					$listofidcompanytoscan .= ','.$tmpproject->socid;
8557
				}
8558
				unset($tmpproject);
8559
			}
8560
8561
			$possiblelinks = array(
8562
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."propal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('propal').')'),
8563
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."commande as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('commande').')'),
8564
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."facture as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('invoice').')'),
8565
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."facture_rec as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('invoice').')'),
8566
				'contrat'=>array(
8567
					'enabled'=>$conf->contrat->enabled,
8568
					'perms'=>1,
8569
					'label'=>'LinkToContract',
8570
					'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
8571
							FROM ".$this->db->prefix()."societe as s, ".$this->db->prefix()."contrat as t, ".$this->db->prefix()."contratdet as td WHERE t.fk_soc = s.rowid AND td.fk_contrat = t.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('contract').') GROUP BY s.rowid, s.nom, s.client, t.rowid, t.ref, t.ref_customer, t.ref_supplier'
8572
				),
8573
				'fichinter'=>array('enabled'=>!empty($conf->ficheinter->enabled) ? $conf->ficheinter->enabled : 0, 'perms'=>1, 'label'=>'LinkToIntervention', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM ".$this->db->prefix()."societe as s, ".$this->db->prefix()."fichinter as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('intervention').')'),
8574
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."supplier_proposal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('supplier_proposal').')'),
8575
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('commande_fournisseur').')'),
8576
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('facture_fourn').')'),
8577
				'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 ".$this->db->prefix()."societe as s, ".$this->db->prefix()."ticket as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('ticket').')'),
8578
				'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 ".$this->db->prefix()."societe as s INNER JOIN ".$this->db->prefix()."mrp_mo as t ON t.fk_soc = s.rowid  WHERE  t.fk_soc IN (".$this->db->sanitize($listofidcompanytoscan).') AND t.entity IN ('.getEntity('mo').')')
8579
			);
8580
		}
8581
8582
		if (!empty($listofidcompanytoscan)) {  // If empty, we don't have criteria to scan the object we can link to
8583
			// Can complete the possiblelink array
8584
			$hookmanager->initHooks(array('commonobject'));
8585
			$parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
8586
			$reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
8587
		}
8588
8589
		if (empty($reshook)) {
8590
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
8591
				$possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
8592
			}
8593
		} 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...
8594
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
8595
				$possiblelinks = $hookmanager->resArray;
8596
			}
8597
		}
8598
8599
		foreach ($possiblelinks as $key => $possiblelink) {
8600
			$num = 0;
8601
8602
			if (empty($possiblelink['enabled'])) {
8603
				continue;
8604
			}
8605
8606
			if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
8607
				print '<div id="'.$key.'list"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display:none"').'>';
8608
8609
				if (!empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8610
					print '<br><form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
8611
					print '<input type="hidden" name="id" value="' . $object->id . '">';
8612
					print '<input type="hidden" name="action" value="addlinkbyref">';
8613
					print '<input type="hidden" name="token" value="'.newToken().'">';
8614
					print '<input type="hidden" name="addlink" value="' . $key . '">';
8615
					print '<table class="noborder">';
8616
					print '<tr>';
8617
					print '<td>' . $langs->trans("Ref") . '</td>';
8618
					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>';
8619
					print '</tr>';
8620
					print '</table>';
8621
					print '</form>';
8622
				}
8623
8624
				$sql = $possiblelink['sql'];
8625
8626
				$resqllist = $this->db->query($sql);
8627
				if ($resqllist) {
8628
					$num = $this->db->num_rows($resqllist);
8629
					$i = 0;
8630
8631
					print '<br>';
8632
					print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
8633
					print '<input type="hidden" name="action" value="addlink">';
8634
					print '<input type="hidden" name="token" value="'.newToken().'">';
8635
					print '<input type="hidden" name="id" value="'.$object->id.'">';
8636
					print '<input type="hidden" name="addlink" value="'.$key.'">';
8637
					print '<table class="noborder">';
8638
					print '<tr class="liste_titre">';
8639
					print '<td class="nowrap"></td>';
8640
					print '<td class="center">'.$langs->trans("Ref").'</td>';
8641
					print '<td class="left">'.$langs->trans("RefCustomer").'</td>';
8642
					print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
8643
					print '<td class="left">'.$langs->trans("Company").'</td>';
8644
					print '</tr>';
8645
					while ($i < $num) {
8646
						$objp = $this->db->fetch_object($resqllist);
8647
8648
						print '<tr class="oddeven">';
8649
						print '<td class="left">';
8650
						print '<input type="radio" name="idtolinkto" id="'.$key.'_'.$objp->rowid.'" value="'.$objp->rowid.'">';
8651
						print '</td>';
8652
						print '<td class="center"><label for="'.$key.'_'.$objp->rowid.'">'.$objp->ref.'</label></td>';
8653
						print '<td>'.(!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')).'</td>';
8654
						print '<td class="right">';
8655
						if ($possiblelink['label'] == 'LinkToContract') {
8656
							$form = new Form($this->db);
8657
							print $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")).' ';
8658
						}
8659
						print '<span class="amount">'.price($objp->total_ht).'</span>';
8660
						print '</td>';
8661
						print '<td>'.$objp->name.'</td>';
8662
						print '</tr>';
8663
						$i++;
8664
					}
8665
					print '</table>';
8666
					print '<div class="center">';
8667
					print '<input type="submit" class="button valignmiddle marginleftonly marginrightonly" value="'.$langs->trans('ToLink').'">';
8668
					if (empty($conf->use_javascript_ajax)) {
8669
						print '<input type="submit" class="button button-cancel marginleftonly marginrightonly" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
8670
					} else {
8671
						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>';
8672
					}
8673
					print '</form>';
8674
					$this->db->free($resqllist);
8675
				} else {
8676
					dol_print_error($this->db);
8677
				}
8678
				print '</div>';
8679
8680
				//$linktoelem.=($linktoelem?' &nbsp; ':'');
8681
				if ($num > 0 || !empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
8682
					$linktoelemlist .= '<li><a href="#linkto'.$key.'" class="linkto dropdowncloseonclick" rel="'.$key.'">'.$langs->trans($possiblelink['label']).' ('.$num.')</a></li>';
8683
					// } else $linktoelem.=$langs->trans($possiblelink['label']);
8684
				} else {
8685
					$linktoelemlist .= '<li><span class="linktodisabled">'.$langs->trans($possiblelink['label']).' (0)</span></li>';
8686
				}
8687
			}
8688
		}
8689
8690
		if ($linktoelemlist) {
8691
			$linktoelem = '
8692
    		<dl class="dropdown" id="linktoobjectname">
8693
    		';
8694
			if (!empty($conf->use_javascript_ajax)) {
8695
				$linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>'.$langs->trans("LinkTo").'...</a></dt>';
8696
			}
8697
			$linktoelem .= '<dd>
8698
    		<div class="multiselectlinkto">
8699
    		<ul class="ulselectedfields">'.$linktoelemlist.'
8700
    		</ul>
8701
    		</div>
8702
    		</dd>
8703
    		</dl>';
8704
		} else {
8705
			$linktoelem = '';
8706
		}
8707
8708
		if (!empty($conf->use_javascript_ajax)) {
8709
			print '<!-- Add js to show linkto box -->
8710
				<script>
8711
				jQuery(document).ready(function() {
8712
					jQuery(".linkto").click(function() {
8713
						console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
8714
					    jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
8715
					});
8716
				});
8717
				</script>
8718
		    ';
8719
		}
8720
8721
		return $linktoelem;
8722
	}
8723
8724
	/**
8725
	 *	Return an html string with a select combo box to choose yes or no
8726
	 *
8727
	 *	@param	string		$htmlname		Name of html select field
8728
	 *	@param	string		$value			Pre-selected value
8729
	 *	@param	int			$option			0 return yes/no, 1 return 1/0
8730
	 *	@param	bool		$disabled		true or false
8731
	 *  @param	int      	$useempty		1=Add empty line
8732
	 *  @param	int			$addjscombo		1=Add js beautifier on combo box
8733
	 *  @param	string		$morecss		More CSS
8734
	 *	@return	string						See option
8735
	 */
8736
	public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = '')
8737
	{
8738
		global $langs;
8739
8740
		$yes = "yes";
8741
		$no = "no";
8742
		if ($option) {
8743
			$yes = "1";
8744
			$no = "0";
8745
		}
8746
8747
		$disabled = ($disabled ? ' disabled' : '');
8748
8749
		$resultyesno = '<select class="flat width75'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'"'.$disabled.'>'."\n";
8750
		if ($useempty) {
8751
			$resultyesno .= '<option value="-1"'.(($value < 0) ? ' selected' : '').'>&nbsp;</option>'."\n";
8752
		}
8753
		if (("$value" == 'yes') || ($value == 1)) {
8754
			$resultyesno .= '<option value="'.$yes.'" selected>'.$langs->trans("Yes").'</option>'."\n";
8755
			$resultyesno .= '<option value="'.$no.'">'.$langs->trans("No").'</option>'."\n";
8756
		} else {
8757
			$selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
8758
			$resultyesno .= '<option value="'.$yes.'">'.$langs->trans("Yes").'</option>'."\n";
8759
			$resultyesno .= '<option value="'.$no.'"'.$selected.'>'.$langs->trans("No").'</option>'."\n";
8760
		}
8761
		$resultyesno .= '</select>'."\n";
8762
8763
		if ($addjscombo) {
8764
			$resultyesno .= ajax_combobox($htmlname);
8765
		}
8766
8767
		return $resultyesno;
8768
	}
8769
8770
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8771
	/**
8772
	 *  Return list of export templates
8773
	 *
8774
	 *  @param	string	$selected          Id modele pre-selectionne
8775
	 *  @param  string	$htmlname          Name of HTML select
8776
	 *  @param  string	$type              Type of searched templates
8777
	 *  @param  int		$useempty          Affiche valeur vide dans liste
8778
	 *  @return	void
8779
	 */
8780
	public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
8781
	{
8782
		// phpcs:enable
8783
		$sql = "SELECT rowid, label";
8784
		$sql .= " FROM ".$this->db->prefix()."export_model";
8785
		$sql .= " WHERE type = '".$this->db->escape($type)."'";
8786
		$sql .= " ORDER BY rowid";
8787
		$result = $this->db->query($sql);
8788
		if ($result) {
8789
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
8790
			if ($useempty) {
8791
				print '<option value="-1">&nbsp;</option>';
8792
			}
8793
8794
			$num = $this->db->num_rows($result);
8795
			$i = 0;
8796
			while ($i < $num) {
8797
				$obj = $this->db->fetch_object($result);
8798
				if ($selected == $obj->rowid) {
8799
					print '<option value="'.$obj->rowid.'" selected>';
8800
				} else {
8801
					print '<option value="'.$obj->rowid.'">';
8802
				}
8803
				print $obj->label;
8804
				print '</option>';
8805
				$i++;
8806
			}
8807
			print "</select>";
8808
		} else {
8809
			dol_print_error($this->db);
8810
		}
8811
	}
8812
8813
	/**
8814
	 *    Return a HTML area with the reference of object and a navigation bar for a business object
8815
	 *    Note: To complete search with a particular filter on select, you can set $object->next_prev_filter set to define SQL criterias.
8816
	 *
8817
	 *    @param	object	$object			Object to show.
8818
	 *    @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link.
8819
	 *    @param	string	$morehtml  		More html content to output just before the nav bar.
8820
	 *    @param	int		$shownav	  	Show Condition (navigation is shown if value is 1).
8821
	 *    @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.
8822
	 *    @param	string	$fieldref   	Name of field ref of object (object->ref) to show or 'none' to not show ref.
8823
	 *    @param	string	$morehtmlref  	More html to show after ref.
8824
	 *    @param	string	$moreparam  	More param to add in nav link url. Must start with '&...'.
8825
	 *	  @param	int		$nodbprefix		Do not include DB prefix to forge table name.
8826
	 *	  @param	string	$morehtmlleft	More html code to show before ref.
8827
	 *	  @param	string	$morehtmlstatus	More html code to show under navigation arrows (status place).
8828
	 *	  @param	string	$morehtmlright	More html code to show after ref.
8829
	 * 	  @return	string    				Portion HTML with ref + navigation buttons
8830
	 */
8831
	public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
8832
	{
8833
		global $conf, $langs, $hookmanager, $extralanguages;
8834
8835
		$ret = '';
8836
		if (empty($fieldid)) {
8837
			$fieldid = 'rowid';
8838
		}
8839
		if (empty($fieldref)) {
8840
			$fieldref = 'ref';
8841
		}
8842
8843
		// Preparing gender's display if there is one
8844
		$addgendertxt = '';
8845
		if (property_exists($object, 'gender') && !empty($object->gender)) {
8846
			$addgendertxt = ' ';
8847
			switch ($object->gender) {
8848
				case 'man':
8849
					$addgendertxt .= '<i class="fas fa-mars"></i>';
8850
					break;
8851
				case 'woman':
8852
					$addgendertxt .= '<i class="fas fa-venus"></i>';
8853
					break;
8854
				case 'other':
8855
					$addgendertxt .= '<i class="fas fa-genderless"></i>';
8856
					break;
8857
			}
8858
		}
8859
		/*
8860
		$addadmin = '';
8861
		if (property_exists($object, 'admin')) {
8862
			if (!empty($conf->multicompany->enabled) && !empty($object->admin) && empty($object->entity)) {
8863
				$addadmin .= img_picto($langs->trans("SuperAdministratorDesc"), "redstar", 'class="paddingleft"');
8864
			} elseif (!empty($object->admin)) {
8865
				$addadmin .= img_picto($langs->trans("AdministratorDesc"), "star", 'class="paddingleft"');
8866
			}
8867
		}*/
8868
8869
		// Add where from hooks
8870
		if (is_object($hookmanager)) {
8871
			$parameters = array('showrefnav' => true);
8872
			$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
8873
			$object->next_prev_filter .= $hookmanager->resPrint;
8874
		}
8875
		$previous_ref = $next_ref = '';
8876
		if ($shownav) {
8877
			//print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
8878
			$object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
8879
8880
			$navurl = $_SERVER["PHP_SELF"];
8881
			// Special case for project/task page
8882
			if ($paramid == 'project_ref') {
8883
				if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) {     // TODO Remove this when nav with project_ref on task pages are ok
8884
					$navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
8885
					$paramid = 'ref';
8886
				}
8887
			}
8888
8889
			// accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
8890
			// accesskey is for Mac:               CTRL + key for all browsers
8891
			$stringforfirstkey = $langs->trans("KeyboardShortcut");
8892
			if ($conf->browser->name == 'chrome') {
8893
				$stringforfirstkey .= ' ALT +';
8894
			} elseif ($conf->browser->name == 'firefox') {
8895
				$stringforfirstkey .= ' ALT + SHIFT +';
8896
			} else {
8897
				$stringforfirstkey .= ' CTL +';
8898
			}
8899
8900
			$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>';
8901
			$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>';
8902
		}
8903
8904
		//print "xx".$previous_ref."x".$next_ref;
8905
		$ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
8906
8907
		// Right part of banner
8908
		if ($morehtmlright) {
8909
			$ret .= '<div class="inline-block floatleft">'.$morehtmlright.'</div>';
8910
		}
8911
8912
		if ($previous_ref || $next_ref || $morehtml) {
8913
			$ret .= '<div class="pagination paginationref"><ul class="right">';
8914
		}
8915
		if ($morehtml) {
8916
			$ret .= '<li class="noborder litext'.(($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '').'">'.$morehtml.'</li>';
8917
		}
8918
		if ($shownav && ($previous_ref || $next_ref)) {
8919
			$ret .= '<li class="pagination">'.$previous_ref.'</li>';
8920
			$ret .= '<li class="pagination">'.$next_ref.'</li>';
8921
		}
8922
		if ($previous_ref || $next_ref || $morehtml) {
8923
			$ret .= '</ul></div>';
8924
		}
8925
8926
		$parameters = array();
8927
		$reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
8928
		if (empty($reshook)) {
8929
			$morehtmlstatus .= $hookmanager->resPrint;
8930
		} else {
8931
			$morehtmlstatus = $hookmanager->resPrint;
8932
		}
8933
		if ($morehtmlstatus) {
8934
			$ret .= '<div class="statusref">'.$morehtmlstatus.'</div>';
8935
		}
8936
8937
		$parameters = array();
8938
		$reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
8939
		if (empty($reshook)) {
8940
			$morehtmlref .= $hookmanager->resPrint;
8941
		} elseif ($reshook > 0) {
8942
			$morehtmlref = $hookmanager->resPrint;
8943
		}
8944
8945
		// Left part of banner
8946
		if ($morehtmlleft) {
8947
			if ($conf->browser->layout == 'phone') {
8948
				$ret .= '<!-- morehtmlleft --><div class="floatleft">'.$morehtmlleft.'</div>'; // class="center" to have photo in middle
8949
			} else {
8950
				$ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">'.$morehtmlleft.'</div>';
8951
			}
8952
		}
8953
8954
		//if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
8955
		$ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid'.(($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '').'">';
8956
8957
		// For thirdparty, contact, user, member, the ref is the id, so we show something else
8958
		if ($object->element == 'societe') {
8959
			$ret .= dol_htmlentities($object->name);
8960
8961
			// List of extra languages
8962
			$arrayoflangcode = array();
8963
			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
8964
				$arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
8965
			}
8966
8967
			if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
8968
				if (!is_object($extralanguages)) {
8969
					include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
8970
					$extralanguages = new ExtraLanguages($this->db);
8971
				}
8972
				$extralanguages->fetch_name_extralanguages('societe');
8973
8974
				if (!empty($extralanguages->attributes['societe']['name'])) {
8975
					$object->fetchValuesForExtraLanguages();
8976
8977
					$htmltext = '';
8978
					// If there is extra languages
8979
					foreach ($arrayoflangcode as $extralangcode) {
8980
						$htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
8981
						if ($object->array_languages['name'][$extralangcode]) {
8982
							$htmltext .= $object->array_languages['name'][$extralangcode];
8983
						} else {
8984
							$htmltext .= '<span class="opacitymedium">'.$langs->trans("SwitchInEditModeToAddTranslation").'</span>';
8985
						}
8986
					}
8987
					$ret .= '<!-- Show translations of name -->'."\n";
8988
					$ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
8989
				}
8990
			}
8991
		} elseif ($object->element == 'member') {
8992
			$ret .= $object->ref.'<br>';
8993
			$fullname = $object->getFullName($langs);
8994
			if ($object->morphy == 'mor' && $object->societe) {
8995
				$ret .= dol_htmlentities($object->societe).((!empty($fullname) && $object->societe != $fullname) ? ' ('.dol_htmlentities($fullname).$addgendertxt.')' : '');
8996
			} else {
8997
				$ret .= dol_htmlentities($fullname).$addgendertxt.((!empty($object->societe) && $object->societe != $fullname) ? ' ('.dol_htmlentities($object->societe).')' : '');
8998
			}
8999
		} elseif (in_array($object->element, array('contact', 'user', 'usergroup'))) {
9000
			$ret .= dol_htmlentities($object->getFullName($langs));
9001
		} elseif (in_array($object->element, array('action', 'agenda'))) {
9002
			$ret .= $object->ref.'<br>'.$object->label;
9003
		} elseif (in_array($object->element, array('adherent_type'))) {
9004
			$ret .= $object->label;
9005
		} elseif ($object->element == 'ecm_directories') {
9006
			$ret .= '';
9007
		} elseif ($fieldref != 'none') {
9008
			$ret .= dol_htmlentities($object->$fieldref);
9009
		}
9010
9011
		if ($morehtmlref) {
9012
			// don't add a additional space, when "$morehtmlref" starts with a HTML div tag
9013
			if (substr($morehtmlref, 0, 4) != '<div') {
9014
				$ret .= ' ';
9015
			}
9016
9017
			$ret .= $morehtmlref;
9018
		}
9019
9020
		$ret .= '</div>';
9021
9022
		$ret .= '</div><!-- End banner content -->';
9023
9024
		return $ret;
9025
	}
9026
9027
9028
	/**
9029
	 *  Return HTML code to output a barcode
9030
	 *
9031
	 *  @param	Object	$object			Object containing data to retrieve file name
9032
	 * 	@param	int		$width			Width of photo
9033
	 * 	@param	string	$morecss		More CSS on img of barcode
9034
	 * 	@return string    				HTML code to output barcode
9035
	 */
9036
	public function showbarcode(&$object, $width = 100, $morecss = '')
9037
	{
9038
		global $conf;
9039
9040
		//Check if barcode is filled in the card
9041
		if (empty($object->barcode)) {
9042
			return '';
9043
		}
9044
9045
		// Complete object if not complete
9046
		if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
9047
			$result = $object->fetch_barcode();
9048
			//Check if fetch_barcode() failed
9049
			if ($result < 1) {
9050
				return '<!-- ErrorFetchBarcode -->';
9051
			}
9052
		}
9053
9054
		// Barcode image
9055
		$url = DOL_URL_ROOT.'/viewimage.php?modulepart=barcode&generator='.urlencode($object->barcode_type_coder).'&code='.urlencode($object->barcode).'&encoding='.urlencode($object->barcode_type_code);
9056
		$out = '<!-- url barcode = '.$url.' -->';
9057
		$out .= '<img src="'.$url.'"'.($morecss ? ' class="'.$morecss.'"' : '').'>';
9058
		return $out;
9059
	}
9060
9061
	/**
9062
	 *    	Return HTML code to output a photo
9063
	 *
9064
	 *    	@param	string		$modulepart			Key to define module concerned ('societe', 'userphoto', 'memberphoto')
9065
	 *     	@param  object		$object				Object containing data to retrieve file name
9066
	 * 		@param	int			$width				Width of photo
9067
	 * 		@param	int			$height				Height of photo (auto if 0)
9068
	 * 		@param	int			$caneditfield		Add edit fields
9069
	 * 		@param	string		$cssclass			CSS name to use on img for photo
9070
	 * 		@param	string		$imagesize		    'mini', 'small' or '' (original)
9071
	 *      @param  int         $addlinktofullsize  Add link to fullsize image
9072
	 *      @param  int         $cache              1=Accept to use image in cache
9073
	 *      @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 ''.
9074
	 *      @param	int			$noexternsourceoverwrite	No overwrite image with extern source (like 'gravatar' or other module)
9075
	 * 	  	@return string    						HTML code to output photo
9076
	 */
9077
	public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
9078
	{
9079
		global $conf, $langs;
9080
9081
		$entity = (!empty($object->entity) ? $object->entity : $conf->entity);
9082
		$id = (!empty($object->id) ? $object->id : $object->rowid);
9083
9084
		$ret = '';
9085
		$dir = '';
9086
		$file = '';
9087
		$originalfile = '';
9088
		$altfile = '';
9089
		$email = '';
9090
		$capture = '';
9091
		if ($modulepart == 'societe') {
9092
			$dir = $conf->societe->multidir_output[$entity];
9093
			if (!empty($object->logo)) {
9094
				if (dolIsAllowedForPreview($object->logo)) {
9095
					if ((string) $imagesize == 'mini') {
9096
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
9097
					} elseif ((string) $imagesize == 'small') {
9098
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_small');
9099
					} else {
9100
						$file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
9101
					}
9102
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
9103
				}
9104
			}
9105
			$email = $object->email;
9106
		} elseif ($modulepart == 'contact')	{
9107
			$dir = $conf->societe->multidir_output[$entity].'/contact';
9108
			if (!empty($object->photo)) {
9109
				if (dolIsAllowedForPreview($object->photo)) {
9110
					if ((string) $imagesize == 'mini') {
9111
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_mini');
9112
					} elseif ((string) $imagesize == 'small') {
9113
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_small');
9114
					} else {
9115
						$file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
9116
					}
9117
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
9118
				}
9119
			}
9120
			$email = $object->email;
9121
			$capture = 'user';
9122
		} elseif ($modulepart == 'userphoto') {
9123
			$dir = $conf->user->dir_output;
9124
			if (!empty($object->photo)) {
9125
				if (dolIsAllowedForPreview($object->photo)) {
9126
					if ((string) $imagesize == 'mini') {
9127
						$file = get_exdir(0, 0, 0, 0, $object, 'user').'photos/'.getImageFileNameForSize($object->photo, '_mini');
9128
					} elseif ((string) $imagesize == 'small') {
9129
						$file = get_exdir(0, 0, 0, 0, $object, 'user').'photos/'.getImageFileNameForSize($object->photo, '_small');
9130
					} else {
9131
						$file = get_exdir(0, 0, 0, 0, $object, 'user').'photos/'.$object->photo;
9132
					}
9133
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'user').'photos/'.$object->photo;
9134
				}
9135
			}
9136
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
9137
				$altfile = $object->id.".jpg"; // For backward compatibility
9138
			}
9139
			$email = $object->email;
9140
			$capture = 'user';
9141
		} elseif ($modulepart == 'memberphoto')	{
9142
			$dir = $conf->adherent->dir_output;
9143
			if (!empty($object->photo)) {
9144
				if (dolIsAllowedForPreview($object->photo)) {
9145
					if ((string) $imagesize == 'mini') {
9146
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_mini');
9147
					} elseif ((string) $imagesize == 'small') {
9148
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_small');
9149
					} else {
9150
						$file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
9151
					}
9152
					$originalfile = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
9153
				}
9154
			}
9155
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
9156
				$altfile = $object->id.".jpg"; // For backward compatibility
9157
			}
9158
			$email = $object->email;
9159
			$capture = 'user';
9160
		} else {
9161
			// Generic case to show photos
9162
			$dir = $conf->$modulepart->dir_output;
9163
			if (!empty($object->photo)) {
9164
				if (dolIsAllowedForPreview($object->photo)) {
9165
					if ((string) $imagesize == 'mini') {
9166
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_mini');
9167
					} elseif ((string) $imagesize == 'small') {
9168
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_small');
9169
					} else {
9170
						$file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
9171
					}
9172
					$originalfile = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
9173
				}
9174
			}
9175
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
9176
				$altfile = $object->id.".jpg"; // For backward compatibility
9177
			}
9178
			$email = $object->email;
9179
		}
9180
9181
		if ($forcecapture) {
9182
			$capture = $forcecapture;
9183
		}
9184
9185
		if ($dir) {
9186
			if ($file && file_exists($dir."/".$file)) {
9187
				if ($addlinktofullsize) {
9188
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
9189
					if ($urladvanced) {
9190
						$ret .= '<a href="'.$urladvanced.'">';
9191
					} else {
9192
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
9193
					}
9194
				}
9195
				$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.'">';
9196
				if ($addlinktofullsize) {
9197
					$ret .= '</a>';
9198
				}
9199
			} elseif ($altfile && file_exists($dir."/".$altfile)) {
9200
				if ($addlinktofullsize) {
9201
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
9202
					if ($urladvanced) {
9203
						$ret .= '<a href="'.$urladvanced.'">';
9204
					} else {
9205
						$ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
9206
					}
9207
				}
9208
				$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.'">';
9209
				if ($addlinktofullsize) {
9210
					$ret .= '</a>';
9211
				}
9212
			} else {
9213
				$nophoto = '/public/theme/common/nophoto.png';
9214
				$defaultimg = 'identicon';		// For gravatar
9215
				if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) {	// For modules that need a special image when photo not found
9216
					if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && strpos($object->morphy, 'mor')) !== false) {
9217
						$nophoto = 'company';
9218
					} else {
9219
						$nophoto = '/public/theme/common/user_anonymous.png';
9220
						if (!empty($object->gender) && $object->gender == 'man') {
9221
							$nophoto = '/public/theme/common/user_man.png';
9222
						}
9223
						if (!empty($object->gender) && $object->gender == 'woman') {
9224
							$nophoto = '/public/theme/common/user_woman.png';
9225
						}
9226
					}
9227
				}
9228
9229
				if (!empty($conf->gravatar->enabled) && $email && empty($noexternsourceoverwrite)) {
9230
					// see https://gravatar.com/site/implement/images/php/
9231
					$ret .= '<!-- Put link to gravatar -->';
9232
					$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="" 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
9233
				} else {
9234
					if ($nophoto == 'company') {
9235
						$ret .= '<div class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').'">'.img_picto('', 'company').'</div>';
9236
					} else {
9237
						$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.$nophoto.'">';
9238
					}
9239
				}
9240
			}
9241
9242
			if ($caneditfield) {
9243
				if ($object->photo) {
9244
					$ret .= "<br>\n";
9245
				}
9246
				$ret .= '<table class="nobordernopadding centpercent">';
9247
				if ($object->photo) {
9248
					$ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">'.$langs->trans("Delete").'</label><br><br></td></tr>';
9249
				}
9250
				$ret .= '<tr><td class="tdoverflow"><input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"'.($capture ? ' capture="'.$capture.'"' : '').'></td></tr>';
9251
				$ret .= '</table>';
9252
			}
9253
		} else {
9254
			dol_print_error('', 'Call of showphoto with wrong parameters modulepart='.$modulepart);
9255
		}
9256
9257
		return $ret;
9258
	}
9259
9260
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9261
	/**
9262
	 *	Return select list of groups
9263
	 *
9264
	 *  @param	string	$selected       Id group preselected
9265
	 *  @param  string	$htmlname       Field name in form
9266
	 *  @param  int		$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
9267
	 *  @param  string	$exclude        Array list of groups id to exclude
9268
	 * 	@param	int		$disabled		If select list must be disabled
9269
	 *  @param  string	$include        Array list of groups id to include
9270
	 * 	@param	int		$enableonly		Array list of groups id to be enabled. All other must be disabled
9271
	 * 	@param	string	$force_entity	'0' or Ids of environment to force
9272
	 * 	@param	bool	$multiple		add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
9273
	 *  @param  string	$morecss		More css to add to html component
9274
	 *  @return	string
9275
	 *  @see select_dolusers()
9276
	 */
9277
	public function select_dolgroups($selected = '', $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $multiple = false, $morecss = '')
9278
	{
9279
		// phpcs:enable
9280
		global $conf, $user, $langs;
9281
9282
		// Permettre l'exclusion de groupes
9283
		if (is_array($exclude)) {
0 ignored issues
show
introduced by
The condition is_array($exclude) is always false.
Loading history...
9284
			$excludeGroups = implode(",", $exclude);
9285
		}
9286
		// Permettre l'inclusion de groupes
9287
		if (is_array($include)) {
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
9288
			$includeGroups = implode(",", $include);
9289
		}
9290
9291
		if (!is_array($selected)) {
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
9292
			$selected = array($selected);
9293
		}
9294
9295
		$out = '';
9296
9297
		// On recherche les groupes
9298
		$sql = "SELECT ug.rowid, ug.nom as name";
9299
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
9300
			$sql .= ", e.label";
9301
		}
9302
		$sql .= " FROM ".$this->db->prefix()."usergroup as ug ";
9303
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
9304
			$sql .= " LEFT JOIN ".$this->db->prefix()."entity as e ON e.rowid=ug.entity";
9305
			if ($force_entity) {
9306
				$sql .= " WHERE ug.entity IN (0, ".$force_entity.")";
9307
			} else {
9308
				$sql .= " WHERE ug.entity IS NOT NULL";
9309
			}
9310
		} else {
9311
			$sql .= " WHERE ug.entity IN (0, ".$conf->entity.")";
9312
		}
9313
		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...
9314
			$sql .= " AND ug.rowid NOT IN (".$this->db->sanitize($excludeGroups).")";
9315
		}
9316
		if (is_array($include) && $includeGroups) {
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
Comprehensibility Best Practice introduced by
The variable $includeGroups does not seem to be defined for all execution paths leading up to this point.
Loading history...
9317
			$sql .= " AND ug.rowid IN (".$this->db->sanitize($includeGroups).")";
9318
		}
9319
		$sql .= " ORDER BY ug.nom ASC";
9320
9321
		dol_syslog(get_class($this)."::select_dolgroups", LOG_DEBUG);
9322
		$resql = $this->db->query($sql);
9323
		if ($resql) {
9324
			// Enhance with select2
9325
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
9326
			$out .= ajax_combobox($htmlname);
9327
9328
			$out .= '<select class="flat minwidth200'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
9329
9330
			$num = $this->db->num_rows($resql);
9331
			$i = 0;
9332
			if ($num) {
9333
				if ($show_empty && !$multiple) {
9334
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>&nbsp;</option>'."\n";
9335
				}
9336
9337
				while ($i < $num) {
9338
					$obj = $this->db->fetch_object($resql);
9339
					$disableline = 0;
9340
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
9341
						$disableline = 1;
9342
					}
9343
9344
					$out .= '<option value="'.$obj->rowid.'"';
9345
					if ($disableline) {
9346
						$out .= ' disabled';
9347
					}
9348
					if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid) || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
9349
						$out .= ' selected';
9350
					}
9351
					$out .= '>';
9352
9353
					$out .= $obj->name;
9354
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1) {
9355
						$out .= " (".$obj->label.")";
9356
					}
9357
9358
					$out .= '</option>';
9359
					$i++;
9360
				}
9361
			} else {
9362
				if ($show_empty) {
9363
					$out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'></option>'."\n";
9364
				}
9365
				$out .= '<option value="" disabled>'.$langs->trans("NoUserGroupDefined").'</option>';
9366
			}
9367
			$out .= '</select>';
9368
		} else {
9369
			dol_print_error($this->db);
9370
		}
9371
9372
		return $out;
9373
	}
9374
9375
9376
	/**
9377
	 *	Return HTML to show the search and clear seach button
9378
	 *
9379
	 *  @return	string
9380
	 */
9381
	public function showFilterButtons()
9382
	{
9383
		$out = '<div class="nowraponall">';
9384
		$out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9385
		$out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9386
		$out .= '</div>';
9387
9388
		return $out;
9389
	}
9390
9391
	/**
9392
	 *	Return HTML to show the search and clear search button
9393
	 *
9394
	 *  @param  string  $cssclass                  CSS class
9395
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
9396
	 *  @param  string  $massactionname            Mass action button name that will launch an action on the selected items
9397
	 *  @return	string
9398
	 */
9399
	public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
9400
	{
9401
		global $conf, $langs;
9402
9403
		$out = '';
9404
9405
		if (!empty($conf->use_javascript_ajax)) {
9406
			$out .= '<div class="inline-block checkallactions"><input type="checkbox" id="'.$cssclass.'s" name="'.$cssclass.'s" class="checkallactions"></div>';
9407
		}
9408
		$out .= '<script>
9409
            $(document).ready(function() {
9410
                $("#' . $cssclass.'s").click(function() {
9411
                    if($(this).is(\':checked\')){
9412
                        console.log("We check all '.$cssclass.' and trigger the change method");
9413
                		$(".'.$cssclass.'").prop(\'checked\', true).trigger(\'change\');
9414
                    }
9415
                    else
9416
                    {
9417
                        console.log("We uncheck all");
9418
                		$(".'.$cssclass.'").prop(\'checked\', false).trigger(\'change\');
9419
                    }'."\n";
9420
		if ($calljsfunction) {
9421
			$out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "'.$massactionname.'", "'.$cssclass.'"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
9422
		}
9423
		$out .= '         });
9424
        	        $(".' . $cssclass.'").change(function() {
9425
					$(this).closest("tr").toggleClass("highlight", this.checked);
9426
				});
9427
		 	});
9428
    	</script>';
9429
9430
		return $out;
9431
	}
9432
9433
	/**
9434
	 *	Return HTML to show the search and clear seach button
9435
	 *
9436
	 *  @param	int  	$addcheckuncheckall        Add the check all/uncheck all checkbox (use javascript) and code to manage this
9437
	 *  @param  string  $cssclass                  CSS class
9438
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
9439
	 *  @param  string  $massactionname            Mass action name
9440
	 *  @return	string
9441
	 */
9442
	public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
9443
	{
9444
		$out = $this->showFilterButtons();
9445
		if ($addcheckuncheckall) {
9446
			$out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
9447
		}
9448
		return $out;
9449
	}
9450
9451
	/**
9452
	 * Return HTML to show the select of expense categories
9453
	 *
9454
	 * @param	string	$selected              preselected category
9455
	 * @param	string	$htmlname              name of HTML select list
9456
	 * @param	integer	$useempty              1=Add empty line
9457
	 * @param	array	$excludeid             id to exclude
9458
	 * @param	string	$target                htmlname of target select to bind event
9459
	 * @param	int		$default_selected      default category to select if fk_c_type_fees change = EX_KME
9460
	 * @param	array	$params                param to give
9461
	 * @param	int		$info_admin			   Show the tooltip help picto to setup list
9462
	 * @return	string
9463
	 */
9464
	public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
9465
	{
9466
		global $langs, $user;
9467
9468
		$out = '';
9469
		$sql = "SELECT rowid, label FROM ".$this->db->prefix()."c_exp_tax_cat WHERE active = 1";
9470
		$sql .= " AND entity IN (0,".getEntity('exp_tax_cat').")";
9471
		if (!empty($excludeid)) {
9472
			$sql .= " AND rowid NOT IN (".$this->db->sanitize(implode(',', $excludeid)).")";
9473
		}
9474
		$sql .= " ORDER BY label";
9475
9476
		$resql = $this->db->query($sql);
9477
		if ($resql) {
9478
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp maxwidth200">';
9479
			if ($useempty) {
9480
				$out .= '<option value="0">&nbsp;</option>';
9481
			}
9482
9483
			while ($obj = $this->db->fetch_object($resql)) {
9484
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.$langs->trans($obj->label).'</option>';
9485
			}
9486
			$out .= '</select>';
9487
			$out .= ajax_combobox('select_'.$htmlname);
9488
9489
			if (!empty($htmlname) && $user->admin && $info_admin) {
9490
				$out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
9491
			}
9492
9493
			if (!empty($target)) {
9494
				$sql = "SELECT c.id FROM ".$this->db->prefix()."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
9495
				$resql = $this->db->query($sql);
9496
				if ($resql) {
9497
					if ($this->db->num_rows($resql) > 0) {
9498
						$obj = $this->db->fetch_object($resql);
9499
						$out .= '<script>
9500
							$(function() {
9501
								$("select[name='.$target.']").on("change", function() {
9502
									var current_val = $(this).val();
9503
									if (current_val == '.$obj->id.') {';
9504
						if (!empty($default_selected) || !empty($selected)) {
9505
							$out .= '$("select[name='.$htmlname.']").val("'.($default_selected > 0 ? $default_selected : $selected).'");';
9506
						}
9507
9508
						$out .= '
9509
										$("select[name='.$htmlname.']").change();
9510
									}
9511
								});
9512
9513
								$("select[name='.$htmlname.']").change(function() {
9514
9515
									if ($("select[name='.$target.']").val() == '.$obj->id.') {
9516
										// get price of kilometer to fill the unit price
9517
										$.ajax({
9518
											method: "POST",
9519
											dataType: "json",
9520
											data: { fk_c_exp_tax_cat: $(this).val(), token: \''.currentToken().'\' },
9521
											url: "'.(DOL_URL_ROOT.'/expensereport/ajax/ajaxik.php?'.$params).'",
9522
										}).done(function( data, textStatus, jqXHR ) {
9523
											console.log(data);
9524
											if (typeof data.up != "undefined") {
9525
												$("input[name=value_unit]").val(data.up);
9526
												$("select[name='.$htmlname.']").attr("title", data.title);
9527
											} else {
9528
												$("input[name=value_unit]").val("");
9529
												$("select[name='.$htmlname.']").attr("title", "");
9530
											}
9531
										});
9532
									}
9533
								});
9534
							});
9535
						</script>';
9536
					}
9537
				}
9538
			}
9539
		} else {
9540
			dol_print_error($this->db);
9541
		}
9542
9543
		return $out;
9544
	}
9545
9546
	/**
9547
	 * Return HTML to show the select ranges of expense range
9548
	 *
9549
	 * @param	string	$selected    preselected category
9550
	 * @param	string	$htmlname    name of HTML select list
9551
	 * @param	integer	$useempty    1=Add empty line
9552
	 * @return	string
9553
	 */
9554
	public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
9555
	{
9556
		global $conf, $langs;
9557
9558
		$out = '';
9559
		$sql = "SELECT rowid, range_ik FROM ".$this->db->prefix()."c_exp_tax_range";
9560
		$sql .= " WHERE entity = ".$conf->entity." AND active = 1";
9561
9562
		$resql = $this->db->query($sql);
9563
		if ($resql) {
9564
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
9565
			if ($useempty) {
9566
				$out .= '<option value="0"></option>';
9567
			}
9568
9569
			while ($obj = $this->db->fetch_object($resql)) {
9570
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.price($obj->range_ik, 0, $langs, 1, 0).'</option>';
9571
			}
9572
			$out .= '</select>';
9573
		} else {
9574
			dol_print_error($this->db);
9575
		}
9576
9577
		return $out;
9578
	}
9579
9580
	/**
9581
	 * Return HTML to show a select of expense
9582
	 *
9583
	 * @param	string	$selected    preselected category
9584
	 * @param	string	$htmlname    name of HTML select list
9585
	 * @param	integer	$useempty    1=Add empty choice
9586
	 * @param	integer	$allchoice   1=Add all choice
9587
	 * @param	integer	$useid       0=use 'code' as key, 1=use 'id' as key
9588
	 * @return	string
9589
	 */
9590
	public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
9591
	{
9592
		global $langs;
9593
9594
		$out = '';
9595
		$sql = "SELECT id, code, label FROM ".$this->db->prefix()."c_type_fees";
9596
		$sql .= " WHERE active = 1";
9597
9598
		$resql = $this->db->query($sql);
9599
		if ($resql) {
9600
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
9601
			if ($useempty) {
9602
				$out .= '<option value="0"></option>';
9603
			}
9604
			if ($allchoice) {
9605
				$out .= '<option value="-1">'.$langs->trans('AllExpenseReport').'</option>';
9606
			}
9607
9608
			$field = 'code';
9609
			if ($useid) {
9610
				$field = 'id';
9611
			}
9612
9613
			while ($obj = $this->db->fetch_object($resql)) {
9614
				$key = $langs->trans($obj->code);
9615
				$out .= '<option '.($selected == $obj->{$field} ? 'selected="selected"' : '').' value="'.$obj->{$field}.'">'.($key != $obj->code ? $key : $obj->label).'</option>';
9616
			}
9617
			$out .= '</select>';
9618
		} else {
9619
			dol_print_error($this->db);
9620
		}
9621
9622
		return $out;
9623
	}
9624
9625
	/**
9626
	 *  Output a combo list with invoices qualified for a third party
9627
	 *
9628
	 *  @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)
9629
	 *  @param  int		$selected   	Id invoice preselected
9630
	 *  @param  string	$htmlname   	Name of HTML select
9631
	 *	@param	int		$maxlength		Maximum length of label
9632
	 *	@param	int		$option_only	Return only html options lines without the select tag
9633
	 *	@param	string	$show_empty		Add an empty line ('1' or string to show for empty line)
9634
	 *  @param	int		$discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
9635
	 *  @param	int		$forcefocus		Force focus on field (works with javascript only)
9636
	 *  @param	int		$disabled		Disabled
9637
	 *  @param	string	$morecss        More css added to the select component
9638
	 *  @param	string	$projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
9639
	 *  @param	string	$showproject	'all' = Show project info, ''=Hide project info
9640
	 *  @param	User	$usertofilter	User object to use for filtering
9641
	 *	@return int         			Nbr of project if OK, <0 if KO
9642
	 */
9643
	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)
9644
	{
9645
		global $user, $conf, $langs;
9646
9647
		require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
9648
9649
		if (is_null($usertofilter)) {
9650
			$usertofilter = $user;
9651
		}
9652
9653
		$out = '';
9654
9655
		$hideunselectables = false;
9656
		if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) {
9657
			$hideunselectables = true;
9658
		}
9659
9660
		if (empty($projectsListId)) {
9661
			if (empty($usertofilter->rights->projet->all->lire)) {
9662
				$projectstatic = new Project($this->db);
9663
				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
9664
			}
9665
		}
9666
9667
		// Search all projects
9668
		$sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref,
9669
            p.title, p.fk_soc, p.fk_statut, p.public,";
9670
		$sql .= ' s.nom as name';
9671
		$sql .= ' FROM '.$this->db->prefix().'projet as p';
9672
		$sql .= ' LEFT JOIN '.$this->db->prefix().'societe as s ON s.rowid = p.fk_soc,';
9673
		$sql .= ' '.$this->db->prefix().'facture as f';
9674
		$sql .= " WHERE p.entity IN (".getEntity('project').")";
9675
		$sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
9676
		//if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
9677
		//if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
9678
		//if ($socid > 0)  $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
9679
		$sql .= " ORDER BY p.ref, f.ref ASC";
9680
9681
		$resql = $this->db->query($sql);
9682
		if ($resql) {
9683
			// Use select2 selector
9684
			if (!empty($conf->use_javascript_ajax)) {
9685
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
9686
				$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
9687
				$out .= $comboenhancement;
9688
				$morecss = 'minwidth200imp maxwidth500';
9689
			}
9690
9691
			if (empty($option_only)) {
9692
				$out .= '<select class="valignmiddle flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').' id="'.$htmlname.'" name="'.$htmlname.'">';
9693
			}
9694
			if (!empty($show_empty)) {
9695
				$out .= '<option value="0" class="optiongrey">';
9696
				if (!is_numeric($show_empty)) {
9697
					$out .= $show_empty;
9698
				} else {
9699
					$out .= '&nbsp;';
9700
				}
9701
				$out .= '</option>';
9702
			}
9703
			$num = $this->db->num_rows($resql);
9704
			$i = 0;
9705
			if ($num) {
9706
				while ($i < $num) {
9707
					$obj = $this->db->fetch_object($resql);
9708
					// 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.
9709
					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire)) {
9710
						// Do nothing
9711
					} else {
9712
						if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
9713
							$i++;
9714
							continue;
9715
						}
9716
9717
						$labeltoshow = '';
9718
9719
						if ($showproject == 'all') {
9720
							$labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
9721
							if ($obj->name) {
9722
								$labeltoshow .= ' - '.$obj->name; // Soc name
9723
							}
9724
9725
							$disabled = 0;
9726
							if ($obj->fk_statut == Project::STATUS_DRAFT) {
9727
								$disabled = 1;
9728
								$labeltoshow .= ' - '.$langs->trans("Draft");
9729
							} elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
9730
								if ($discard_closed == 2) {
9731
									$disabled = 1;
9732
								}
9733
								$labeltoshow .= ' - '.$langs->trans("Closed");
9734
							} elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
9735
								$disabled = 1;
9736
								$labeltoshow .= ' - '.$langs->trans("LinkedToAnotherCompany");
9737
							}
9738
						}
9739
9740
						if (!empty($selected) && $selected == $obj->rowid) {
9741
							$out .= '<option value="'.$obj->rowid.'" selected';
9742
							//if ($disabled) $out.=' disabled';						// with select2, field can't be preselected if disabled
9743
							$out .= '>'.$labeltoshow.'</option>';
9744
						} else {
9745
							if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
9746
								$resultat = '';
9747
							} else {
9748
								$resultat = '<option value="'.$obj->rowid.'"';
9749
								if ($disabled) {
9750
									$resultat .= ' disabled';
9751
								}
9752
								//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
9753
								//else $labeltoshow.=' ('.$langs->trans("Private").')';
9754
								$resultat .= '>';
9755
								$resultat .= $labeltoshow;
9756
								$resultat .= '</option>';
9757
							}
9758
							$out .= $resultat;
9759
						}
9760
					}
9761
					$i++;
9762
				}
9763
			}
9764
			if (empty($option_only)) {
9765
				$out .= '</select>';
9766
			}
9767
9768
			print $out;
9769
9770
			$this->db->free($resql);
9771
			return $num;
9772
		} else {
9773
			dol_print_error($this->db);
9774
			return -1;
9775
		}
9776
	}
9777
9778
	/**
9779
	 *  Output a combo list with invoices qualified for a third party
9780
	 *
9781
	 * @param int $selected Id invoice preselected
9782
	 * @param string $htmlname Name of HTML select
9783
	 * @param int $maxlength Maximum length of label
9784
	 * @param int $option_only Return only html options lines without the select tag
9785
	 * @param string $show_empty Add an empty line ('1' or string to show for empty line)
9786
	 * @param int $forcefocus Force focus on field (works with javascript only)
9787
	 * @param int $disabled Disabled
9788
	 * @param string $morecss More css added to the select component
9789
	 * @return int                    Nbr of project if OK, <0 if KO
9790
	 */
9791
	public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
9792
	{
9793
		global $user, $conf, $langs;
9794
9795
		$out = '';
9796
9797
		dol_syslog('FactureRec::fetch', LOG_DEBUG);
9798
9799
		$sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
9800
		//$sql.= ', el.fk_source';
9801
		$sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
9802
		$sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
9803
		$sql .= " ORDER BY f.titre ASC";
9804
9805
		$resql = $this->db->query($sql);
9806
		if ($resql) {
9807
			// Use select2 selector
9808
			if (!empty($conf->use_javascript_ajax)) {
9809
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9810
				$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
9811
				$out .= $comboenhancement;
9812
				$morecss = 'minwidth200imp maxwidth500';
9813
			}
9814
9815
			if (empty($option_only)) {
9816
				$out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
9817
			}
9818
			if (!empty($show_empty)) {
9819
				$out .= '<option value="0" class="optiongrey">';
9820
				if (!is_numeric($show_empty)) {
9821
					$out .= $show_empty;
9822
				} else {
9823
					$out .= '&nbsp;';
9824
				}
9825
				$out .= '</option>';
9826
			}
9827
			$num = $this->db->num_rows($resql);
9828
			if ($num) {
9829
				while ($obj = $this->db->fetch_object($resql)) {
9830
					$labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
9831
9832
					$disabled = 0;
9833
					if (!empty($obj->suspended)) {
9834
						$disabled = 1;
9835
						$labeltoshow .= ' - ' . $langs->trans("Closed");
9836
					}
9837
9838
9839
					if (!empty($selected) && $selected == $obj->rowid) {
9840
						$out .= '<option value="' . $obj->rowid . '" selected';
9841
						//if ($disabled) $out.=' disabled';						// with select2, field can't be preselected if disabled
9842
						$out .= '>' . $labeltoshow . '</option>';
9843
					} else {
9844
						if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $hideunselectables seems to be never defined.
Loading history...
9845
							$resultat = '';
9846
						} else {
9847
							$resultat = '<option value="' . $obj->rowid . '"';
9848
							if ($disabled) {
9849
								$resultat .= ' disabled';
9850
							}
9851
							$resultat .= '>';
9852
							$resultat .= $labeltoshow;
9853
							$resultat .= '</option>';
9854
						}
9855
						$out .= $resultat;
9856
					}
9857
				}
9858
			}
9859
			if (empty($option_only)) {
9860
				$out .= '</select>';
9861
			}
9862
9863
			print $out;
9864
9865
			$this->db->free($resql);
9866
			return $num;
9867
		} else {
9868
			$this->errors[]=$this->db->lasterror;
9869
			return -1;
9870
		}
9871
	}
9872
9873
	/**
9874
	 * Output the component to make advanced search criteries
9875
	 *
9876
	 * @param	array		$arrayofcriterias			          Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...)
9877
	 * @param	array		$search_component_params	          Array of selected search criterias
9878
	 * @param   array       $arrayofinputfieldsalreadyoutput      Array of input fields already inform. The component will not generate a hidden input field if it is in this list.
9879
	 * @param	string		$search_component_params_hidden		  String with $search_component_params criterias
9880
	 * @return	string									          HTML component for advanced search
9881
	 */
9882
	public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '')
9883
	{
9884
		global $langs;
9885
9886
		$ret = '';
9887
9888
		$ret .= '<div class="divadvancedsearchfieldcomp inline-block">';
9889
		//$ret .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9890
		$ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
9891
		$ret .= '<span class="fas fa-filter linkobject boxfilter pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("Filters")).'" id="idsubimgproductdistribution"></span>';
9892
		//$ret .= $langs->trans("Filters");
9893
		$ret .= '</a>';
9894
9895
		$ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
9896
9897
		// Show select fields as tags.
9898
		$ret .= '<div name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
9899
9900
		if ($search_component_params_hidden) {
9901
			if (!preg_match('/^\(.*\)$/', $search_component_params_hidden)) {	// If $search_component_params_hidden does not start and end with ()
9902
				$search_component_params_hidden .= '('.$search_component_params_hidden.')';
9903
			}
9904
			$errormessage = '';
9905
			if (!dolCheckFilters($search_component_params_hidden, $errormessage)) {
9906
				print 'ERROR in parsing search string';
9907
			}
9908
			$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
9909
			//var_dump($search_component_params_hidden);
9910
			$htmltags = preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $search_component_params_hidden);
9911
			//var_dump($htmltags);
9912
			$ret .= '<span class="marginleftonlyshort valignmiddle tagsearch"><span class="tagsearchdelete select2-selection__choice__remove">x</span> '.$htmltags.'</span>';
9913
		}
9914
9915
		//$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9916
9917
		//$ret .= search_component_params
9918
		//$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
9919
		//$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
9920
9921
		$show_search_component_params_hidden = 1;
9922
		if ($show_search_component_params_hidden) {
9923
			$ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
9924
		}
9925
		$ret .= "<!-- We store the full search string into this field. For example: (t.ref:like:'SO-%') and ((t.ref:like:'CO-%') or (t.ref:like:'AA%')) -->";
9926
		$ret .= '<input type="hidden" name="search_component_params_hidden" value="'.dol_escape_htmltag($search_component_params_hidden).'">';
9927
		// For compatibility with forms that show themself the search criteria in addition of this component, we output the fields
9928
		foreach ($arrayofcriterias as $criterias) {
9929
			foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {
9930
				if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
9931
					continue;
9932
				}
9933
				if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
9934
					continue;
9935
				}
9936
				if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
9937
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_start">';
9938
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startyear">';
9939
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startmonth">';
9940
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startday">';
9941
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_end">';
9942
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endyear">';
9943
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endmonth">';
9944
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endday">';
9945
				} else {
9946
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'">';
9947
				}
9948
			}
9949
		}
9950
9951
		$ret .= '</div>';
9952
9953
		$ret .= "<!-- Syntax of Generic filter string: t.ref:like:'SO-%', t.date_creation:<:'20160101', t.date_creation:<:'2016-01-01 12:30:00', t.nature:is:NULL, t.field2:isnot:NULL -->\n";
9954
		$ret .= '<input type="text" placeholder="'.$langs->trans("Search").'" name="search_component_params_input" class="noborderbottom search_component_input" value="">';
9955
9956
		$ret .= '</div>';
9957
		$ret .= '</div>';
9958
9959
		return $ret;
9960
	}
9961
9962
	/**
9963
	 * selectModelMail
9964
	 *
9965
	 * @param   string   $prefix     	Prefix
9966
	 * @param   string   $modelType  	Model type
9967
	 * @param	int		 $default	 	1=Show also Default mail template
9968
	 * @param	int		 $addjscombo	Add js combobox
9969
	 * @return  string               	HTML select string
9970
	 */
9971
	public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
9972
	{
9973
		global $langs, $user;
9974
9975
		$retstring = '';
9976
9977
		$TModels = array();
9978
9979
		include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
9980
		$formmail = new FormMail($this->db);
9981
		$result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
9982
9983
		if ($default) {
9984
			$TModels[0] = $langs->trans('DefaultMailModel');
9985
		}
9986
		if ($result > 0) {
9987
			foreach ($formmail->lines_model as $model) {
9988
				$TModels[$model->id] = $model->label;
9989
			}
9990
		}
9991
9992
		$retstring .= '<select class="flat" id="select_'.$prefix.'model_mail" name="'.$prefix.'model_mail">';
9993
9994
		foreach ($TModels as $id_model => $label_model) {
9995
			$retstring .= '<option value="'.$id_model.'"';
9996
			$retstring .= ">".$label_model."</option>";
9997
		}
9998
9999
		$retstring .= "</select>";
10000
10001
		if ($addjscombo) {
10002
			$retstring .= ajax_combobox('select_'.$prefix.'model_mail');
10003
		}
10004
10005
		return $retstring;
10006
	}
10007
10008
	/**
10009
	 * Output the buttons to submit a creation/edit form
10010
	 *
10011
	 * @param   string  $save_label     Alternative label for save button
10012
	 * @param   string  $cancel_label   Alternative label for cancel button
10013
	 * @param   array   $morebuttons    Add additional buttons between save and cancel
10014
	 * @param   bool    $withoutdiv     Option to remove enclosing centered div
10015
	 * @param	string	$morecss		More CSS
10016
	 * @return 	string					Html code with the buttons
10017
	 */
10018
	public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = 0, $morecss = '')
10019
	{
10020
		global $langs;
10021
10022
		$buttons = array();
10023
10024
		$save = array(
10025
			'name' => 'save',
10026
			'label_key' => $save_label,
10027
		);
10028
10029
		if ($save_label == 'Create' || $save_label == 'Add' ) {
10030
			$save['name'] = 'add';
10031
		} elseif ($save_label == 'Modify') {
10032
			$save['name'] = 'edit';
10033
		}
10034
10035
		$cancel = array(
10036
				'name' => 'cancel',
10037
				'label_key' => 'Cancel',
10038
		);
10039
10040
		!empty($save_label) ? $buttons[] = $save : '';
10041
10042
		if (!empty($morebuttons)) {
10043
			$buttons[] = $morebuttons;
10044
		}
10045
10046
		!empty($cancel_label) ? $buttons[] = $cancel : '';
10047
10048
		$retstring = $withoutdiv ? '': '<div class="center">';
10049
10050
		foreach ($buttons as $button) {
10051
			$addclass = empty($button['addclass']) ? '' : $button['addclass'];
10052
			$retstring .= '<input type="submit" class="button button-'.$button['name'].($morecss ? ' '.$morecss : '').' '.$addclass.'" name="'.$button['name'].'" value="'.dol_escape_htmltag($langs->trans($button['label_key'])).'">';
10053
		}
10054
		$retstring .= $withoutdiv ? '': '</div>';
10055
10056
		return $retstring;
10057
	}
10058
}
10059