Passed
Branch develop (e8e6ec)
by
unknown
28:30
created

Form::select_dolusers_forevent()   F

Complexity

Conditions 17
Paths 416

Size

Total Lines 66
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 38
c 0
b 0
f 0
nc 416
nop 15
dl 0
loc 66
rs 1.8611

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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

797
				if (empty($disablefavorites)) 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

797
				if (empty($disablefavorites)) array_multisort($favorite, SORT_DESC, $label, /** @scrutinizer ignore-type */ SORT_ASC, $countryArray);
Loading history...
798
				else $countryArray = dol_sort_array($countryArray, 'label');
799
800
				if ($showempty)
801
				{
802
					$out .= '<option value="">&nbsp;</option>'."\n";
803
				}
804
805
				if ($addspecialentries)	// Add dedicated entries for groups of countries
806
				{
807
					//if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
808
					$out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
809
					$out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
810
					if ($mysoc->isInEEC()) $out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
811
					$out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
812
					$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
813
				}
814
815
				foreach ($countryArray as $row)
816
				{
817
					//if (empty($showempty) && empty($row['rowid'])) continue;
818
					if (empty($row['rowid'])) continue;
819
					if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) continue; // exclude some countries
820
821
					if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) $atleastonefavorite++;
822
					if (empty($row['favorite']) && $atleastonefavorite)
823
					{
824
						$atleastonefavorite = 0;
825
						$out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
826
					}
827
828
					$labeltoshow = '';
829
					if ($row['label']) $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
830
					else $labeltoshow .= '&nbsp;';
831
					if ($row['code_iso']) {
832
						$labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
833
						$tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium marginrightonly"');
834
						$labeltoshow = $tmpflag.' '.$labeltoshow;
835
					}
836
837
					if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
838
						$out .= '<option value="'.($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']).'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
839
					} else {
840
						$out .= '<option value="'.($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']).'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
841
					}
842
					$out .= $labeltoshow;
843
					$out .= '</option>';
844
				}
845
			}
846
			$out .= '</select>';
847
		} else {
848
			dol_print_error($this->db);
849
		}
850
851
		// Make select dynamic
852
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
853
		$out .= ajax_combobox('select'.$htmlname);
854
855
		return $out;
856
	}
857
858
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
859
	/**
860
	 *  Return select list of incoterms
861
	 *
862
	 *  @param	string	$selected       		Id or Code of preselected incoterm
863
	 *  @param	string	$location_incoterms     Value of input location
864
	 *  @param	string	$page       			Defined the form action
865
	 *  @param  string	$htmlname       		Name of html select object
866
	 *  @param  string	$htmloption     		Options html on select object
867
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
868
	 *  @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')))
869
	 *  @return string           				HTML string with select and input
870
	 */
871
	public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array())
872
	{
873
		// phpcs:enable
874
		global $conf, $langs;
875
876
		$langs->load("dict");
877
878
		$out = '';
879
		$incotermArray = array();
880
881
		$sql = "SELECT rowid, code";
882
		$sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms";
883
		$sql .= " WHERE active > 0";
884
		$sql .= " ORDER BY code ASC";
885
886
		dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
887
		$resql = $this->db->query($sql);
888
		if ($resql)
889
		{
890
			if ($conf->use_javascript_ajax && !$forcecombo)
891
			{
892
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
893
				$out .= ajax_combobox($htmlname, $events);
894
			}
895
896
			if (!empty($page))
897
			{
898
				$out .= '<form method="post" action="'.$page.'">';
899
				$out .= '<input type="hidden" name="action" value="set_incoterms">';
900
				$out .= '<input type="hidden" name="token" value="'.newToken().'">';
901
			}
902
903
			$out .= '<select id="'.$htmlname.'" class="flat selectincoterm minwidth100imp noenlargeonsmartphone" name="'.$htmlname.'" '.$htmloption.'>';
904
			$out .= '<option value="0">&nbsp;</option>';
905
			$num = $this->db->num_rows($resql);
906
			$i = 0;
907
			if ($num)
908
			{
909
				$foundselected = false;
910
911
				while ($i < $num)
912
				{
913
					$obj = $this->db->fetch_object($resql);
914
					$incotermArray[$i]['rowid'] = $obj->rowid;
915
					$incotermArray[$i]['code'] = $obj->code;
916
					$i++;
917
				}
918
919
				foreach ($incotermArray as $row)
920
				{
921
					if ($selected && ($selected == $row['rowid'] || $selected == $row['code']))
922
					{
923
						$out .= '<option value="'.$row['rowid'].'" selected>';
924
					} else {
925
						$out .= '<option value="'.$row['rowid'].'">';
926
					}
927
928
					if ($row['code']) $out .= $row['code'];
929
930
					$out .= '</option>';
931
				}
932
			}
933
			$out .= '</select>';
934
935
			$out .= '<input id="location_incoterms" class="maxwidth100onsmartphone" name="location_incoterms" value="'.$location_incoterms.'">';
936
937
			if (!empty($page))
938
			{
939
				$out .= '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'"></form>';
940
			}
941
		} else {
942
			dol_print_error($this->db);
943
		}
944
945
		return $out;
946
	}
947
948
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
949
	/**
950
	 *	Return list of types of lines (product or service)
951
	 * 	Example: 0=product, 1=service, 9=other (for external module)
952
	 *
953
	 *	@param  string	$selected       Preselected type
954
	 *	@param  string	$htmlname       Name of field in html form
955
	 * 	@param	int		$showempty		Add an empty field
956
	 * 	@param	int		$hidetext		Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
957
	 * 	@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')
958
	 *  @return	void
959
	 */
960
	public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
961
	{
962
		// phpcs:enable
963
		global $db, $langs, $user, $conf;
964
965
		// If product & services are enabled or both disabled.
966
		if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled))
967
			|| (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled)))
968
		{
969
			if (empty($hidetext)) print $langs->trans("Type").': ';
970
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
971
			if ($showempty)
972
			{
973
				print '<option value="-1"';
974
				if ($selected == -1) print ' selected';
975
				print '>&nbsp;</option>';
976
			}
977
978
			print '<option value="0"';
979
			if (0 == $selected) print ' selected';
980
			print '>'.$langs->trans("Product");
981
982
			print '<option value="1"';
983
			if (1 == $selected) print ' selected';
984
			print '>'.$langs->trans("Service");
985
986
			print '</select>';
987
			print ajax_combobox('select_'.$htmlname);
988
			//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
989
		}
990
		if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3)
991
		{
992
			print $langs->trans("Service");
993
			print '<input type="hidden" name="'.$htmlname.'" value="1">';
994
		}
995
		if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2)
996
		{
997
			print $langs->trans("Product");
998
			print '<input type="hidden" name="'.$htmlname.'" value="0">';
999
		}
1000
		if ($forceall < 0)	// This should happened only for contracts when both predefined product and service are disabled.
1001
		{
1002
			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
1003
		}
1004
	}
1005
1006
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1007
	/**
1008
	 *	Load into cache cache_types_fees, array of types of fees
1009
	 *
1010
	 *	@return     int             Nb of lines loaded, <0 if KO
1011
	 */
1012
	public function load_cache_types_fees()
1013
	{
1014
		// phpcs:enable
1015
		global $langs;
1016
1017
		$num = count($this->cache_types_fees);
1018
		if ($num > 0) return 0; // Cache already loaded
1019
1020
		dol_syslog(__METHOD__, LOG_DEBUG);
1021
1022
		$langs->load("trips");
1023
1024
		$sql = "SELECT c.code, c.label";
1025
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
1026
		$sql .= " WHERE active > 0";
1027
1028
		$resql = $this->db->query($sql);
1029
		if ($resql)
1030
		{
1031
			$num = $this->db->num_rows($resql);
1032
			$i = 0;
1033
1034
			while ($i < $num)
1035
			{
1036
				$obj = $this->db->fetch_object($resql);
1037
1038
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1039
				$label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1040
				$this->cache_types_fees[$obj->code] = $label;
1041
				$i++;
1042
			}
1043
1044
			asort($this->cache_types_fees);
1045
1046
			return $num;
1047
		} else {
1048
			dol_print_error($this->db);
1049
			return -1;
1050
		}
1051
	}
1052
1053
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1054
	/**
1055
	 *	Return list of types of notes
1056
	 *
1057
	 *	@param	string		$selected		Preselected type
1058
	 *	@param  string		$htmlname		Name of field in form
1059
	 * 	@param	int			$showempty		Add an empty field
1060
	 * 	@return	void
1061
	 */
1062
	public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1063
	{
1064
		// phpcs:enable
1065
		global $user, $langs;
1066
1067
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1068
1069
		$this->load_cache_types_fees();
1070
1071
		print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1072
		if ($showempty)
1073
		{
1074
			print '<option value="-1"';
1075
			if ($selected == -1) print ' selected';
1076
			print '>&nbsp;</option>';
1077
		}
1078
1079
		foreach ($this->cache_types_fees as $key => $value)
1080
		{
1081
			print '<option value="'.$key.'"';
1082
			if ($key == $selected) print ' selected';
1083
			print '>';
1084
			print $value;
1085
			print '</option>';
1086
		}
1087
1088
		print '</select>';
1089
		if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1090
	}
1091
1092
1093
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1094
	/**
1095
	 *  Output html form to select a third party
1096
	 *
1097
	 *	@param	string	$selected       		Preselected type
1098
	 *	@param  string	$htmlname       		Name of field in form
1099
	 *  @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)')
1100
	 *	@param	string	$showempty				Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
1101
	 * 	@param	int		$showtype				Show third party type in combolist (customer, prospect or supplier)
1102
	 * 	@param	int		$forcecombo				Force to load all values and output a standard combobox (with no beautification)
1103
	 *  @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')))
1104
	 *	@param	int		$limit					Maximum number of elements
1105
	 *  @param	string	$morecss				Add more css styles to the SELECT component
1106
	 *	@param  string	$moreparam      		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1107
	 *	@param	string	$selected_input_value	Value of preselected input text (for use with ajax)
1108
	 *  @param	int		$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1109
	 *  @param	array	$ajaxoptions			Options for ajax_autocompleter
1110
	 * 	@param  bool	$multiple				add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
1111
	 * 	@return	string							HTML string with select box for thirdparty.
1112
	 */
1113
	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)
1114
	{
1115
		// phpcs:enable
1116
		global $conf, $user, $langs;
1117
1118
		$out = '';
1119
1120
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo)
1121
		{
1122
			// No immediate load of all database
1123
			$placeholder = '';
1124
			if ($selected && empty($selected_input_value))
1125
			{
1126
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1127
				$societetmp = new Societe($this->db);
1128
				$societetmp->fetch($selected);
1129
				$selected_input_value = $societetmp->name;
1130
				unset($societetmp);
1131
			}
1132
			// mode 1
1133
			$urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).($showtype ? '&showtype='.urlencode($showtype) : '');
1134
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1135
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 250; }</style>';
1136
			if (empty($hidelabel)) print $langs->trans("RefOrLabel").' : ';
1137
			elseif ($hidelabel > 1) {
1138
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
1139
				if ($hidelabel == 2) {
1140
					$out .= img_picto($langs->trans("Search"), 'search');
1141
				}
1142
			}
1143
			$out .= '<input type="text" class="'.$morecss.'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
1144
			if ($hidelabel == 3) {
1145
				$out .= img_picto($langs->trans("Search"), 'search');
1146
			}
1147
		} else {
1148
			// Immediate load of all database
1149
			$out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple);
1150
		}
1151
1152
		return $out;
1153
	}
1154
1155
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1156
	/**
1157
	 *  Output html form to select a third party.
1158
	 *  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.
1159
	 *
1160
	 *	@param	string	$selected       Preselected type
1161
	 *	@param  string	$htmlname       Name of field in form
1162
	 *  @param  string	$filter         Optional filters criteras (example: 's.rowid <> x', 's.client in (1,3)')
1163
	 *	@param	string	$showempty		Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
1164
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
1165
	 * 	@param	int		$forcecombo		Force to use standard HTML select component without beautification
1166
	 *  @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')))
1167
	 *  @param	string	$filterkey		Filter on key value
1168
	 *  @param	int		$outputmode		0=HTML select string, 1=Array
1169
	 *  @param	int		$limit			Limit number of answers
1170
	 *  @param	string	$morecss		Add more css styles to the SELECT component
1171
	 *	@param  string	$moreparam      Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1172
	 *	@param  bool	$multiple       add [] in the name of element and add 'multiple' attribut
1173
	 * 	@return	string					HTML string with
1174
	 */
1175
	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)
1176
	{
1177
		// phpcs:enable
1178
		global $conf, $user, $langs;
1179
1180
		$out = '';
1181
		$num = 0;
1182
		$outarray = array();
1183
1184
		if ($selected === '') $selected = array();
1185
		elseif (!is_array($selected)) $selected = array($selected);
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
1186
1187
		// Clean $filter that may contains sql conditions so sql code
1188
		if (function_exists('testSqlAndScriptInject')) {
1189
			if (testSqlAndScriptInject($filter, 3) > 0) {
1190
				$filter = '';
1191
			}
1192
		}
1193
1194
		// We search companies
1195
		$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1196
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1197
			$sql .= ", s.address, s.zip, s.town";
1198
			$sql .= ", dictp.code as country_code";
1199
		}
1200
		$sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
1201
		if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1202
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid = s.fk_pays";
1203
		}
1204
		if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1205
		$sql .= " WHERE s.entity IN (".getEntity('societe').")";
1206
		if (!empty($user->socid)) $sql .= " AND s.rowid = ".$user->socid;
1207
		if ($filter) $sql .= " AND (".$filter.")";
1208
		if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id;
1209
		if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND s.status <> 0";
1210
		// Add criteria
1211
		if ($filterkey && $filterkey != '')
1212
		{
1213
			$sql .= " AND (";
1214
			$prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1215
			// For natural search
1216
			$scrit = explode(' ', $filterkey);
1217
			$i = 0;
1218
			if (count($scrit) > 1) $sql .= "(";
1219
			foreach ($scrit as $crit) {
1220
				if ($i > 0) $sql .= " AND ";
1221
				$sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1222
				$i++;
1223
			}
1224
			if (count($scrit) > 1) $sql .= ")";
1225
			if (!empty($conf->barcode->enabled))
1226
			{
1227
				$sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1228
			}
1229
			$sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1230
			$sql .= ")";
1231
		}
1232
		$sql .= $this->db->order("nom", "ASC");
1233
		$sql .= $this->db->plimit($limit, 0);
1234
1235
		// Build output string
1236
		dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1237
		$resql = $this->db->query($sql);
1238
		if ($resql)
1239
		{
1240
			if (!$forcecombo)
1241
			{
1242
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1243
				$out .= ajax_combobox($htmlname, $events, $conf->global->COMPANY_USE_SEARCH_TO_SELECT);
1244
			}
1245
1246
			// Construct $out and $outarray
1247
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1248
1249
			$textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1250
			if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT))
1251
			{
1252
				// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1253
				//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1254
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
1255
				else $textifempty .= $langs->trans("All");
1256
			}
1257
			if ($showempty) $out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.$textifempty.'</span>').'">'.$textifempty.'</option>'."\n";
1258
1259
			$num = $this->db->num_rows($resql);
1260
			$i = 0;
1261
			if ($num)
1262
			{
1263
				while ($i < $num)
1264
				{
1265
					$obj = $this->db->fetch_object($resql);
1266
					$label = '';
1267
					if ($conf->global->SOCIETE_ADD_REF_IN_LIST) {
1268
						if (($obj->client) && (!empty($obj->code_client))) {
1269
							$label = $obj->code_client.' - ';
1270
						}
1271
						if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1272
							$label .= $obj->code_fournisseur.' - ';
1273
						}
1274
						$label .= ' '.$obj->name;
1275
					} else {
1276
						$label = $obj->name;
1277
					}
1278
1279
					if (!empty($obj->name_alias)) {
1280
						$label .= ' ('.$obj->name_alias.')';
1281
					}
1282
1283
					if ($showtype)
1284
					{
1285
						if ($obj->client || $obj->fournisseur) $label .= ' (';
1286
						if ($obj->client == 1 || $obj->client == 3) $label .= $langs->trans("Customer");
1287
						if ($obj->client == 2 || $obj->client == 3) $label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1288
						if ($obj->fournisseur) $label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1289
						if ($obj->client || $obj->fournisseur) $label .= ')';
1290
					}
1291
1292
					if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1293
						$label .= ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1294
						if (!empty($obj->country_code)) {
1295
							$label .= ', '.$langs->trans('Country'.$obj->country_code);
1296
						}
1297
					}
1298
1299
					if (empty($outputmode))
1300
					{
1301
						if (in_array($obj->rowid, $selected))
1302
						{
1303
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
1304
						} else {
1305
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
1306
						}
1307
					} else {
1308
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
1309
					}
1310
1311
					$i++;
1312
					if (($i % 10) == 0) $out .= "\n";
1313
				}
1314
			}
1315
			$out .= '</select>'."\n";
1316
		} else {
1317
			dol_print_error($this->db);
1318
		}
1319
1320
		$this->result = array('nbofthirdparties'=>$num);
1321
1322
		if ($outputmode) return $outarray;
1323
		return $out;
1324
	}
1325
1326
1327
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1328
	/**
1329
	 *  Return HTML combo list of absolute discounts
1330
	 *
1331
	 *  @param	string	$selected       Id remise fixe pre-selectionnee
1332
	 *  @param  string	$htmlname       Nom champ formulaire
1333
	 *  @param  string	$filter         Criteres optionnels de filtre
1334
	 *  @param	int		$socid			Id of thirdparty
1335
	 *  @param	int		$maxvalue		Max value for lines that can be selected
1336
	 *  @return	int						Return number of qualifed lines in list
1337
	 */
1338
	public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1339
	{
1340
		// phpcs:enable
1341
		global $langs, $conf;
1342
1343
		// On recherche les remises
1344
		$sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1345
		$sql .= " re.description, re.fk_facture_source";
1346
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1347
		$sql .= " WHERE re.fk_soc = ".(int) $socid;
1348
		$sql .= " AND re.entity = ".$conf->entity;
1349
		if ($filter) $sql .= " AND ".$filter;
1350
		$sql .= " ORDER BY re.description ASC";
1351
1352
		dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1353
		$resql = $this->db->query($sql);
1354
		if ($resql)
1355
		{
1356
			print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1357
			$num = $this->db->num_rows($resql);
1358
1359
			$qualifiedlines = $num;
1360
1361
			$i = 0;
1362
			if ($num)
1363
			{
1364
				print '<option value="0">&nbsp;</option>';
1365
				while ($i < $num)
1366
				{
1367
					$obj = $this->db->fetch_object($resql);
1368
					$desc = dol_trunc($obj->description, 40);
1369
					if (preg_match('/\(CREDIT_NOTE\)/', $desc)) $desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1370
					if (preg_match('/\(DEPOSIT\)/', $desc)) $desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1371
					if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) $desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1372
					if (preg_match('/\(EXCESS PAID\)/', $desc)) $desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1373
1374
					$selectstring = '';
1375
					if ($selected > 0 && $selected == $obj->rowid) $selectstring = ' selected';
1376
1377
					$disabled = '';
1378
					if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue)
1379
					{
1380
						$qualifiedlines--;
1381
						$disabled = ' disabled';
1382
					}
1383
1384
					if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source))
1385
					{
1386
						$tmpfac = new Facture($this->db);
1387
						if ($tmpfac->fetch($obj->fk_facture_source) > 0) $desc = $desc.' - '.$tmpfac->ref;
1388
					}
1389
1390
					print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1391
					$i++;
1392
				}
1393
			}
1394
			print '</select>';
1395
			return $qualifiedlines;
1396
		} else {
1397
			dol_print_error($this->db);
1398
			return -1;
1399
		}
1400
	}
1401
1402
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1403
	/**
1404
	 *  Return list of all contacts (for a third party or all)
1405
	 *
1406
	 *  @param	int		$socid      	Id ot third party or 0 for all
1407
	 *  @param  string	$selected   	Id contact pre-selectionne
1408
	 *  @param  string	$htmlname  	    Name of HTML field ('none' for a not editable field)
1409
	 *  @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
1410
	 *  @param  string	$exclude        List of contacts id to exclude
1411
	 *  @param	string	$limitto		Disable answers that are not id in this array list
1412
	 *  @param	integer	$showfunction   Add function into label
1413
	 *  @param	string	$moreclass		Add more class to class style
1414
	 *  @param	integer	$showsoc	    Add company into label
1415
	 *  @param	int		$forcecombo		Force to use combo box
1416
	 *  @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')))
1417
	 *  @param	bool	$options_only	Return options only (for ajax treatment)
1418
	 *  @param	string	$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1419
	 *  @param	string	$htmlid			Html id to use instead of htmlname
1420
	 *  @return	int						<0 if KO, Nb of contact in list if OK
1421
	 *  @deprecated						You can use selectcontacts directly (warning order of param was changed)
1422
	 */
1423
	public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '')
1424
	{
1425
		// phpcs:enable
1426
		print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1427
		return $this->num;
1428
	}
1429
1430
	/**
1431
	 *	Return HTML code of the SELECT of list of all contacts (for a third party or all).
1432
	 *  This also set the number of contacts found into $this->num
1433
	 *
1434
	 * @since 9.0 Add afterSelectContactOptions hook
1435
	 *
1436
	 *	@param	int			$socid      	Id ot third party or 0 for all or -1 for empty list
1437
	 *	@param  array|int	$selected   	Array of ID of pre-selected contact id
1438
	 *	@param  string		$htmlname  	    Name of HTML field ('none' for a not editable field)
1439
	 *	@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
1440
	 *	@param  string		$exclude        List of contacts id to exclude
1441
	 *	@param	string		$limitto		Disable answers that are not id in this array list
1442
	 *	@param	integer		$showfunction   Add function into label
1443
	 *	@param	string		$moreclass		Add more class to class style
1444
	 *	@param	bool		$options_only	Return options only (for ajax treatment)
1445
	 *	@param	integer		$showsoc	    Add company into label
1446
	 * 	@param	int			$forcecombo		Force to use combo box (so no ajax beautify effect)
1447
	 *  @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')))
1448
	 *  @param	string		$moreparam		Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1449
	 *  @param	string		$htmlid			Html id to use instead of htmlname
1450
	 *  @param	bool		$multiple		add [] in the name of element and add 'multiple' attribut
1451
	 *  @param	integer		$disableifempty Set tag 'disabled' on select if there is no choice
1452
	 *	@return	 int|string					<0 if KO, HTML with select string if OK.
1453
	 */
1454
	public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0)
1455
	{
1456
		global $conf, $langs, $hookmanager, $action;
1457
1458
		$langs->load('companies');
1459
1460
		if (empty($htmlid)) $htmlid = $htmlname;
1461
		$num = 0;
1462
1463
		if ($selected === '') $selected = array();
1464
		elseif (!is_array($selected)) $selected = array($selected);
1465
		$out = '';
1466
1467
		if (!is_object($hookmanager))
1468
		{
1469
			include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1470
			$hookmanager = new HookManager($this->db);
1471
		}
1472
1473
		// We search third parties
1474
		$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";
1475
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) $sql .= ", s.nom as company, s.town AS company_town";
1476
		$sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp";
1477
		if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) $sql .= " LEFT OUTER JOIN  ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc";
1478
		$sql .= " WHERE sp.entity IN (".getEntity('socpeople').")";
1479
		if ($socid > 0 || $socid == -1) $sql .= " AND sp.fk_soc=".$socid;
1480
		if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND sp.statut <> 0";
1481
		$sql .= " ORDER BY sp.lastname ASC";
1482
1483
		dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1484
		$resql = $this->db->query($sql);
1485
		if ($resql)
1486
		{
1487
			$num = $this->db->num_rows($resql);
1488
1489
			if ($conf->use_javascript_ajax && !$forcecombo && !$options_only)
1490
			{
1491
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1492
				$out .= ajax_combobox($htmlid, $events, $conf->global->CONTACT_USE_SEARCH_TO_SELECT);
1493
			}
1494
1495
			if ($htmlname != 'none' && !$options_only) {
1496
				$out .= '<select class="flat'.($moreclass ? ' '.$moreclass : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1497
			}
1498
1499
			if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1500
			if ($showempty == 2) $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1501
1502
			$i = 0;
1503
			if ($num)
1504
			{
1505
				include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1506
				$contactstatic = new Contact($this->db);
1507
1508
				while ($i < $num)
1509
				{
1510
					$obj = $this->db->fetch_object($resql);
1511
1512
					// Set email (or phones) and town extended infos
1513
					$extendedInfos = '';
1514
					if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1515
						$extendedInfos = array();
1516
						$email = trim($obj->email);
1517
						if (!empty($email)) $extendedInfos[] = $email;
1518
						else {
1519
							$phone = trim($obj->phone);
1520
							$phone_perso = trim($obj->phone_perso);
1521
							$phone_mobile = trim($obj->phone_mobile);
1522
							if (!empty($phone)) $extendedInfos[] = $phone;
1523
							if (!empty($phone_perso)) $extendedInfos[] = $phone_perso;
1524
							if (!empty($phone_mobile)) $extendedInfos[] = $phone_mobile;
1525
						}
1526
						$contact_town = trim($obj->contact_town);
1527
						$company_town = trim($obj->company_town);
1528
						if (!empty($contact_town)) $extendedInfos[] = $contact_town;
1529
						elseif (!empty($company_town)) $extendedInfos[] = $company_town;
1530
						$extendedInfos = implode(' - ', $extendedInfos);
1531
						if (!empty($extendedInfos)) $extendedInfos = ' - '.$extendedInfos;
1532
					}
1533
1534
					$contactstatic->id = $obj->rowid;
1535
					$contactstatic->lastname = $obj->lastname;
1536
					$contactstatic->firstname = $obj->firstname;
1537
					if ($obj->statut == 1) {
1538
						if ($htmlname != 'none')
1539
						{
1540
							$disabled = 0;
1541
							if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) $disabled = 1;
1542
							if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) $disabled = 1;
1543
							if (!empty($selected) && in_array($obj->rowid, $selected))
1544
							{
1545
								$out .= '<option value="'.$obj->rowid.'"';
1546
								if ($disabled) $out .= ' disabled';
1547
								$out .= ' selected>';
1548
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1549
								if ($showfunction && $obj->poste) $out .= ' ('.$obj->poste.')';
1550
								if (($showsoc > 0) && $obj->company) $out .= ' - ('.$obj->company.')';
1551
								$out .= '</option>';
1552
							} else {
1553
								$out .= '<option value="'.$obj->rowid.'"';
1554
								if ($disabled) $out .= ' disabled';
1555
								$out .= '>';
1556
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1557
								if ($showfunction && $obj->poste) $out .= ' ('.$obj->poste.')';
1558
								if (($showsoc > 0) && $obj->company) $out .= ' - ('.$obj->company.')';
1559
								$out .= '</option>';
1560
							}
1561
						} else {
1562
							if (in_array($obj->rowid, $selected))
1563
							{
1564
								$out .= $contactstatic->getFullName($langs).$extendedInfos;
1565
								if ($showfunction && $obj->poste) $out .= ' ('.$obj->poste.')';
1566
								if (($showsoc > 0) && $obj->company) $out .= ' - ('.$obj->company.')';
1567
							}
1568
						}
1569
					}
1570
					$i++;
1571
				}
1572
			} else {
1573
				$labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1574
				$out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1575
				$out .= $labeltoshow;
1576
				$out .= '</option>';
1577
			}
1578
1579
			$parameters = array(
1580
				'socid'=>$socid,
1581
				'htmlname'=>$htmlname,
1582
				'resql'=>$resql,
1583
				'out'=>&$out,
1584
				'showfunction'=>$showfunction,
1585
				'showsoc'=>$showsoc,
1586
			);
1587
1588
			$reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1589
1590
			if ($htmlname != 'none' && !$options_only)
1591
			{
1592
				$out .= '</select>';
1593
			}
1594
1595
			$this->num = $num;
1596
			return $out;
1597
		} else {
1598
			dol_print_error($this->db);
1599
			return -1;
1600
		}
1601
	}
1602
1603
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1604
	/**
1605
	 *	Return the HTML select list of users
1606
	 *
1607
	 *  @param	string			$selected       Id user preselected
1608
	 *  @param  string			$htmlname       Field name in form
1609
	 *  @param  int				$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
1610
	 *  @param  array			$exclude        Array list of users id to exclude
1611
	 * 	@param	int				$disabled		If select list must be disabled
1612
	 *  @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
1613
	 * 	@param	int				$enableonly		Array list of users id to be enabled. All other must be disabled
1614
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1615
	 * 	@return	void
1616
	 *  @deprecated		Use select_dolusers instead
1617
	 *  @see select_dolusers()
1618
	 */
1619
	public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1620
	{
1621
		// phpcs:enable
1622
		print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1623
	}
1624
1625
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1626
	/**
1627
	 *	Return select list of users
1628
	 *
1629
	 *  @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)
1630
	 *  @param  string			$htmlname       Field name in form
1631
	 *  @param  int				$show_empty     0=list with no empty value, 1=add also an empty value into list
1632
	 *  @param  array			$exclude        Array list of users id to exclude
1633
	 * 	@param	int				$disabled		If select list must be disabled
1634
	 *  @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
1635
	 * 	@param	array			$enableonly		Array list of users id to be enabled. If defined, it means that others will be disabled
1636
	 *  @param	string			$force_entity	'0' or Ids of environment to force
1637
	 *  @param	int				$maxlength		Maximum length of string into list (0=no limit)
1638
	 *  @param	int				$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
1639
	 *  @param	string			$morefilter		Add more filters into sql request (Example: 'employee = 1')
1640
	 *  @param	integer			$show_every		0=default list, 1=add also a value "Everybody" at beginning of list
1641
	 *  @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.
1642
	 *  @param	string			$morecss		More css
1643
	 *  @param  int     		$noactive       Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
1644
	 *  @param  int				$outputmode     0=HTML select string, 1=Array
1645
	 *  @param  bool			$multiple       add [] in the name of element and add 'multiple' attribut
1646
	 * 	@return	string							HTML select string
1647
	 *  @see select_dolgroups()
1648
	 */
1649
	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)
1650
	{
1651
		// phpcs:enable
1652
		global $conf, $user, $langs, $hookmanager;
1653
1654
		// If no preselected user defined, we take current user
1655
		if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) $selected = $user->id;
1656
1657
		if ($selected === '') $selected = array();
1658
		elseif (!is_array($selected)) $selected = array($selected);
1659
1660
		$excludeUsers = null;
1661
		$includeUsers = null;
1662
1663
		// Permettre l'exclusion d'utilisateurs
1664
		if (is_array($exclude))	$excludeUsers = implode(",", $exclude);
1665
		// Permettre l'inclusion d'utilisateurs
1666
		if (is_array($include))	$includeUsers = implode(",", $include);
1667
		elseif ($include == 'hierarchy')
1668
		{
1669
			// Build list includeUsers to have only hierarchy
1670
			$includeUsers = implode(",", $user->getAllChildIds(0));
1671
		} elseif ($include == 'hierarchyme')
1672
		{
1673
			// Build list includeUsers to have only hierarchy and current user
1674
			$includeUsers = implode(",", $user->getAllChildIds(1));
1675
		}
1676
1677
		$out = '';
1678
		$outarray = array();
1679
1680
		// Forge request to select users
1681
		$sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1682
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity)
1683
		{
1684
			$sql .= ", e.label";
1685
		}
1686
		$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
1687
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity)
1688
		{
1689
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=u.entity";
1690
			if ($force_entity) $sql .= " WHERE u.entity IN (0,".$force_entity.")";
1691
			else $sql .= " WHERE u.entity IS NOT NULL";
1692
		} else {
1693
			if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE))
1694
			{
1695
				$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug";
1696
				$sql .= " ON ug.fk_user = u.rowid";
1697
				$sql .= " WHERE ug.entity = ".$conf->entity;
1698
			} else {
1699
				$sql .= " WHERE u.entity IN (0,".$conf->entity.")";
1700
			}
1701
		}
1702
		if (!empty($user->socid)) $sql .= " AND u.fk_soc = ".$user->socid;
1703
		if (is_array($exclude) && $excludeUsers) $sql .= " AND u.rowid NOT IN (".$excludeUsers.")";
1704
		if ($includeUsers) $sql .= " AND u.rowid IN (".$includeUsers.")";
1705
		if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) $sql .= " AND u.statut <> 0";
1706
		if (!empty($morefilter)) $sql .= " ".$morefilter;
1707
1708
		//Add hook to filter on user (for exemple on usergroup define in custom modules)
1709
		$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...
1710
		if (!empty($reshook)) $sql .= $hookmanager->resPrint;
1711
1712
		if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION))	// MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
1713
		{
1714
			$sql .= " ORDER BY u.firstname ASC";
1715
		} else {
1716
			$sql .= " ORDER BY u.lastname ASC";
1717
		}
1718
1719
		dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
1720
		$resql = $this->db->query($sql);
1721
		if ($resql)
1722
		{
1723
			$num = $this->db->num_rows($resql);
1724
			$i = 0;
1725
			if ($num)
1726
			{
1727
				// Enhance with select2
1728
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1729
				$out .= ajax_combobox($htmlname);
1730
1731
				// do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
1732
				$out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
1733
				if ($show_empty && !$multiple) $out .= '<option value="-1"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>&nbsp;</option>'."\n";
1734
				if ($show_every) $out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
1735
1736
				$userstatic = new User($this->db);
1737
1738
				while ($i < $num)
1739
				{
1740
					$obj = $this->db->fetch_object($resql);
1741
1742
					$userstatic->id = $obj->rowid;
1743
					$userstatic->lastname = $obj->lastname;
1744
					$userstatic->firstname = $obj->firstname;
1745
					$userstatic->photo = $obj->photo;
1746
					$userstatic->statut = $obj->status;
1747
					$userstatic->entity = $obj->entity;
1748
					$userstatic->admin = $obj->admin;
1749
1750
					$disableline = '';
1751
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) $disableline = ($enableonlytext ? $enableonlytext : '1');
1752
1753
					$labeltoshow = '';
1754
1755
					// $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
1756
					$fullNameMode = 0;
1757
					if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION))
1758
					{
1759
						$fullNameMode = 1; //Firstname+lastname
1760
					}
1761
					$labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
1762
					if (empty($obj->firstname) && empty($obj->lastname)) $labeltoshow .= $obj->login;
1763
1764
					// Complete name with more info
1765
					$moreinfo = '';
1766
					if (!empty($conf->global->MAIN_SHOW_LOGIN))
1767
					{
1768
						$moreinfo .= ($moreinfo ? ' - ' : ' (').$obj->login;
1769
					}
1770
					if ($showstatus >= 0)
1771
					{
1772
						if ($obj->status == 1 && $showstatus == 1)
1773
						{
1774
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
1775
						}
1776
						if ($obj->status == 0 && $showstatus == 1)
1777
						{
1778
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
1779
						}
1780
					}
1781
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity)
1782
					{
1783
						if (!$obj->entity)
1784
						{
1785
							$moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
1786
						} else {
1787
							$moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
1788
						}
1789
					}
1790
					$moreinfo .= ($moreinfo ? ')' : '');
1791
					if ($disableline && $disableline != '1')
1792
					{
1793
						$moreinfo .= ' - '.$disableline; // This is text from $enableonlytext parameter
1794
					}
1795
					$labeltoshow .= $moreinfo;
1796
1797
					$out .= '<option value="'.$obj->rowid.'"';
1798
					if ($disableline) $out .= ' disabled';
1799
					if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
1800
						$out .= ' selected';
1801
					}
1802
					$out .= ' data-html="';
1803
					$outhtml = '';
1804
					// if (!empty($obj->photo)) {
1805
					$outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
1806
					// }
1807
					if ($showstatus >= 0 && $obj->status == 0) $outhtml .= '<strike class="opacitymediumxxx">';
1808
					$outhtml .= $labeltoshow;
1809
					if ($showstatus >= 0 && $obj->status == 0) $outhtml .= '</strike>';
1810
					$out .= dol_escape_htmltag($outhtml);
1811
					$out .= '">';
1812
					$out .= $labeltoshow;
1813
					$out .= '</option>';
1814
1815
					$outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
1816
1817
					$i++;
1818
				}
1819
			} else {
1820
				$out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
1821
				$out .= '<option value="">'.$langs->trans("None").'</option>';
1822
			}
1823
			$out .= '</select>';
1824
		} else {
1825
			dol_print_error($this->db);
1826
		}
1827
1828
		if ($outputmode) return $outarray;
1829
		return $out;
1830
	}
1831
1832
1833
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1834
	/**
1835
	 *	Return select list of users. Selected users are stored into session.
1836
	 *  List of users are provided into $_SESSION['assignedtouser'].
1837
	 *
1838
	 *  @param  string	$action         Value for $action
1839
	 *  @param  string	$htmlname       Field name in form
1840
	 *  @param  int		$show_empty     0=list without the empty value, 1=add empty value
1841
	 *  @param  array	$exclude        Array list of users id to exclude
1842
	 * 	@param	int		$disabled		If select list must be disabled
1843
	 *  @param  array	$include        Array list of users id to include or 'hierarchy' to have only supervised users
1844
	 * 	@param	array	$enableonly		Array list of users id to be enabled. All other must be disabled
1845
	 *  @param	int		$force_entity	'0' or Ids of environment to force
1846
	 *  @param	int		$maxlength		Maximum length of string into list (0=no limit)
1847
	 *  @param	int		$showstatus		0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
1848
	 *  @param	string	$morefilter		Add more filters into sql request
1849
	 *  @param	int		$showproperties		Show properties of each attendees
1850
	 *  @param	array	$listofuserid		Array with properties of each user
1851
	 *  @param	array	$listofcontactid	Array with properties of each contact
1852
	 *  @param	array	$listofotherid		Array with properties of each other contact
1853
	 * 	@return	string					HTML select string
1854
	 *  @see select_dolgroups()
1855
	 */
1856
	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())
1857
	{
1858
		// phpcs:enable
1859
		global $conf, $user, $langs;
1860
1861
		$userstatic = new User($this->db);
1862
		$out = '';
1863
1864
1865
		$assignedtouser = array();
1866
		if (!empty($_SESSION['assignedtouser']))
1867
		{
1868
			$assignedtouser = json_decode($_SESSION['assignedtouser'], true);
1869
		}
1870
		$nbassignetouser = count($assignedtouser);
1871
1872
		//if ($nbassignetouser && $action != 'view') $out .= '<br>';
1873
		if ($nbassignetouser) $out .= '<ul class="attendees">';
1874
		$i = 0; $ownerid = 0;
1875
		foreach ($assignedtouser as $key => $value)
1876
		{
1877
			if ($value['id'] == $ownerid) continue;
1878
1879
			$out .= '<li>';
1880
			$userstatic->fetch($value['id']);
1881
			$out .= $userstatic->getNomUrl(-1);
1882
			if ($i == 0) { $ownerid = $value['id']; $out .= ' ('.$langs->trans("Owner").')'; }
1883
			if ($nbassignetouser > 1 && $action != 'view')
1884
			{
1885
				$out .= ' <input type="image" style="border: 0px;" src="'.img_picto($langs->trans("Remove"), 'delete', '', 0, 1).'" value="'.$userstatic->id.'" class="removedassigned" id="removedassigned_'.$userstatic->id.'" name="removedassigned_'.$userstatic->id.'">';
1886
			}
1887
			// Show my availability
1888
			if ($showproperties)
1889
			{
1890
				if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid)))
1891
				{
1892
					$out .= '<div class="myavailability inline-block">';
1893
					$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>';
1894
					$out .= '</div>';
1895
				}
1896
			}
1897
			//$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
1898
			//$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
1899
1900
			$out .= '</li>';
1901
			$i++;
1902
		}
1903
		if ($nbassignetouser) $out .= '</ul>';
1904
1905
		// Method with no ajax
1906
		if ($action != 'view')
1907
		{
1908
			$out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
1909
			$out .= '<script type="text/javascript" language="javascript">jQuery(document).ready(function () {';
1910
			$out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
1911
			$out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
1912
			$out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
1913
			$out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
1914
			$out .= '});';
1915
			$out .= '})</script>';
1916
			$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
1917
			$out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
1918
			$out .= '<br>';
1919
		}
1920
1921
		return $out;
1922
	}
1923
1924
1925
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1926
	/**
1927
	 *  Return list of products for customer in Ajax if Ajax activated or go to select_produits_list
1928
	 *
1929
	 *  @param		int			$selected				Preselected products
1930
	 *  @param		string		$htmlname				Name of HTML select field (must be unique in page).
1931
	 *  @param		int			$filtertype				Filter on product type (''=nofilter, 0=product, 1=service)
1932
	 *  @param		int			$limit					Limit on number of returned lines
1933
	 *  @param		int			$price_level			Level of price to show
1934
	 *  @param		int			$status					Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell
1935
	 *  @param		int			$finished				2=all, 1=finished, 0=raw material
1936
	 *  @param		string		$selected_input_value	Value of preselected input text (for use with ajax)
1937
	 *  @param		int			$hidelabel				Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1938
	 *  @param		array		$ajaxoptions			Options for ajax_autocompleter
1939
	 *  @param      int			$socid					Thirdparty Id (to get also price dedicated to this customer)
1940
	 *  @param		string		$showempty				'' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
1941
	 * 	@param		int			$forcecombo				Force to use combo box
1942
	 *  @param      string      $morecss                Add more css on select
1943
	 *  @param      int         $hidepriceinlabel       1=Hide prices in label
1944
	 *  @param      string      $warehouseStatus        Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used
1945
	 *										            'warehouseopen' = count products from open warehouses,
1946
	 *										            'warehouseclosed' = count products from closed warehouses,
1947
	 *										            'warehouseinternal' = count products from warehouses for internal correct/transfer only
1948
	 *  @param 		array 		$selected_combinations 	Selected combinations. Format: array([attrid] => attrval, [...])
1949
	 *  @param		string		$nooutput				No print, return the output into a string
1950
	 *  @return		void|string
1951
	 */
1952
	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)
1953
	{
1954
		// phpcs:enable
1955
		global $langs, $conf;
1956
1957
		$out = '';
1958
1959
		// check parameters
1960
		$price_level = (!empty($price_level) ? $price_level : 0);
1961
		if (is_null($ajaxoptions)) $ajaxoptions = array();
0 ignored issues
show
introduced by
The condition is_null($ajaxoptions) is always false.
Loading history...
1962
1963
		if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) {
1964
			if (!empty($conf->product->enabled) && empty($conf->service->enabled)) {
1965
				$filtertype = '0';
1966
			} elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) {
1967
				$filtertype = '1';
1968
			}
1969
		}
1970
1971
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT))
1972
		{
1973
			$placeholder = '';
1974
1975
			if ($selected && empty($selected_input_value))
1976
			{
1977
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1978
				$producttmpselect = new Product($this->db);
1979
				$producttmpselect->fetch($selected);
1980
				$selected_input_value = $producttmpselect->ref;
1981
				unset($producttmpselect);
1982
			}
1983
			// handle case where product or service module is disabled + no filter specified
1984
			if ($filtertype == '')
1985
			{
1986
				if (empty($conf->product->enabled)) { // when product module is disabled, show services only
1987
					$filtertype = 1;
1988
				} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
1989
					$filtertype = 0;
1990
				}
1991
			}
1992
			// mode=1 means customers products
1993
			$urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
1994
			//Price by customer
1995
			if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
1996
				$urloption .= '&socid='.$socid;
1997
			}
1998
			$out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
1999
2000
			if (!empty($conf->variants->enabled) && is_array($selected_combinations)) {
2001
				// Code to automatically insert with javascript the select of attributes under the select of product
2002
				// when a parent of variant has been selected.
2003
				$out .= '
2004
				<!-- script to auto show attributes select tags if a variant was selected -->
2005
				<script>
2006
					// auto show attributes fields
2007
					selected = '.json_encode($selected_combinations).';
2008
					combvalues = {};
2009
2010
					jQuery(document).ready(function () {
2011
2012
						jQuery("input[name=\'prod_entry_mode\']").change(function () {
2013
							if (jQuery(this).val() == \'free\') {
2014
								jQuery(\'div#attributes_box\').empty();
2015
							}
2016
						});
2017
2018
						jQuery("input#'.$htmlname.'").change(function () {
2019
2020
							if (!jQuery(this).val()) {
2021
								jQuery(\'div#attributes_box\').empty();
2022
								return;
2023
							}
2024
2025
							console.log("A change has started. We get variants fields to inject html select");
2026
2027
							jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2028
								id: jQuery(this).val()
2029
							}, function (data) {
2030
								jQuery(\'div#attributes_box\').empty();
2031
2032
								jQuery.each(data, function (key, val) {
2033
2034
									combvalues[val.id] = val.values;
2035
2036
									var span = jQuery(document.createElement(\'div\')).css({
2037
										\'display\': \'table-row\'
2038
									});
2039
2040
									span.append(
2041
										jQuery(document.createElement(\'div\')).text(val.label).css({
2042
											\'font-weight\': \'bold\',
2043
											\'display\': \'table-cell\'
2044
										})
2045
									);
2046
2047
									var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2048
										\'margin-left\': \'15px\',
2049
										\'white-space\': \'pre\'
2050
									}).append(
2051
										jQuery(document.createElement(\'option\')).val(\'\')
2052
									);
2053
2054
									jQuery.each(combvalues[val.id], function (key, val) {
2055
										var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2056
2057
										if (selected[val.fk_product_attribute] == val.id) {
2058
											tag.attr(\'selected\', \'selected\');
2059
										}
2060
2061
										html.append(tag);
2062
									});
2063
2064
									span.append(html);
2065
									jQuery(\'div#attributes_box\').append(span);
2066
								});
2067
							})
2068
						});
2069
2070
						'.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2071
					});
2072
				</script>
2073
                ';
2074
			}
2075
2076
			if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : ';
2077
			elseif ($hidelabel > 1) {
2078
				$placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2079
				if ($hidelabel == 2) {
2080
					$out .= img_picto($langs->trans("Search"), 'search');
2081
				}
2082
			}
2083
			$out .= '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
2084
			if ($hidelabel == 3) {
2085
				$out .= img_picto($langs->trans("Search"), 'search');
2086
			}
2087
		} else {
2088
			$out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus);
2089
		}
2090
2091
		if (empty($nooutput)) print $out;
2092
		else return $out;
2093
	}
2094
2095
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2096
	/**
2097
	 *	Return list of products for a customer.
2098
	 *  Called by select_produits.
2099
	 *
2100
	 *	@param      int		$selected           Preselected product
2101
	 *	@param      string	$htmlname           Name of select html
2102
	 *  @param		string	$filtertype         Filter on product type (''=nofilter, 0=product, 1=service)
2103
	 *	@param      int		$limit              Limit on number of returned lines
2104
	 *	@param      int		$price_level        Level of price to show
2105
	 * 	@param      string	$filterkey          Filter on product
2106
	 *	@param		int		$status             -1=Return all products, 0=Products not on sell, 1=Products on sell
2107
	 *  @param      int		$finished           Filter on finished field: 2=No filter
2108
	 *  @param      int		$outputmode         0=HTML select string, 1=Array
2109
	 *  @param      int		$socid     		    Thirdparty Id (to get also price dedicated to this customer)
2110
	 *  @param		string	$showempty		    '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2111
	 * 	@param		int		$forcecombo		    Force to use combo box
2112
	 *  @param      string  $morecss            Add more css on select
2113
	 *  @param      int     $hidepriceinlabel   1=Hide prices in label
2114
	 *  @param      string  $warehouseStatus    Warehouse status filter to group/count stock. Following comma separated filter options can be used.
2115
	 *										    'warehouseopen' = count products from open warehouses,
2116
	 *										    'warehouseclosed' = count products from closed warehouses,
2117
	 *										    'warehouseinternal' = count products from warehouses for internal correct/transfer only
2118
	 *  @return     array    				    Array of keys for json
2119
	 */
2120
	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 = '')
2121
	{
2122
		// phpcs:enable
2123
		global $langs, $conf, $user, $db;
2124
2125
		$out = '';
2126
		$outarray = array();
2127
2128
		// Units
2129
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2130
			$langs->load('other');
2131
		}
2132
2133
		$warehouseStatusArray = array();
2134
		if (!empty($warehouseStatus))
2135
		{
2136
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2137
			if (preg_match('/warehouseclosed/', $warehouseStatus))
2138
			{
2139
				$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2140
			}
2141
			if (preg_match('/warehouseopen/', $warehouseStatus))
2142
			{
2143
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2144
			}
2145
			if (preg_match('/warehouseinternal/', $warehouseStatus))
2146
			{
2147
				$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2148
			}
2149
		}
2150
2151
		$selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression";
2152
		if (count($warehouseStatusArray))
2153
		{
2154
			$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
2155
		} else {
2156
			$selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2157
		}
2158
2159
		$sql = "SELECT ";
2160
		$sql .= $selectFields.$selectFieldsGrouped;
2161
2162
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY))
2163
		{
2164
			//Product category
2165
			$sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie
2166
						FROM ".MAIN_DB_PREFIX."categorie_product
2167
						WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid
2168
						LIMIT 1
2169
				) AS categorie_product_id ";
2170
		}
2171
2172
		//Price by customer
2173
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid))
2174
		{
2175
			$sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2176
			$sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.ref_customer as custref';
2177
			$selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custref";
2178
		}
2179
		// Units
2180
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2181
			$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";
2182
			$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';
2183
		}
2184
2185
		// Multilang : we add translation
2186
		if (!empty($conf->global->MAIN_MULTILANGS))
2187
		{
2188
			$sql .= ", pl.label as label_translated";
2189
			$sql .= ", pl.description as description_translated";
2190
			$selectFields .= ", label_translated";
2191
			$selectFields .= ", description_translated";
2192
		}
2193
		// Price by quantity
2194
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))
2195
		{
2196
			$sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid";
2197
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level;
2198
			$sql .= " ORDER BY date_price";
2199
			$sql .= " DESC LIMIT 1) as price_rowid";
2200
			$sql .= ", (SELECT pp.price_by_qty FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable
2201
			if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level;
2202
			$sql .= " ORDER BY date_price";
2203
			$sql .= " DESC LIMIT 1) as price_by_qty";
2204
			$selectFields .= ", price_rowid, price_by_qty";
2205
		}
2206
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
2207
		if (count($warehouseStatusArray))
2208
		{
2209
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid";
2210
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2211
			$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.
2212
		}
2213
2214
		// include search in supplier ref
2215
		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF))
2216
		{
2217
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2218
		}
2219
2220
		//Price by customer
2221
		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2222
			$sql .= " LEFT JOIN  ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid";
2223
		}
2224
		// Units
2225
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2226
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
2227
		}
2228
		// Multilang : we add translation
2229
		if (!empty($conf->global->MAIN_MULTILANGS))
2230
		{
2231
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid ";
2232
			if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2233
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2234
				$soc = new Societe($db);
2235
				$result = $soc->fetch($socid);
2236
				if ($result > 0 && !empty($soc->default_lang)) {
2237
					$sql .= " AND pl.lang='" . $this->db->escape($soc->default_lang) . "'";
2238
				} else {
2239
					$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2240
				}
2241
			} else {
2242
				$sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'";
2243
			}
2244
		}
2245
2246
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2247
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2248
		}
2249
2250
		$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2251
2252
		if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2253
			$sql .= " AND pac.rowid IS NULL";
2254
		}
2255
2256
		if ($finished == 0)
2257
		{
2258
			$sql .= " AND p.finished = ".$finished;
2259
		} elseif ($finished == 1)
2260
		{
2261
			$sql .= " AND p.finished = ".$finished;
2262
			if ($status >= 0)  $sql .= " AND p.tosell = ".$status;
2263
		} elseif ($status >= 0)
2264
		{
2265
			$sql .= " AND p.tosell = ".$status;
2266
		}
2267
		// Filter by product type
2268
		if (strval($filtertype) != '') $sql .= " AND p.fk_product_type = ".$filtertype;
2269
		elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only
2270
			$sql .= " AND p.fk_product_type = 1";
2271
		} elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2272
			$sql .= " AND p.fk_product_type = 0";
2273
		}
2274
		// Add criteria on ref/label
2275
		if ($filterkey != '')
2276
		{
2277
			$sql .= ' AND (';
2278
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2279
			// For natural search
2280
			$scrit = explode(' ', $filterkey);
2281
			$i = 0;
2282
			if (count($scrit) > 1) $sql .= "(";
2283
			foreach ($scrit as $crit)
2284
			{
2285
				if ($i > 0) $sql .= " AND ";
2286
				$sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2287
				if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2288
				if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) $sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2289
				if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION))
2290
				{
2291
					$sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2292
					if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2293
				}
2294
				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) $sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2295
				$sql .= ")";
2296
				$i++;
2297
			}
2298
			if (count($scrit) > 1) $sql .= ")";
2299
			if (!empty($conf->barcode->enabled)) $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2300
			$sql .= ')';
2301
		}
2302
		if (count($warehouseStatusArray))
2303
		{
2304
			$sql .= ' GROUP BY'.$selectFields;
2305
		}
2306
2307
		//Sort by category
2308
		if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY))
2309
		{
2310
			$sql .= " ORDER BY categorie_product_id ";
2311
			//ASC OR DESC order
2312
			($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2313
		} else {
2314
			$sql .= $this->db->order("p.ref");
2315
		}
2316
2317
		$sql .= $this->db->plimit($limit, 0);
2318
2319
		// Build output string
2320
		dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2321
		$result = $this->db->query($sql);
2322
		if ($result)
2323
		{
2324
			require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2325
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2326
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2327
2328
			$num = $this->db->num_rows($result);
2329
2330
			$events = null;
2331
2332
			if (!$forcecombo)
2333
			{
2334
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2335
				$out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT);
2336
			}
2337
2338
			$out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2339
2340
			$textifempty = '';
2341
			// Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2342
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2343
			if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT))
2344
			{
2345
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
2346
				else $textifempty .= $langs->trans("All");
2347
			} else {
2348
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
2349
			}
2350
			if ($showempty) $out .= '<option value="0" selected>'.$textifempty.'</option>';
2351
2352
			$i = 0;
2353
			while ($num && $i < $num)
2354
			{
2355
				$opt = '';
2356
				$optJson = array();
2357
				$objp = $this->db->fetch_object($result);
2358
2359
				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)
2360
				{ // Price by quantity will return many prices for the same product
2361
					$sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2362
					$sql .= " FROM ".MAIN_DB_PREFIX."product_price_by_qty";
2363
					$sql .= " WHERE fk_product_price=".$objp->price_rowid;
2364
					$sql .= " ORDER BY quantity ASC";
2365
2366
					dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2367
					$result2 = $this->db->query($sql);
2368
					if ($result2)
2369
					{
2370
						$nb_prices = $this->db->num_rows($result2);
2371
						$j = 0;
2372
						while ($nb_prices && $j < $nb_prices) {
2373
							$objp2 = $this->db->fetch_object($result2);
2374
2375
							$objp->price_by_qty_rowid = $objp2->rowid;
2376
							$objp->price_by_qty_price_base_type = $objp2->price_base_type;
2377
							$objp->price_by_qty_quantity = $objp2->quantity;
2378
							$objp->price_by_qty_unitprice = $objp2->unitprice;
2379
							$objp->price_by_qty_remise_percent = $objp2->remise_percent;
2380
							// For backward compatibility
2381
							$objp->quantity = $objp2->quantity;
2382
							$objp->price = $objp2->price;
2383
							$objp->unitprice = $objp2->unitprice;
2384
							$objp->remise_percent = $objp2->remise_percent;
2385
							$objp->remise = $objp2->remise;
2386
2387
							$this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2388
2389
							$j++;
2390
2391
							// Add new entry
2392
							// "key" value of json key array is used by jQuery automatically as selected value
2393
							// "label" value of json key array is used by jQuery automatically as text for combo box
2394
							$out .= $opt;
2395
							array_push($outarray, $optJson);
2396
						}
2397
					}
2398
				} else {
2399
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
2400
						$price_product = new Product($this->db);
2401
						$price_product->fetch($objp->rowid, '', '', 1);
2402
						$priceparser = new PriceParser($this->db);
2403
						$price_result = $priceparser->parseProduct($price_product);
2404
						if ($price_result >= 0) {
2405
							$objp->price = $price_result;
2406
							$objp->unitprice = $price_result;
2407
							//Calculate the VAT
2408
							$objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2409
							$objp->price_ttc = price2num($objp->price_ttc, 'MU');
2410
						}
2411
					}
2412
2413
					$this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2414
					// Add new entry
2415
					// "key" value of json key array is used by jQuery automatically as selected value
2416
					// "label" value of json key array is used by jQuery automatically as text for combo box
2417
					$out .= $opt;
2418
					array_push($outarray, $optJson);
2419
				}
2420
2421
				$i++;
2422
			}
2423
2424
			$out .= '</select>';
2425
2426
			$this->db->free($result);
2427
2428
			if (empty($outputmode)) return $out;
2429
			return $outarray;
2430
		} else {
2431
			dol_print_error($db);
2432
		}
2433
	}
2434
2435
	/**
2436
	 * constructProductListOption.
2437
	 * This define value for &$opt and &$optJson.
2438
	 *
2439
	 * @param 	resource	$objp			    Resultset of fetch
2440
	 * @param 	string		$opt			    Option (var used for returned value in string option format)
2441
	 * @param 	string		$optJson		    Option (var used for returned value in json format)
2442
	 * @param 	int			$price_level	    Price level
2443
	 * @param 	string		$selected		    Preselected value
2444
	 * @param   int         $hidepriceinlabel   Hide price in label
2445
	 * @param   string      $filterkey          Filter key to highlight
2446
	 * @param	int			$novirtualstock 	Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on.
2447
	 * @return	void
2448
	 */
2449
	protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2450
	{
2451
		global $langs, $conf, $user, $db;
2452
2453
		$outkey = '';
2454
		$outval = '';
2455
		$outref = '';
2456
		$outlabel = '';
2457
		$outlabel_translated = '';
2458
		$outdesc = '';
2459
		$outdesc_translated = '';
2460
		$outbarcode = '';
2461
		$outorigin = '';
2462
		$outtype = '';
2463
		$outprice_ht = '';
2464
		$outprice_ttc = '';
2465
		$outpricebasetype = '';
2466
		$outtva_tx = '';
2467
		$outqty = 1;
2468
		$outdiscount = 0;
2469
2470
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2471
2472
		$label = $objp->label;
2473
		if (!empty($objp->label_translated)) $label = $objp->label_translated;
2474
		if (!empty($filterkey) && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2475
2476
		$outkey = $objp->rowid;
2477
		$outref = $objp->ref;
2478
		$outrefcust = empty($objp->custref) ? '' : $objp->custref;
2479
		$outlabel = $objp->label;
2480
		$outdesc = $objp->description;
2481
		if (!empty($conf->global->MAIN_MULTILANGS))
2482
		{
2483
			$outlabel_translated = $objp->label_translated;
2484
			$outdesc_translated = $objp->description_translated;
2485
		}
2486
		$outbarcode = $objp->barcode;
2487
		$outorigin = $objp->fk_country;
2488
		$outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2489
2490
		$outtype = $objp->fk_product_type;
2491
		$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2492
		$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2493
2494
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO))  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2495
2496
		// Units
2497
		$outvalUnits = '';
2498
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2499
			if (!empty($objp->unit_short)) {
2500
				$outvalUnits .= ' - '.$objp->unit_short;
2501
			}
2502
		}
2503
		if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2504
			if (!empty($objp->weight) && $objp->weight_units !== null) {
2505
				$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2506
				$outvalUnits .= ' - '.$unitToShow;
2507
			}
2508
			if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2509
				$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2510
				$outvalUnits .= ' - '.$unitToShow;
2511
			}
2512
			if (!empty($objp->surface) && $objp->surface_units !== null) {
2513
				$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2514
				$outvalUnits .= ' - '.$unitToShow;
2515
			}
2516
			if (!empty($objp->volume) && $objp->volume_units !== null) {
2517
				$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2518
				$outvalUnits .= ' - '.$unitToShow;
2519
			}
2520
		}
2521
		if ($outdurationvalue && $outdurationunit) {
2522
			$da = array(
2523
				'h' => $langs->trans('Hour'),
2524
				'd' => $langs->trans('Day'),
2525
				'w' => $langs->trans('Week'),
2526
				'm' => $langs->trans('Month'),
2527
				'y' => $langs->trans('Year')
2528
			);
2529
			if (isset($da[$outdurationunit])) {
2530
				$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2531
			}
2532
		}
2533
2534
		$opt = '<option value="'.$objp->rowid.'"';
2535
		$opt .= ($objp->rowid == $selected) ? ' selected' : '';
2536
		if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0)
2537
		{
2538
			$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.'"';
2539
		}
2540
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)))
2541
		{
2542
			if (!empty($user->rights->stock->lire)) {
2543
				if ($objp->stock > 0) $opt .= ' class="product_line_stock_ok"';
2544
		   		elseif ($objp->stock <= 0) $opt .= ' class="product_line_stock_too_low"';
2545
			}
2546
		}
2547
		if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2548
			$opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2549
			$opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2550
		}
2551
		$opt .= '>';
2552
		$opt .= $objp->ref;
2553
		if (! empty($objp->custref)) $opt.= ' (' . $objp->custref . ')';
2554
		if ($outbarcode) $opt .= ' ('.$outbarcode.')';
2555
		$opt .= ' - '.dol_trunc($label, $maxlengtharticle);
2556
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) $opt .= ' ('.getCountry($outorigin, 1).')';
2557
2558
		$objRef = $objp->ref;
2559
		if (! empty($objp->custref)) $objRef .= ' (' . $objp->custref . ')';
2560
		if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2561
		$outval .= $objRef;
2562
		if ($outbarcode) $outval .= ' ('.$outbarcode.')';
2563
		$outval .= ' - '.dol_trunc($label, $maxlengtharticle);
2564
		if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) $outval .= ' ('.getCountry($outorigin, 1).')';
2565
2566
		// Units
2567
		$opt .= $outvalUnits;
2568
		$outval .= $outvalUnits;
2569
2570
		$found = 0;
2571
2572
		// Multiprice
2573
		// If we need a particular price level (from 1 to 6)
2574
		if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)))
2575
		{
2576
			$sql = "SELECT price, price_ttc, price_base_type, tva_tx";
2577
			$sql .= " FROM ".MAIN_DB_PREFIX."product_price";
2578
			$sql .= " WHERE fk_product = ".((int) $objp->rowid);
2579
			$sql .= " AND entity IN (".getEntity('productprice').")";
2580
			$sql .= " AND price_level = ".((int) $price_level);
2581
			$sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
2582
			$sql .= " LIMIT 1";
2583
2584
			dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
2585
			$result2 = $this->db->query($sql);
2586
			if ($result2)
2587
			{
2588
				$objp2 = $this->db->fetch_object($result2);
2589
				if ($objp2)
2590
				{
2591
					$found = 1;
2592
					if ($objp2->price_base_type == 'HT')
2593
					{
2594
						$opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2595
						$outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2596
					} else {
2597
						$opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2598
						$outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2599
					}
2600
					$outprice_ht = price($objp2->price);
2601
					$outprice_ttc = price($objp2->price_ttc);
2602
					$outpricebasetype = $objp2->price_base_type;
2603
					$outtva_tx = $objp2->tva_tx;
2604
				}
2605
			} else {
2606
				dol_print_error($this->db);
2607
			}
2608
		}
2609
2610
		// Price by quantity
2611
		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)))
2612
		{
2613
			$found = 1;
2614
			$outqty = $objp->quantity;
2615
			$outdiscount = $objp->remise_percent;
2616
			if ($objp->quantity == 1)
2617
			{
2618
				$opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
2619
				$outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
2620
				$opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
2621
				$outval .= $langs->transnoentities("Unit");
2622
			} else {
2623
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2624
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
2625
				$opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
2626
				$outval .= $langs->transnoentities("Units");
2627
			}
2628
2629
			$outprice_ht = price($objp->unitprice);
2630
			$outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
2631
			$outpricebasetype = $objp->price_base_type;
2632
			$outtva_tx = $objp->tva_tx;
2633
		}
2634
		if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1)
2635
		{
2636
			$opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2637
			$outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
2638
		}
2639
		if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1)
2640
		{
2641
			$opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
2642
			$outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
2643
		}
2644
2645
		// Price by customer
2646
		if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES))
2647
		{
2648
			if (!empty($objp->idprodcustprice))
2649
			{
2650
				$found = 1;
2651
2652
				if ($objp->custprice_base_type == 'HT')
2653
				{
2654
					$opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2655
					$outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2656
				} else {
2657
					$opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2658
					$outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2659
				}
2660
2661
				$outprice_ht = price($objp->custprice);
2662
				$outprice_ttc = price($objp->custprice_ttc);
2663
				$outpricebasetype = $objp->custprice_base_type;
2664
				$outtva_tx = $objp->custtva_tx;
2665
			}
2666
		}
2667
2668
		// If level no defined or multiprice not found, we used the default price
2669
		if (empty($hidepriceinlabel) && !$found)
2670
		{
2671
			if ($objp->price_base_type == 'HT')
2672
			{
2673
				$opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
2674
				$outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
2675
			} else {
2676
				$opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
2677
				$outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
2678
			}
2679
			$outprice_ht = price($objp->price);
2680
			$outprice_ttc = price($objp->price_ttc);
2681
			$outpricebasetype = $objp->price_base_type;
2682
			$outtva_tx = $objp->tva_tx;
2683
		}
2684
2685
		if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)))
2686
		{
2687
			if (!empty($user->rights->stock->lire)) {
2688
				$opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
2689
2690
				if ($objp->stock > 0) {
2691
					$outval .= ' - <span class="product_line_stock_ok">';
2692
				} elseif ($objp->stock <= 0) {
2693
					$outval .= ' - <span class="product_line_stock_too_low">';
2694
				}
2695
				$outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
2696
				$outval .= '</span>';
2697
				if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO))  // Warning, this option may slow down combo list generation
2698
				{
2699
					$langs->load("stocks");
2700
2701
					$tmpproduct = new Product($this->db);
2702
					$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
2703
					$tmpproduct->load_virtual_stock();
2704
					$virtualstock = $tmpproduct->stock_theorique;
2705
2706
					$opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
2707
2708
					$outval .= ' - '.$langs->transnoentities("VirtualStock").':';
2709
					if ($virtualstock > 0) {
2710
						$outval .= '<span class="product_line_stock_ok">';
2711
					} elseif ($virtualstock <= 0) {
2712
						$outval .= '<span class="product_line_stock_too_low">';
2713
					}
2714
					$outval .= $virtualstock;
2715
					$outval .= '</span>';
2716
2717
					unset($tmpproduct);
2718
				}
2719
			}
2720
		}
2721
2722
		$opt .= "</option>\n";
2723
		$optJson = array(
2724
			'key'=>$outkey,
2725
			'value'=>$outref,
2726
			'label'=>$outval,
2727
			'label2'=>$outlabel,
2728
			'desc'=>$outdesc,
2729
			'type'=>$outtype,
2730
			'price_ht'=>price2num($outprice_ht),
2731
			'price_ttc'=>price2num($outprice_ttc),
2732
			'pricebasetype'=>$outpricebasetype,
2733
			'tva_tx'=>$outtva_tx, 'qty'=>$outqty,
2734
			'discount'=>$outdiscount,
2735
			'duration_value'=>$outdurationvalue,
2736
			'duration_unit'=>$outdurationunit,
2737
			'pbq'=>$outpbq,
2738
			'labeltrans'=>$outlabel_translated,
2739
			'desctrans'=>$outdesc_translated,
2740
			'ref_customer'=>$outrefcust
2741
		);
2742
	}
2743
2744
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2745
	/**
2746
	 *	Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list)
2747
	 *
2748
	 *	@param	int		$socid			Id third party
2749
	 *	@param  string	$selected       Preselected product
2750
	 *	@param  string	$htmlname       Name of HTML Select
2751
	 *  @param	string	$filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
2752
	 *	@param  string	$filtre			For a SQL filter
2753
	 *	@param	array	$ajaxoptions	Options for ajax_autocompleter
2754
	 *  @param	int		$hidelabel		Hide label (0=no, 1=yes)
2755
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
2756
	 *  @param	string	$morecss		More CSS
2757
	 *  @param	string	$placeholder	Placeholder
2758
	 *	@return	void
2759
	 */
2760
	public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
2761
	{
2762
		// phpcs:enable
2763
		global $langs, $conf;
2764
		global $price_level, $status, $finished;
2765
2766
		$selected_input_value = '';
2767
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT))
2768
		{
2769
			if ($selected > 0)
2770
			{
2771
				require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2772
				$producttmpselect = new Product($this->db);
2773
				$producttmpselect->fetch($selected);
2774
				$selected_input_value = $producttmpselect->ref;
2775
				unset($producttmpselect);
2776
			}
2777
2778
			// mode=2 means suppliers products
2779
			$urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
2780
			print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
2781
			print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
2782
		} else {
2783
			print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', -1, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
2784
		}
2785
	}
2786
2787
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2788
	/**
2789
	 *	Return list of suppliers products
2790
	 *
2791
	 *	@param	int		$socid   		Id societe fournisseur (0 pour aucun filtre)
2792
	 *	@param  int		$selected       Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD')
2793
	 *	@param  string	$htmlname       Nom de la zone select
2794
	 *  @param	string	$filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
2795
	 *	@param  string	$filtre         Pour filtre sql
2796
	 *	@param  string	$filterkey      Filtre des produits
2797
	 *  @param  int		$statut         -1=Return all products, 0=Products not on sell, 1=Products on sell (not used here, a filter on tobuy is already hard coded in request)
2798
	 *  @param  int		$outputmode     0=HTML select string, 1=Array
2799
	 *  @param  int     $limit          Limit of line number
2800
	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
2801
	 *  @param	string	$morecss		Add more CSS
2802
	 *  @param	int		$showstockinlist	Show stock information (slower).
2803
	 *  @param	string	$placeholder	Placeholder
2804
	 *  @return array           		Array of keys for json
2805
	 */
2806
	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 = '')
2807
	{
2808
		// phpcs:enable
2809
		global $langs, $conf, $db, $user;
2810
2811
		$out = '';
2812
		$outarray = array();
2813
2814
		$maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2815
2816
		$langs->load('stocks');
2817
		// Units
2818
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2819
			$langs->load('other');
2820
		}
2821
2822
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock,";
2823
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
2824
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name,";
2825
		$sql .= " pfp.supplier_reputation";
2826
		// if we use supplier description of the products
2827
		if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
2828
			$sql .= " ,pfp.desc_fourn as description";
2829
		} else {
2830
			$sql .= " ,p.description";
2831
		}
2832
		// Units
2833
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2834
			$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";
2835
		}
2836
		if (!empty($conf->barcode->enabled)) $sql .= ", pfp.barcode";
2837
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
2838
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
2839
		if ($socid) $sql .= " AND pfp.fk_soc = ".$socid;
2840
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
2841
		// Units
2842
		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2843
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit";
2844
		}
2845
		$sql .= " WHERE p.entity IN (".getEntity('product').")";
2846
		$sql .= " AND p.tobuy = 1";
2847
		if (strval($filtertype) != '') $sql .= " AND p.fk_product_type=".$this->db->escape($filtertype);
2848
		if (!empty($filtre)) $sql .= " ".$filtre;
2849
		// Add criteria on ref/label
2850
		if ($filterkey != '')
2851
		{
2852
			$sql .= ' AND (';
2853
			$prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2854
			// For natural search
2855
			$scrit = explode(' ', $filterkey);
2856
			$i = 0;
2857
			if (count($scrit) > 1) $sql .= "(";
2858
			foreach ($scrit as $crit)
2859
			{
2860
				if ($i > 0) $sql .= " AND ";
2861
				$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)."%'";
2862
				if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
2863
					$sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2864
				}
2865
				$sql .= ")";
2866
				$i++;
2867
			}
2868
			if (count($scrit) > 1) $sql .= ")";
2869
			if (!empty($conf->barcode->enabled)) {
2870
				$sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2871
				$sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2872
			}
2873
			$sql .= ')';
2874
		}
2875
		$sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
2876
		$sql .= $this->db->plimit($limit, 0);
2877
2878
		// Build output string
2879
2880
		dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
2881
		$result = $this->db->query($sql);
2882
		if ($result)
2883
		{
2884
			require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2885
			require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2886
2887
			$num = $this->db->num_rows($result);
2888
2889
			//$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';	// remove select to have id same with combo and ajax
2890
			$out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
2891
			if (!$selected) $out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
2892
			else $out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
2893
2894
			$i = 0;
2895
			while ($i < $num)
2896
			{
2897
				$objp = $this->db->fetch_object($result);
2898
2899
				$outkey = $objp->idprodfournprice; // id in table of price
2900
				if (!$outkey && $alsoproductwithnosupplierprice) $outkey = 'idprod_'.$objp->rowid; // id of product
2901
2902
				$outref = $objp->ref;
2903
				$outval = '';
2904
				$outbarcode = $objp->barcode;
2905
				$outqty = 1;
2906
				$outdiscount = 0;
2907
				$outtype = $objp->fk_product_type;
2908
				$outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2909
				$outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2910
2911
				// Units
2912
				$outvalUnits = '';
2913
				if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2914
					if (!empty($objp->unit_short)) {
2915
						$outvalUnits .= ' - '.$objp->unit_short;
2916
					}
2917
					if (!empty($objp->weight) && $objp->weight_units !== null) {
2918
						$unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2919
						$outvalUnits .= ' - '.$unitToShow;
2920
					}
2921
					if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2922
						$unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2923
						$outvalUnits .= ' - '.$unitToShow;
2924
					}
2925
					if (!empty($objp->surface) && $objp->surface_units !== null) {
2926
						$unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2927
						$outvalUnits .= ' - '.$unitToShow;
2928
					}
2929
					if (!empty($objp->volume) && $objp->volume_units !== null) {
2930
						$unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2931
						$outvalUnits .= ' - '.$unitToShow;
2932
					}
2933
					if ($outdurationvalue && $outdurationunit) {
2934
						$da = array(
2935
							'h' => $langs->trans('Hour'),
2936
							'd' => $langs->trans('Day'),
2937
							'w' => $langs->trans('Week'),
2938
							'm' => $langs->trans('Month'),
2939
							'y' => $langs->trans('Year')
2940
						);
2941
						if (isset($da[$outdurationunit])) {
2942
							$outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2943
						}
2944
					}
2945
				}
2946
2947
				$objRef = $objp->ref;
2948
				if ($filterkey && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2949
				$objRefFourn = $objp->ref_fourn;
2950
				if ($filterkey && $filterkey != '') $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
2951
				$label = $objp->label;
2952
				if ($filterkey && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2953
2954
				$optlabel = $objp->ref;
2955
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
2956
					$optlabel .= ' <span class=\'opacitymedium\'>('.$objp->ref_fourn.')</span>';
2957
				}
2958
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
2959
					$optlabel .= ' ('.$outbarcode.')';
2960
				}
2961
				$optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
2962
2963
				$outvallabel = $objRef;
2964
				if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
2965
					$outvallabel .= ' ('.$objRefFourn.')';
2966
				}
2967
				if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
2968
					$outvallabel .= ' ('.$outbarcode.')';
2969
				}
2970
				$outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
2971
2972
				// Units
2973
				$optlabel .= $outvalUnits;
2974
				$outvallabel .= $outvalUnits;
2975
2976
				if (!empty($objp->idprodfournprice))
2977
				{
2978
					$outqty = $objp->quantity;
2979
					$outdiscount = $objp->remise_percent;
2980
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
2981
						$prod_supplier = new ProductFournisseur($this->db);
2982
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
2983
						$prod_supplier->id = $objp->fk_product;
2984
						$prod_supplier->fourn_qty = $objp->quantity;
2985
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
2986
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
2987
						$priceparser = new PriceParser($this->db);
2988
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
2989
						if ($price_result >= 0) {
2990
							$objp->fprice = $price_result;
2991
							if ($objp->quantity >= 1)
2992
							{
2993
								$objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
2994
							}
2995
						}
2996
					}
2997
					if ($objp->quantity == 1)
2998
					{
2999
						$optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3000
						$outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3001
						$optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3002
						$outvallabel .= $langs->transnoentities("Unit");
3003
					} else {
3004
						$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;
3005
						$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;
3006
						$optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3007
						$outvallabel .= ' '.$langs->transnoentities("Units");
3008
					}
3009
3010
					if ($objp->quantity > 1)
3011
					{
3012
						$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
3013
						$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
3014
					}
3015
					if ($objp->remise_percent >= 1)
3016
					{
3017
						$optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3018
						$outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3019
					}
3020
					if ($objp->duration)
3021
					{
3022
						$optlabel .= " - ".$objp->duration;
3023
						$outvallabel .= " - ".$objp->duration;
3024
					}
3025
					if (!$socid)
3026
					{
3027
						$optlabel .= " - ".dol_trunc($objp->name, 8);
3028
						$outvallabel .= " - ".dol_trunc($objp->name, 8);
3029
					}
3030
					if ($objp->supplier_reputation)
3031
					{
3032
						//TODO dictionary
3033
						$reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3034
3035
						$optlabel .= " - ".$reputations[$objp->supplier_reputation];
3036
						$outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3037
					}
3038
				} else {
3039
					if (empty($alsoproductwithnosupplierprice))     // No supplier price defined for couple product/supplier
3040
					{
3041
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3042
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3043
					} else // No supplier price defined for product, even on other suppliers
3044
					{
3045
						$optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3046
						$outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3047
					}
3048
				}
3049
3050
				if (!empty($conf->stock->enabled) && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)))
3051
				{
3052
					$novirtualstock = ($showstockinlist == 2);
3053
3054
					if (!empty($user->rights->stock->lire)) {
3055
						$outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3056
3057
						if ($objp->stock > 0) {
3058
							$optlabel .= ' - <span class="product_line_stock_ok">';
3059
						} elseif ($objp->stock <= 0) {
3060
							$optlabel .= ' - <span class="product_line_stock_too_low">';
3061
						}
3062
						$optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3063
						$optlabel .= '</span>';
3064
						if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO))  // Warning, this option may slow down combo list generation
3065
						{
3066
							$langs->load("stocks");
3067
3068
							$tmpproduct = new Product($this->db);
3069
							$tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3070
							$tmpproduct->load_virtual_stock();
3071
							$virtualstock = $tmpproduct->stock_theorique;
3072
3073
							$outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3074
3075
							$optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3076
							if ($virtualstock > 0) {
3077
								$optlabel .= '<span class="product_line_stock_ok">';
3078
							} elseif ($virtualstock <= 0) {
3079
								$optlabel .= '<span class="product_line_stock_too_low">';
3080
							}
3081
							$optlabel .= $virtualstock;
3082
							$optlabel .= '</span>';
3083
3084
							unset($tmpproduct);
3085
						}
3086
					}
3087
				}
3088
3089
				$opt = '<option value="'.$outkey.'"';
3090
				if ($selected && $selected == $objp->idprodfournprice) $opt .= ' selected';
3091
				if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) $opt .= ' disabled';
3092
				if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0)
3093
				{
3094
					$opt .= ' data-qty="'.$objp->quantity.'" data-up="'.$objp->unitprice.'" data-discount="'.$outdiscount.'"';
3095
				}
3096
				$opt .= ' data-description="'.dol_escape_htmltag($objp->description).'"';
3097
				$opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"';
3098
				$opt .= '>';
3099
3100
				$opt .= $optlabel;
3101
				$outval .= $outvallabel;
3102
3103
				$opt .= "</option>\n";
3104
3105
3106
				// Add new entry
3107
				// "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
3108
				// "label" value of json key array is used by jQuery automatically as text for combo box
3109
				$out .= $opt;
3110
				array_push($outarray,
3111
					array('key'=>$outkey,
3112
						'value'=>$outref,
3113
						'label'=>$outval,
3114
						'qty'=>$outqty,
3115
						'price_ht'=>price2num($objp->unitprice, 'MT'),
3116
						'discount'=>$outdiscount,
3117
						'type'=>$outtype,
3118
						'duration_value'=>$outdurationvalue,
3119
						'duration_unit'=>$outdurationunit,
3120
						'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3121
						'description'=>$objp->description
3122
					)
3123
				);
3124
				// Exemple of var_dump $outarray
3125
				// array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3126
				//           ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3127
				//      	 ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3128
				//}
3129
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3130
				//$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3131
				//var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3132
3133
				$i++;
3134
			}
3135
			$out .= '</select>';
3136
3137
			$this->db->free($result);
3138
3139
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3140
			$out .= ajax_combobox($htmlname);
3141
3142
			if (empty($outputmode)) return $out;
3143
			return $outarray;
3144
		} else {
3145
			dol_print_error($this->db);
3146
		}
3147
	}
3148
3149
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3150
	/**
3151
	 *	Return list of suppliers prices for a product
3152
	 *
3153
	 *  @param	    int		$productid       	Id of product
3154
	 *  @param      string	$htmlname        	Name of HTML field
3155
	 *  @param      int		$selected_supplier  Pre-selected supplier if more than 1 result
3156
	 *  @return	    string
3157
	 */
3158
	public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3159
	{
3160
		// phpcs:enable
3161
		global $langs, $conf;
3162
3163
		$langs->load('stocks');
3164
3165
		$sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3166
		$sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3167
		$sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3168
		$sql .= " FROM ".MAIN_DB_PREFIX."product as p";
3169
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3170
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
3171
		$sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3172
		$sql .= " AND p.tobuy = 1";
3173
		$sql .= " AND s.fournisseur = 1";
3174
		$sql .= " AND p.rowid = ".$productid;
3175
		$sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3176
3177
		dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3178
		$result = $this->db->query($sql);
3179
3180
		if ($result)
3181
		{
3182
			$num = $this->db->num_rows($result);
3183
3184
			$form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3185
3186
			if (!$num)
3187
			{
3188
				$form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3189
			} else {
3190
				require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3191
				$form .= '<option value="0">&nbsp;</option>';
3192
3193
				$i = 0;
3194
				while ($i < $num)
3195
				{
3196
					$objp = $this->db->fetch_object($result);
3197
3198
					$opt = '<option value="'.$objp->idprodfournprice.'"';
3199
					//if there is only one supplier, preselect it
3200
					if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier)) {
3201
						$opt .= ' selected';
3202
					}
3203
					$opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3204
3205
					if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3206
						$prod_supplier = new ProductFournisseur($this->db);
3207
						$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3208
						$prod_supplier->id = $productid;
3209
						$prod_supplier->fourn_qty = $objp->quantity;
3210
						$prod_supplier->fourn_tva_tx = $objp->tva_tx;
3211
						$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3212
						$priceparser = new PriceParser($this->db);
3213
						$price_result = $priceparser->parseProductSupplier($prod_supplier);
3214
						if ($price_result >= 0) {
3215
							$objp->fprice = $price_result;
3216
							if ($objp->quantity >= 1)
3217
							{
3218
								$objp->unitprice = $objp->fprice / $objp->quantity;
3219
							}
3220
						}
3221
					}
3222
					if ($objp->quantity == 1)
3223
					{
3224
						$opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3225
					}
3226
3227
					$opt .= $objp->quantity.' ';
3228
3229
					if ($objp->quantity == 1)
3230
					{
3231
						$opt .= $langs->trans("Unit");
3232
					} else {
3233
						$opt .= $langs->trans("Units");
3234
					}
3235
					if ($objp->quantity > 1)
3236
					{
3237
						$opt .= " - ";
3238
						$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");
3239
					}
3240
					if ($objp->duration) $opt .= " - ".$objp->duration;
3241
					$opt .= "</option>\n";
3242
3243
					$form .= $opt;
3244
					$i++;
3245
				}
3246
			}
3247
3248
			$form .= '</select>';
3249
			$this->db->free($result);
3250
			return $form;
3251
		} else {
3252
			dol_print_error($this->db);
3253
		}
3254
	}
3255
3256
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3257
	/**
3258
	 *    Return list of delivery address
3259
	 *
3260
	 *    @param    string	$selected          	Id contact pre-selectionn
3261
	 *    @param    int		$socid				Id of company
3262
	 *    @param    string	$htmlname          	Name of HTML field
3263
	 *    @param    int		$showempty         	Add an empty field
3264
	 *    @return	integer|null
3265
	 */
3266
	public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3267
	{
3268
		// phpcs:enable
3269
		// looking for users
3270
		$sql = "SELECT a.rowid, a.label";
3271
		$sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a";
3272
		$sql .= " WHERE a.fk_soc = ".$socid;
3273
		$sql .= " ORDER BY a.label ASC";
3274
3275
		dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3276
		$resql = $this->db->query($sql);
3277
		if ($resql)
3278
		{
3279
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3280
			if ($showempty) print '<option value="0">&nbsp;</option>';
3281
			$num = $this->db->num_rows($resql);
3282
			$i = 0;
3283
			if ($num)
3284
			{
3285
				while ($i < $num)
3286
				{
3287
					$obj = $this->db->fetch_object($resql);
3288
3289
					if ($selected && $selected == $obj->rowid)
3290
					{
3291
						print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3292
					} else {
3293
						print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3294
					}
3295
					$i++;
3296
				}
3297
			}
3298
			print '</select>';
3299
			return $num;
3300
		} else {
3301
			dol_print_error($this->db);
3302
		}
3303
	}
3304
3305
3306
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3307
	/**
3308
	 *      Load into cache list of payment terms
3309
	 *
3310
	 *      @return     int             Nb of lines loaded, <0 if KO
3311
	 */
3312
	public function load_cache_conditions_paiements()
3313
	{
3314
		// phpcs:enable
3315
		global $langs;
3316
3317
		$num = count($this->cache_conditions_paiements);
3318
		if ($num > 0) return 0; // Cache already loaded
3319
3320
		dol_syslog(__METHOD__, LOG_DEBUG);
3321
3322
		$sql = "SELECT rowid, code, libelle as label";
3323
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term';
3324
		$sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3325
		$sql .= " AND active > 0";
3326
		$sql .= " ORDER BY sortorder";
3327
3328
		$resql = $this->db->query($sql);
3329
		if ($resql)
3330
		{
3331
			$num = $this->db->num_rows($resql);
3332
			$i = 0;
3333
			while ($i < $num)
3334
			{
3335
				$obj = $this->db->fetch_object($resql);
3336
3337
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3338
				$label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3339
				$this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3340
				$this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3341
				$i++;
3342
			}
3343
3344
			//$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1);		// We use the field sortorder of table
3345
3346
			return $num;
3347
		} else {
3348
			dol_print_error($this->db);
3349
			return -1;
3350
		}
3351
	}
3352
3353
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3354
	/**
3355
	 *      Charge dans cache la liste des délais de livraison possibles
3356
	 *
3357
	 *      @return     int             Nb of lines loaded, <0 if KO
3358
	 */
3359
	public function load_cache_availability()
3360
	{
3361
		// phpcs:enable
3362
		global $langs;
3363
3364
		$num = count($this->cache_availability);
3365
		if ($num > 0) return 0; // Cache already loaded
3366
3367
		dol_syslog(__METHOD__, LOG_DEBUG);
3368
3369
		$langs->load('propal');
3370
3371
		$sql = "SELECT rowid, code, label, position";
3372
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_availability';
3373
		$sql .= " WHERE active > 0";
3374
3375
		$resql = $this->db->query($sql);
3376
		if ($resql)
3377
		{
3378
			$num = $this->db->num_rows($resql);
3379
			$i = 0;
3380
			while ($i < $num)
3381
			{
3382
				$obj = $this->db->fetch_object($resql);
3383
3384
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3385
				$label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3386
				$this->cache_availability[$obj->rowid]['code'] = $obj->code;
3387
				$this->cache_availability[$obj->rowid]['label'] = $label;
3388
				$this->cache_availability[$obj->rowid]['position'] = $obj->position;
3389
				$i++;
3390
			}
3391
3392
			$this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3393
3394
			return $num;
3395
		} else {
3396
			dol_print_error($this->db);
3397
			return -1;
3398
		}
3399
	}
3400
3401
	/**
3402
	 *      Retourne la liste des types de delais de livraison possibles
3403
	 *
3404
	 *      @param	int		$selected        Id du type de delais pre-selectionne
3405
	 *      @param  string	$htmlname        Nom de la zone select
3406
	 *      @param  string	$filtertype      To add a filter
3407
	 *		@param	int		$addempty		Add empty entry
3408
	 *		@return	void
3409
	 */
3410
	public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0)
3411
	{
3412
		global $langs, $user;
3413
3414
		$this->load_cache_availability();
3415
3416
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3417
3418
		print '<select id="'.$htmlname.'" class="flat" name="'.$htmlname.'">';
3419
		if ($addempty) print '<option value="0">&nbsp;</option>';
3420
		foreach ($this->cache_availability as $id => $arrayavailability)
3421
		{
3422
			if ($selected == $id)
3423
			{
3424
				print '<option value="'.$id.'" selected>';
3425
			} else {
3426
				print '<option value="'.$id.'">';
3427
			}
3428
			print $arrayavailability['label'];
3429
			print '</option>';
3430
		}
3431
		print '</select>';
3432
		if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3433
		print ajax_combobox($htmlname);
3434
	}
3435
3436
	/**
3437
	 *      Load into cache cache_demand_reason, array of input reasons
3438
	 *
3439
	 *      @return     int             Nb of lines loaded, <0 if KO
3440
	 */
3441
	public function loadCacheInputReason()
3442
	{
3443
		global $langs;
3444
3445
		$num = count($this->cache_demand_reason);
3446
		if ($num > 0) return 0; // Cache already loaded
3447
3448
		$sql = "SELECT rowid, code, label";
3449
		$sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason';
3450
		$sql .= " WHERE active > 0";
3451
3452
		$resql = $this->db->query($sql);
3453
		if ($resql)
3454
		{
3455
			$num = $this->db->num_rows($resql);
3456
			$i = 0;
3457
			$tmparray = array();
3458
			while ($i < $num)
3459
			{
3460
				$obj = $this->db->fetch_object($resql);
3461
3462
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3463
				$label = ($obj->label != '-' ? $obj->label : '');
3464
				if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3465
				if ($langs->trans($obj->code) != $obj->code) $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3466
3467
				$tmparray[$obj->rowid]['id']   = $obj->rowid;
3468
				$tmparray[$obj->rowid]['code'] = $obj->code;
3469
				$tmparray[$obj->rowid]['label'] = $label;
3470
				$i++;
3471
			}
3472
3473
			$this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
3474
3475
			unset($tmparray);
3476
			return $num;
3477
		} else {
3478
			dol_print_error($this->db);
3479
			return -1;
3480
		}
3481
	}
3482
3483
	/**
3484
	 *	Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
3485
	 *  List found into table c_input_reason loaded by loadCacheInputReason
3486
	 *
3487
	 *  @param	int		$selected        Id or code of type origin to select by default
3488
	 *  @param  string	$htmlname        Nom de la zone select
3489
	 *  @param  string	$exclude         To exclude a code value (Example: SRC_PROP)
3490
	 *	@param	int		$addempty		 Add an empty entry
3491
	 *	@return	void
3492
	 */
3493
	public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0)
3494
	{
3495
		global $langs, $user;
3496
3497
		$this->loadCacheInputReason();
3498
3499
		print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3500
		if ($addempty) print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
3501
		foreach ($this->cache_demand_reason as $id => $arraydemandreason)
3502
		{
3503
			if ($arraydemandreason['code'] == $exclude) continue;
3504
3505
			if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code']))
3506
			{
3507
				print '<option value="'.$arraydemandreason['id'].'" selected>';
3508
			} else {
3509
				print '<option value="'.$arraydemandreason['id'].'">';
3510
			}
3511
			$label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
3512
			print $langs->trans($label);
3513
			print '</option>';
3514
		}
3515
		print '</select>';
3516
		if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3517
		print ajax_combobox('select_'.$htmlname);
3518
	}
3519
3520
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3521
	/**
3522
	 *      Charge dans cache la liste des types de paiements possibles
3523
	 *
3524
	 *      @return     int                 Nb of lines loaded, <0 if KO
3525
	 */
3526
	public function load_cache_types_paiements()
3527
	{
3528
		// phpcs:enable
3529
		global $langs;
3530
3531
		$num = count($this->cache_types_paiements);
3532
		if ($num > 0) return $num; // Cache already loaded
3533
3534
		dol_syslog(__METHOD__, LOG_DEBUG);
3535
3536
		$this->cache_types_paiements = array();
3537
3538
		$sql = "SELECT id, code, libelle as label, type, active";
3539
		$sql .= " FROM ".MAIN_DB_PREFIX."c_paiement";
3540
		$sql .= " WHERE entity IN (".getEntity('c_paiement').")";
3541
		//if ($active >= 0) $sql.= " AND active = ".$active;
3542
3543
		$resql = $this->db->query($sql);
3544
		if ($resql)
3545
		{
3546
			$num = $this->db->num_rows($resql);
3547
			$i = 0;
3548
			while ($i < $num)
3549
			{
3550
				$obj = $this->db->fetch_object($resql);
3551
3552
				// Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3553
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3554
				$this->cache_types_paiements[$obj->id]['id'] = $obj->id;
3555
				$this->cache_types_paiements[$obj->id]['code'] = $obj->code;
3556
				$this->cache_types_paiements[$obj->id]['label'] = $label;
3557
				$this->cache_types_paiements[$obj->id]['type'] = $obj->type;
3558
				$this->cache_types_paiements[$obj->id]['active'] = $obj->active;
3559
				$i++;
3560
			}
3561
3562
			$this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
3563
3564
			return $num;
3565
		} else {
3566
			dol_print_error($this->db);
3567
			return -1;
3568
		}
3569
	}
3570
3571
3572
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3573
	/**
3574
	 *      Return list of payment modes.
3575
	 *      Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want.
3576
	 *      See instead to force the default value by the caller.
3577
	 *
3578
	 *      @param	int		$selected		Id of payment term to preselect by default
3579
	 *      @param	string	$htmlname		Nom de la zone select
3580
	 *      @param	int		$filtertype		Not used
3581
	 *		@param	int		$addempty		Add an empty entry
3582
	 * 		@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
3583
	 * 		@param	string	$morecss			Add more CSS on select tag
3584
	 *		@return	void
3585
	 */
3586
	public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '')
3587
	{
3588
		// phpcs:enable
3589
		global $langs, $user, $conf;
3590
3591
		dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3592
3593
		$this->load_cache_conditions_paiements();
3594
3595
		// Set default value if not already set by caller
3596
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
3597
3598
		print '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3599
		if ($addempty) print '<option value="0">&nbsp;</option>';
3600
		foreach ($this->cache_conditions_paiements as $id => $arrayconditions)
3601
		{
3602
			if ($selected == $id)
3603
			{
3604
				print '<option value="'.$id.'" selected>';
3605
			} else {
3606
				print '<option value="'.$id.'">';
3607
			}
3608
			print $arrayconditions['label'];
3609
			print '</option>';
3610
		}
3611
		print '</select>';
3612
		if ($user->admin && empty($noinfoadmin)) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3613
		print ajax_combobox($htmlname);
3614
	}
3615
3616
3617
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3618
	/**
3619
	 *      Return list of payment methods
3620
	 *      Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want.
3621
	 *
3622
	 *      @param	string	$selected       Id or code or preselected payment mode
3623
	 *      @param  string	$htmlname       Name of select field
3624
	 *      @param  string	$filtertype     To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
3625
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
3626
	 *      @param  int		$empty			1=can be empty, 0 otherwise
3627
	 * 		@param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
3628
	 *      @param  int		$maxlength      Max length of label
3629
	 *      @param  int     $active         Active or not, -1 = all
3630
	 *      @param  string  $morecss        Add more CSS on select tag
3631
	 * 		@return	void
3632
	 */
3633
	public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
3634
	{
3635
		// phpcs:enable
3636
		global $langs, $user, $conf;
3637
3638
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
3639
3640
		$filterarray = array();
3641
		if ($filtertype == 'CRDT')  	$filterarray = array(0, 2, 3);
3642
		elseif ($filtertype == 'DBIT') 	$filterarray = array(1, 2, 3);
3643
		elseif ($filtertype != '' && $filtertype != '-1') $filterarray = explode(',', $filtertype);
3644
3645
		$this->load_cache_types_paiements();
3646
3647
		// Set default value if not already set by caller
3648
		if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
3649
3650
		print '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3651
		if ($empty) print '<option value="">&nbsp;</option>';
3652
		foreach ($this->cache_types_paiements as $id => $arraytypes)
3653
		{
3654
			// If not good status
3655
			if ($active >= 0 && $arraytypes['active'] != $active) continue;
3656
3657
			// On passe si on a demande de filtrer sur des modes de paiments particuliers
3658
			if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) continue;
3659
3660
			// We discard empty line if showempty is on because an empty line has already been output.
3661
			if ($empty && empty($arraytypes['code'])) continue;
3662
3663
			if ($format == 0) print '<option value="'.$id.'"';
3664
			elseif ($format == 1) print '<option value="'.$arraytypes['code'].'"';
3665
			elseif ($format == 2) print '<option value="'.$arraytypes['code'].'"';
3666
			elseif ($format == 3) print '<option value="'.$id.'"';
3667
			// Print attribute selected or not
3668
			if ($format == 1 || $format == 2) {
3669
				if ($selected == $arraytypes['code']) print ' selected';
3670
			} else {
3671
				if ($selected == $id) print ' selected';
3672
			}
3673
			print '>';
3674
			if ($format == 0) $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
3675
			elseif ($format == 1) $value = $arraytypes['code'];
3676
			elseif ($format == 2) $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
3677
			elseif ($format == 3) $value = $arraytypes['code'];
3678
			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...
3679
			print '</option>';
3680
		}
3681
		print '</select>';
3682
		if ($user->admin && !$noadmininfo) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3683
		print ajax_combobox('select'.$htmlname);
3684
	}
3685
3686
3687
	/**
3688
	 *  Selection HT or TTC
3689
	 *
3690
	 *  @param	string	$selected       Id pre-selectionne
3691
	 *  @param  string	$htmlname       Nom de la zone select
3692
	 *  @param	string	$addjscombo		Add js combo
3693
	 * 	@return	string					Code of HTML select to chose tax or not
3694
	 */
3695
	public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
3696
	{
3697
		global $langs;
3698
3699
		$return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3700
		$options = array(
3701
			'HT'=>$langs->trans("HT"),
3702
			'TTC'=>$langs->trans("TTC")
3703
		);
3704
		foreach ($options as $id => $value)
3705
		{
3706
			if ($selected == $id)
3707
			{
3708
				$return .= '<option value="'.$id.'" selected>'.$value;
3709
			} else {
3710
				$return .= '<option value="'.$id.'">'.$value;
3711
			}
3712
			$return .= '</option>';
3713
		}
3714
		$return .= '</select>';
3715
		if ($addjscombo) $return .= ajax_combobox('select_'.$htmlname);
3716
3717
		return $return;
3718
	}
3719
3720
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3721
	/**
3722
	 *      Load in cache list of transport mode
3723
	 *
3724
	 *      @return     int                 Nb of lines loaded, <0 if KO
3725
	 */
3726
	public function load_cache_transport_mode()
3727
	{
3728
		// phpcs:enable
3729
		global $langs;
3730
3731
		$num = count($this->cache_transport_mode);
3732
		if ($num > 0) return $num; // Cache already loaded
3733
3734
		dol_syslog(__METHOD__, LOG_DEBUG);
3735
3736
		$this->cache_transport_mode = array();
3737
3738
		$sql = "SELECT rowid, code, label, active";
3739
		$sql .= " FROM ".MAIN_DB_PREFIX."c_transport_mode";
3740
		$sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
3741
		//if ($active >= 0) $sql.= " AND active = ".$active;
3742
3743
		$resql = $this->db->query($sql);
3744
		if ($resql)
3745
		{
3746
			$num = $this->db->num_rows($resql);
3747
			$i = 0;
3748
			while ($i < $num)
3749
			{
3750
				$obj = $this->db->fetch_object($resql);
3751
3752
				// If traduction exist, we use it else we take the default label
3753
				$label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3754
				$this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
3755
				$this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
3756
				$this->cache_transport_mode[$obj->rowid]['label'] = $label;
3757
				$this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
3758
				$i++;
3759
			}
3760
3761
			$this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
3762
3763
			return $num;
3764
		}
3765
		else {
3766
			dol_print_error($this->db);
3767
			return -1;
3768
		}
3769
	}
3770
3771
	/**
3772
	 *      Return list of transport mode for intracomm report
3773
	 *
3774
	 *      @param	string	$selected       Id of the transport mode pre-selected
3775
	 *      @param  string	$htmlname       Name of the select field
3776
	 *      @param  int		$format         0=id+label, 1=code+code, 2=code+label, 3=id+code
3777
	 *      @param  int		$empty			1=can be empty, 0 else
3778
	 *      @param	int		$noadmininfo	0=Add admin info, 1=Disable admin info
3779
	 *      @param  int		$maxlength      Max length of label
3780
	 *      @param  int     $active         Active or not, -1 = all
3781
	 *      @param  string  $morecss        Add more CSS on select tag
3782
	 * 		@return	void
3783
	 */
3784
	public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
3785
	{
3786
		global $langs, $user;
3787
3788
		dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
3789
3790
		$this->load_cache_transport_mode();
3791
3792
		print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3793
		if ($empty) print '<option value="">&nbsp;</option>';
3794
		foreach ($this->cache_transport_mode as $id => $arraytypes)
3795
		{
3796
			// If not good status
3797
			if ($active >= 0 && $arraytypes['active'] != $active) continue;
3798
3799
			// We discard empty line if showempty is on because an empty line has already been output.
3800
			if ($empty && empty($arraytypes['code'])) continue;
3801
3802
			if ($format == 0) print '<option value="'.$id.'"';
3803
			elseif ($format == 1) print '<option value="'.$arraytypes['code'].'"';
3804
			elseif ($format == 2) print '<option value="'.$arraytypes['code'].'"';
3805
			elseif ($format == 3) print '<option value="'.$id.'"';
3806
			// If text is selected, we compare with code, else with id
3807
			if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) print ' selected';
3808
			elseif ($selected == $id) print ' selected';
3809
			print '>';
3810
			if ($format == 0) $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
3811
			elseif ($format == 1) $value = $arraytypes['code'];
3812
			elseif ($format == 2) $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
3813
			elseif ($format == 3) $value = $arraytypes['code'];
3814
			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...
3815
			print '</option>';
3816
		}
3817
		print '</select>';
3818
		if ($user->admin && !$noadmininfo) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3819
	}
3820
3821
	/**
3822
	 *  Return a HTML select list of shipping mode
3823
	 *
3824
	 *  @param	string	$selected          Id shipping mode pre-selected
3825
	 *  @param  string	$htmlname          Name of select zone
3826
	 *  @param  string	$filtre            To filter list
3827
	 *  @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.
3828
	 *  @param  string	$moreattrib        To add more attribute on select
3829
	 *	@param	int		$noinfoadmin		0=Add admin info, 1=Disable admin info
3830
	 * 	@return	void
3831
	 */
3832
	public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0)
3833
	{
3834
		global $langs, $conf, $user;
3835
3836
		$langs->load("admin");
3837
		$langs->load("deliveries");
3838
3839
		$sql = "SELECT rowid, code, libelle as label";
3840
		$sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode";
3841
		$sql .= " WHERE active > 0";
3842
		if ($filtre) $sql .= " AND ".$filtre;
3843
		$sql .= " ORDER BY libelle ASC";
3844
3845
		dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
3846
		$result = $this->db->query($sql);
3847
		if ($result) {
3848
			$num = $this->db->num_rows($result);
3849
			$i = 0;
3850
			if ($num) {
3851
				print '<select id="select'.$htmlname.'" class="flat selectshippingmethod" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
3852
				if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
3853
					print '<option value="-1">&nbsp;</option>';
3854
				}
3855
				while ($i < $num) {
3856
					$obj = $this->db->fetch_object($result);
3857
					if ($selected == $obj->rowid) {
3858
						print '<option value="'.$obj->rowid.'" selected>';
3859
					} else {
3860
						print '<option value="'.$obj->rowid.'">';
3861
					}
3862
					print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
3863
					print '</option>';
3864
					$i++;
3865
				}
3866
				print "</select>";
3867
				if ($user->admin  && empty($noinfoadmin)) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3868
3869
				print ajax_combobox('select'.$htmlname);
3870
			} else {
3871
				print $langs->trans("NoShippingMethodDefined");
3872
			}
3873
		} else {
3874
			dol_print_error($this->db);
3875
		}
3876
	}
3877
3878
	/**
3879
	 *    Display form to select shipping mode
3880
	 *
3881
	 *    @param	string	$page        Page
3882
	 *    @param    int		$selected    Id of shipping mode
3883
	 *    @param    string	$htmlname    Name of select html field
3884
	 *    @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.
3885
	 *    @return	void
3886
	 */
3887
	public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
3888
	{
3889
		global $langs, $db;
3890
3891
		$langs->load("deliveries");
3892
3893
		if ($htmlname != "none") {
3894
			print '<form method="POST" action="'.$page.'">';
3895
			print '<input type="hidden" name="action" value="setshippingmethod">';
3896
			print '<input type="hidden" name="token" value="'.newToken().'">';
3897
			$this->selectShippingMethod($selected, $htmlname, '', $addempty);
3898
			print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
3899
			print '</form>';
3900
		} else {
3901
			if ($selected) {
3902
				$code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code');
3903
				print $langs->trans("SendingMethod".strtoupper($code));
3904
			} else {
3905
				print "&nbsp;";
3906
			}
3907
		}
3908
	}
3909
3910
	/**
3911
	 * Creates HTML last in cycle situation invoices selector
3912
	 *
3913
	 * @param     string  $selected   		Preselected ID
3914
	 * @param     int     $socid      		Company ID
3915
	 *
3916
	 * @return    string                     HTML select
3917
	 */
3918
	public function selectSituationInvoices($selected = '', $socid = 0)
3919
	{
3920
		global $langs;
3921
3922
		$langs->load('bills');
3923
3924
		$opt = '<option value ="" selected></option>';
3925
		$sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc';
3926
		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture';
3927
		$sql .= ' WHERE entity IN ('.getEntity('invoice').')';
3928
		$sql .= ' AND situation_counter >= 1';
3929
		$sql .= ' AND fk_soc = '.(int) $socid;
3930
		$sql .= ' AND type <> 2';
3931
		$sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
3932
		$resql = $this->db->query($sql);
3933
3934
		if ($resql && $this->db->num_rows($resql) > 0) {
3935
			// Last seen cycle
3936
			$ref = 0;
3937
			while ($obj = $this->db->fetch_object($resql)) {
3938
				//Same cycle ?
3939
				if ($obj->situation_cycle_ref != $ref) {
3940
					// Just seen this cycle
3941
					$ref = $obj->situation_cycle_ref;
3942
					//not final ?
3943
					if ($obj->situation_final != 1) {
3944
						//Not prov?
3945
						if (substr($obj->ref, 1, 4) != 'PROV') {
3946
							if ($selected == $obj->rowid) {
3947
								$opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
3948
							} else {
3949
								$opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
3950
							}
3951
						}
3952
					}
3953
				}
3954
			}
3955
		} else {
3956
				dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
3957
		}
3958
		if ($opt == '<option value ="" selected></option>')
3959
		{
3960
			$opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
3961
		}
3962
		return $opt;
3963
	}
3964
3965
	/**
3966
	 *      Creates HTML units selector (code => label)
3967
	 *
3968
	 *      @param	string	$selected       Preselected Unit ID
3969
	 *      @param  string	$htmlname       Select name
3970
	 *      @param	int		$showempty		Add a nempty line
3971
	 *      @param  string  $unit_type      Restrict to one given unit type
3972
	 * 		@return	string                  HTML select
3973
	 */
3974
	public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
3975
	{
3976
		global $langs;
3977
3978
		$langs->load('products');
3979
3980
		$return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
3981
3982
		$sql = 'SELECT rowid, label, code from '.MAIN_DB_PREFIX.'c_units';
3983
		$sql .= ' WHERE active > 0';
3984
		if (!empty($unit_type)) {
3985
			$sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
3986
		}
3987
3988
		$resql = $this->db->query($sql);
3989
		if ($resql && $this->db->num_rows($resql) > 0)
3990
		{
3991
			if ($showempty) $return .= '<option value="none"></option>';
3992
3993
			while ($res = $this->db->fetch_object($resql))
3994
			{
3995
				$unitLabel = $res->label;
3996
				if (!empty($langs->tab_translate['unit'.$res->code]))	// check if Translation is available before
3997
				{
3998
					$unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
3999
				}
4000
4001
				if ($selected == $res->rowid)
4002
				{
4003
					$return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4004
				} else {
4005
					$return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4006
				}
4007
			}
4008
			$return .= '</select>';
4009
		}
4010
		return $return;
4011
	}
4012
4013
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4014
	/**
4015
	 *  Return a HTML select list of bank accounts
4016
	 *
4017
	 *  @param	string	$selected           Id account pre-selected
4018
	 *  @param  string	$htmlname           Name of select zone
4019
	 *  @param  int		$status             Status of searched accounts (0=open, 1=closed, 2=both)
4020
	 *  @param  string	$filtre             To filter list
4021
	 *  @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.
4022
	 *  @param  string	$moreattrib         To add more attribute on select
4023
	 *  @param	int		$showcurrency		Show currency in label
4024
	 *  @param	string	$morecss			More CSS
4025
	 *  @param	int		$nooutput			1=Return string, do not send to output
4026
	 * 	@return	int							<0 if error, Num of bank account found if OK (0, 1, 2, ...)
4027
	 */
4028
	public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4029
	{
4030
		// phpcs:enable
4031
		global $langs, $conf;
4032
4033
		$out = '';
4034
4035
		$langs->load("admin");
4036
		$num = 0;
4037
4038
		$sql = "SELECT rowid, label, bank, clos as status, currency_code";
4039
		$sql .= " FROM ".MAIN_DB_PREFIX."bank_account";
4040
		$sql .= " WHERE entity IN (".getEntity('bank_account').")";
4041
		if ($status != 2) $sql .= " AND clos = ".(int) $status;
4042
		if ($filtre) $sql .= " AND ".$filtre;
4043
		$sql .= " ORDER BY label";
4044
4045
		dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4046
		$result = $this->db->query($sql);
4047
		if ($result)
4048
		{
4049
			$num = $this->db->num_rows($result);
4050
			$i = 0;
4051
			if ($num)
4052
			{
4053
				$out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4054
				if ($useempty == 1 || ($useempty == 2 && $num > 1))
4055
				{
4056
					$out .= '<option value="-1">&nbsp;</option>';
4057
				}
4058
4059
				while ($i < $num)
4060
				{
4061
					$obj = $this->db->fetch_object($result);
4062
					if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4063
						$out .= '<option value="'.$obj->rowid.'" selected>';
4064
					} else {
4065
						$out .= '<option value="'.$obj->rowid.'">';
4066
					}
4067
					$out .= trim($obj->label);
4068
					if ($showcurrency) $out .= ' ('.$obj->currency_code.')';
4069
					if ($status == 2 && $obj->status == 1) $out .= ' ('.$langs->trans("Closed").')';
4070
					$out .= '</option>';
4071
					$i++;
4072
				}
4073
				$out .= "</select>";
4074
				$out .= ajax_combobox('select'.$htmlname);
4075
			} else {
4076
				if ($status == 0) $out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4077
				else $out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4078
			}
4079
		} else {
4080
			dol_print_error($this->db);
4081
		}
4082
4083
		// Output or return
4084
		if (empty($nooutput)) print $out;
4085
		else return $out;
4086
4087
		return $num;
4088
	}
4089
4090
	/**
4091
	 *  Return a HTML select list of establishment
4092
	 *
4093
	 *  @param	string	$selected           Id establishment pre-selected
4094
	 *  @param  string	$htmlname           Name of select zone
4095
	 *  @param  int		$status             Status of searched establishment (0=open, 1=closed, 2=both)
4096
	 *  @param  string	$filtre             To filter list
4097
	 *  @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.
4098
	 *  @param  string	$moreattrib         To add more attribute on select
4099
	 * 	@return	int							<0 if error, Num of establishment found if OK (0, 1, 2, ...)
4100
	 */
4101
	public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4102
	{
4103
		// phpcs:enable
4104
		global $langs, $conf;
4105
4106
		$langs->load("admin");
4107
		$num = 0;
4108
4109
		$sql = "SELECT rowid, name, fk_country, status, entity";
4110
		$sql .= " FROM ".MAIN_DB_PREFIX."establishment";
4111
		$sql .= " WHERE 1=1";
4112
		if ($status != 2) $sql .= " AND status = ".(int) $status;
4113
		if ($filtre) $sql .= " AND ".$filtre;
4114
		$sql .= " ORDER BY name";
4115
4116
		dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4117
		$result = $this->db->query($sql);
4118
		if ($result)
4119
		{
4120
			$num = $this->db->num_rows($result);
4121
			$i = 0;
4122
			if ($num)
4123
			{
4124
				print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4125
				if ($useempty == 1 || ($useempty == 2 && $num > 1))
4126
				{
4127
					print '<option value="-1">&nbsp;</option>';
4128
				}
4129
4130
				while ($i < $num)
4131
				{
4132
					$obj = $this->db->fetch_object($result);
4133
					if ($selected == $obj->rowid)
4134
					{
4135
						print '<option value="'.$obj->rowid.'" selected>';
4136
					} else {
4137
						print '<option value="'.$obj->rowid.'">';
4138
					}
4139
					print trim($obj->name);
4140
					if ($status == 2 && $obj->status == 1) print ' ('.$langs->trans("Closed").')';
4141
					print '</option>';
4142
					$i++;
4143
				}
4144
				print "</select>";
4145
			} else {
4146
				if ($status == 0) print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4147
				else print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4148
			}
4149
		} else {
4150
			dol_print_error($this->db);
4151
		}
4152
	}
4153
4154
	/**
4155
	 *    Display form to select bank account
4156
	 *
4157
	 *    @param	string	$page        Page
4158
	 *    @param    int		$selected    Id of bank account
4159
	 *    @param    string	$htmlname    Name of select html field
4160
	 *    @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.
4161
	 *    @return	void
4162
	 */
4163
	public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4164
	{
4165
		global $langs;
4166
		if ($htmlname != "none") {
4167
			print '<form method="POST" action="'.$page.'">';
4168
			print '<input type="hidden" name="action" value="setbankaccount">';
4169
			print '<input type="hidden" name="token" value="'.newToken().'">';
4170
			print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4171
			$nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4172
			if ($nbaccountfound > 0) print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4173
			print '</form>';
4174
		} else {
4175
			$langs->load('banks');
4176
4177
			if ($selected) {
4178
				require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4179
				$bankstatic = new Account($this->db);
4180
				$result = $bankstatic->fetch($selected);
4181
				if ($result) print $bankstatic->getNomUrl(1);
4182
			} else {
4183
				print "&nbsp;";
4184
			}
4185
		}
4186
	}
4187
4188
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4189
	/**
4190
	 *    Return list of categories having choosed type
4191
	 *
4192
	 *    @param	string|int	            $type				Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
4193
	 *    @param    string		            $selected    		Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1.
4194
	 *    @param    string		            $htmlname			HTML field name
4195
	 *    @param    int			            $maxlength      	Maximum length for labels
4196
	 *    @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.
4197
	 *                                                          $markafterid can be an :
4198
	 *                                                          - int (id of category)
4199
	 *                                                          - string (categories ids seprated by comma)
4200
	 *                                                          - array (list of categories ids)
4201
	 *    @param	int			            $outputmode			0=HTML select string, 1=Array
4202
	 *    @param	int			            $include			[=0] Removed or 1=Keep only
4203
	 *    @param	string					$morecss			More CSS
4204
	 *    @return	string
4205
	 *    @see select_categories()
4206
	 */
4207
	public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4208
	{
4209
		// phpcs:enable
4210
		global $conf, $langs;
4211
		$langs->load("categories");
4212
4213
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4214
4215
		// For backward compatibility
4216
		if (is_numeric($type))
4217
		{
4218
			dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4219
		}
4220
4221
		if ($type === Categorie::TYPE_BANK_LINE)
4222
		{
4223
			// TODO Move this into common category feature
4224
			$cate_arbo = array();
4225
			$sql = "SELECT c.label, c.rowid";
4226
			$sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c";
4227
			$sql .= " WHERE entity = ".$conf->entity;
4228
			$sql .= " ORDER BY c.label";
4229
			$result = $this->db->query($sql);
4230
			if ($result)
4231
			{
4232
				$num = $this->db->num_rows($result);
4233
				$i = 0;
4234
				while ($i < $num)
4235
				{
4236
					$objp = $this->db->fetch_object($result);
4237
					if ($objp) $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label);
4238
					$i++;
4239
				}
4240
				$this->db->free($result);
4241
			} else dol_print_error($this->db);
4242
		} else {
4243
			$cat = new Categorie($this->db);
4244
			$cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4245
		}
4246
4247
		$output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4248
		$outarray = array();
4249
		if (is_array($cate_arbo))
4250
		{
4251
			if (!count($cate_arbo)) $output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4252
			else {
4253
				$output .= '<option value="-1">&nbsp;</option>';
4254
				foreach ($cate_arbo as $key => $value)
4255
				{
4256
					if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1))
4257
					{
4258
						$add = 'selected ';
4259
					} else {
4260
						$add = '';
4261
					}
4262
					$output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'">'.dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle').'</option>';
4263
4264
					$outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4265
				}
4266
			}
4267
		}
4268
		$output .= '</select>';
4269
		$output .= "\n";
4270
4271
		if ($outputmode) return $outarray;
4272
		return $output;
4273
	}
4274
4275
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4276
	/**
4277
	 *     Show a confirmation HTML form or AJAX popup
4278
	 *
4279
	 *     @param	string		$page        	   	Url of page to call if confirmation is OK
4280
	 *     @param	string		$title       	   	Title
4281
	 *     @param	string		$question    	   	Question
4282
	 *     @param 	string		$action      	   	Action
4283
	 *	   @param	array		$formquestion	   	An array with forms complementary inputs
4284
	 * 	   @param	string		$selectedchoice		"" or "no" or "yes"
4285
	 * 	   @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
4286
	 *     @param	int			$height          	Force height of box
4287
	 *     @param	int			$width				Force width of box
4288
	 *     @return 	void
4289
	 *     @deprecated
4290
	 *     @see formconfirm()
4291
	 */
4292
	public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4293
	{
4294
		// phpcs:enable
4295
		dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4296
		print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4297
	}
4298
4299
	/**
4300
	 *     Show a confirmation HTML form or AJAX popup.
4301
	 *     Easiest way to use this is with useajax=1.
4302
	 *     If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters)
4303
	 *     just after calling this method. For example:
4304
	 *       print '<script type="text/javascript">'."\n";
4305
	 *       print 'jQuery(document).ready(function() {'."\n";
4306
	 *       print 'jQuery(".xxxlink").click(function(e) { jQuery("#aparamid").val(jQuery(this).attr("rel")); jQuery("#dialog-confirm-xxx").dialog("open"); return false; });'."\n";
4307
	 *       print '});'."\n";
4308
	 *       print '</script>'."\n";
4309
	 *
4310
	 *     @param  	string			$page        	   	Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated)
4311
	 *     @param	string			$title       	   	Title
4312
	 *     @param	string			$question    	   	Question
4313
	 *     @param 	string			$action      	   	Action
4314
	 *	   @param  	array|string	$formquestion	   	An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>))
4315
	 *													type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', 'other' or 'onecolumn'...
4316
	 * 	   @param  	string			$selectedchoice  	'' or 'no', or 'yes' or '1' or '0'
4317
	 * 	   @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
4318
	 *     @param  	int|string		$height          	Force height of box (0 = auto)
4319
	 *     @param	int				$width				Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones.
4320
	 *     @param	int				$disableformtag		1=Disable form tag. Can be used if we are already inside a <form> section.
4321
	 *     @return 	string      		    			HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form
4322
	 */
4323
	public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0)
4324
	{
4325
		global $langs, $conf;
4326
4327
		$more = '<!-- formconfirm for page='.dol_escape_htmltag($page).' -->';
4328
		$formconfirm = '';
4329
		$inputok = array();
4330
		$inputko = array();
4331
4332
		// Clean parameters
4333
		$newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
4334
		if ($conf->browser->layout == 'phone') $width = '95%';
4335
4336
		// Set height automatically if not defined
4337
		if (empty($height)) {
4338
			$height = 220;
4339
			if (is_array($formquestion) && count($formquestion) > 2) {
4340
				$height += ((count($formquestion) - 2) * 24);
4341
			}
4342
		}
4343
4344
		if (is_array($formquestion) && !empty($formquestion))
4345
		{
4346
			// First add hidden fields and value
4347
			foreach ($formquestion as $key => $input)
4348
			{
4349
				if (is_array($input) && !empty($input))
4350
				{
4351
					if ($input['type'] == 'hidden')
4352
					{
4353
						$more .= '<input type="hidden" id="'.$input['name'].'" name="'.$input['name'].'" value="'.dol_escape_htmltag($input['value']).'">'."\n";
4354
					}
4355
				}
4356
			}
4357
4358
			// Now add questions
4359
			$moreonecolumn = '';
4360
			$more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
4361
			foreach ($formquestion as $key => $input)
4362
			{
4363
				if (is_array($input) && !empty($input))
4364
				{
4365
					$size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : '');
4366
					$moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4367
					$morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4368
4369
					if ($input['type'] == 'text') {
4370
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd"><input type="text" class="flat'.$morecss.'" id="'.$input['name'].'" name="'.$input['name'].'"'.$size.' value="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4371
					} elseif ($input['type'] == 'password')	{
4372
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd"><input type="password" class="flat'.$morecss.'" id="'.$input['name'].'" name="'.$input['name'].'"'.$size.' value="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4373
					} elseif ($input['type'] == 'select') {
4374
						if (empty($morecss)) {
4375
							$morecss = 'minwidth100';
4376
						}
4377
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4378
						if (!empty($input['label'])) $more .= $input['label'].'</div><div class="tagtd left">';
4379
						$more .= $this->selectarray($input['name'], $input['values'], $input['default'], 1, 0, 0, $moreattr, 0, 0, 0, '', $morecss);
4380
						$more .= '</div></div>'."\n";
4381
					} elseif ($input['type'] == 'checkbox') {
4382
						$more .= '<div class="tagtr">';
4383
						$more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
4384
						$more .= '<input type="checkbox" class="flat'.$morecss.'" id="'.$input['name'].'" name="'.$input['name'].'"'.$moreattr;
4385
						if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0') $more .= ' checked';
4386
						if (is_bool($input['value']) && $input['value']) $more .= ' checked';
4387
						if (isset($input['disabled'])) $more .= ' disabled';
4388
						$more .= ' /></div>';
4389
						$more .= '</div>'."\n";
4390
					} elseif ($input['type'] == 'radio') {
4391
						$i = 0;
4392
						foreach ($input['values'] as $selkey => $selval)
4393
						{
4394
							$more .= '<div class="tagtr">';
4395
							if ($i == 0) $more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
4396
							else $more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
4397
							$more .= '<div class="tagtd'.($i == 0 ? ' tdtop' : '').'"><input type="radio" class="flat'.$morecss.'" id="'.$input['name'].$selkey.'" name="'.$input['name'].'" value="'.$selkey.'"'.$moreattr;
4398
							if ($input['disabled']) $more .= ' disabled';
4399
							if (isset($input['default']) && $input['default'] === $selkey) $more .= ' checked="checked"';
4400
							$more .= ' /> ';
4401
							$more .= '<label for="'.$input['name'].$selkey.'">'.$selval.'</label>';
4402
							$more .= '</div></div>'."\n";
4403
							$i++;
4404
						}
4405
					} elseif ($input['type'] == 'date') {
4406
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
4407
						$more .= '<div class="tagtd">';
4408
						$more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, 0);
4409
						$more .= '</div></div>'."\n";
4410
						$formquestion[] = array('name'=>$input['name'].'day');
4411
						$formquestion[] = array('name'=>$input['name'].'month');
4412
						$formquestion[] = array('name'=>$input['name'].'year');
4413
						$formquestion[] = array('name'=>$input['name'].'hour');
4414
						$formquestion[] = array('name'=>$input['name'].'min');
4415
					} elseif ($input['type'] == 'other') {
4416
						$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
4417
						if (!empty($input['label'])) $more .= $input['label'].'</div><div class="tagtd">';
4418
						$more .= $input['value'];
4419
						$more .= '</div></div>'."\n";
4420
					} elseif ($input['type'] == 'onecolumn') {
4421
						$moreonecolumn .= '<div class="margintoponly">';
4422
						$moreonecolumn .= $input['value'];
4423
						$moreonecolumn .= '</div>'."\n";
4424
					} elseif ($input['type'] == 'hidden') {
4425
						// Do nothing more, already added by a previous loop
4426
					} else {
4427
						$more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
4428
					}
4429
				}
4430
			}
4431
			$more .= '</div>'."\n";
4432
			$more .= $moreonecolumn;
4433
		}
4434
4435
		// JQUI method dialog is broken with jmobile, we use standard HTML.
4436
		// 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
4437
		// See page product/card.php for example
4438
		if (!empty($conf->dol_use_jmobile)) $useajax = 0;
4439
		if (empty($conf->use_javascript_ajax)) $useajax = 0;
4440
4441
		if ($useajax)
4442
		{
4443
			$autoOpen = true;
4444
			$dialogconfirm = 'dialog-confirm';
4445
			$button = '';
4446
			if (!is_numeric($useajax))
4447
			{
4448
				$button = $useajax;
4449
				$useajax = 1;
4450
				$autoOpen = false;
4451
				$dialogconfirm .= '-'.$button;
4452
			}
4453
			$pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes';
4454
			$pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : '');
4455
4456
			// Add input fields into list of fields to read during submit (inputok and inputko)
4457
			if (is_array($formquestion)) {
4458
				foreach ($formquestion as $key => $input) {
4459
					//print "xx ".$key." rr ".is_array($input)."<br>\n";
4460
					// Add name of fields to propagate with the GET when submitting the form with button OK.
4461
					if (is_array($input) && isset($input['name'])) {
4462
						if (strpos($input['name'], ',') > 0) {
4463
							$inputok = array_merge($inputok, explode(',', $input['name']));
4464
						} else {
4465
							array_push($inputok, $input['name']);
4466
						}
4467
					}
4468
					// Add name of fields to propagate with the GET when submitting the form with button KO.
4469
					if (isset($input['inputko']) && $input['inputko'] == 1) array_push($inputko, $input['name']);
4470
				}
4471
			}
4472
4473
			// Show JQuery confirm box.
4474
			$formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
4475
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4476
				$formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
4477
			}
4478
			if (!empty($more)) {
4479
				$formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
4480
			}
4481
			$formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
4482
			$formconfirm .= '</div>'."\n";
4483
4484
			$formconfirm .= "\n<!-- begin ajax formconfirm page=".$page." -->\n";
4485
			$formconfirm .= '<script type="text/javascript">'."\n";
4486
			$formconfirm .= 'jQuery(document).ready(function() {
4487
            $(function() {
4488
            	$( "#'.$dialogconfirm.'" ).dialog(
4489
            	{
4490
                    autoOpen: '.($autoOpen ? "true" : "false").',';
4491
			if ($newselectedchoice == 'no')
4492
			{
4493
				$formconfirm .= '
4494
						open: function() {
4495
            				$(this).parent().find("button.ui-button:eq(2)").focus();
4496
						},';
4497
			}
4498
			$formconfirm .= '
4499
                    resizable: false,
4500
                    height: "'.$height.'",
4501
                    width: "'.$width.'",
4502
                    modal: true,
4503
                    closeOnEscape: false,
4504
                    buttons: {
4505
                        "'.dol_escape_js($langs->transnoentities("Yes")).'": function() {
4506
                        	var options = "&token='.urlencode(newToken()).'";
4507
                        	var inputok = '.json_encode($inputok).';	/* List of fields into form */
4508
                         	var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
4509
                         	if (inputok.length>0) {
4510
                         		$.each(inputok, function(i, inputname) {
4511
                         			var more = "";
4512
									var inputvalue;
4513
                         			if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
4514
										inputvalue = $("input[name=\'" + inputname + "\']").val();
4515
									} else {
4516
                         		    	if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4517
                         				inputvalue = $("#" + inputname + more).val();
4518
									}
4519
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4520
									console.log("check inputname="+inputname+" inputvalue="+inputvalue);
4521
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4522
                         		});
4523
                         	}
4524
                         	var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "") + options;
4525
            				if (pageyes.length > 0) { location.href = urljump; }
4526
                            $(this).dialog("close");
4527
                        },
4528
                        "'.dol_escape_js($langs->transnoentities("No")).'": function() {
4529
                        	var options = "&token='.urlencode(newToken()).'";
4530
                         	var inputko = '.json_encode($inputko).';	/* List of fields into form */
4531
                         	var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
4532
                         	if (inputko.length>0) {
4533
                         		$.each(inputko, function(i, inputname) {
4534
                         			var more = "";
4535
                         			if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
4536
                         			var inputvalue = $("#" + inputname + more).val();
4537
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
4538
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
4539
                         		});
4540
                         	}
4541
                         	var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "") + options;
4542
                         	//alert(urljump);
4543
            				if (pageno.length > 0) { location.href = urljump; }
4544
                            $(this).dialog("close");
4545
                        }
4546
                    }
4547
                }
4548
                );
4549
4550
            	var button = "'.$button.'";
4551
            	if (button.length > 0) {
4552
                	$( "#" + button ).click(function() {
4553
                		$("#'.$dialogconfirm.'").dialog("open");
4554
        			});
4555
                }
4556
            });
4557
            });
4558
            </script>';
4559
			$formconfirm .= "<!-- end ajax formconfirm -->\n";
4560
		} else {
4561
			$formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
4562
4563
			if (empty($disableformtag)) $formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
4564
4565
			$formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
4566
			$formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
4567
4568
			$formconfirm .= '<table class="valid centpercent">'."\n";
4569
4570
			// Line title
4571
			$formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="3">'.img_picto('', 'recent').' '.$title.'</td></tr>'."\n";
4572
4573
			// Line text
4574
			if (is_array($formquestion) && !empty($formquestion['text'])) {
4575
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="3">'.$formquestion['text'].'</td></tr>'."\n";
4576
			}
4577
4578
			// Line form fields
4579
			if ($more)
4580
			{
4581
				$formconfirm .= '<tr class="valid"><td class="valid" colspan="3">'."\n";
4582
				$formconfirm .= $more;
4583
				$formconfirm .= '</td></tr>'."\n";
4584
			}
4585
4586
			// Line with question
4587
			$formconfirm .= '<tr class="valid">';
4588
			$formconfirm .= '<td class="valid">'.$question.'</td>';
4589
			$formconfirm .= '<td class="valid">';
4590
			$formconfirm .= $this->selectyesno("confirm", $newselectedchoice);
4591
			$formconfirm .= '</td>';
4592
			$formconfirm .= '<td class="valid center"><input class="button valignmiddle confirmvalidatebutton" type="submit" value="'.$langs->trans("Validate").'"></td>';
4593
			$formconfirm .= '</tr>'."\n";
4594
4595
			$formconfirm .= '</table>'."\n";
4596
4597
			if (empty($disableformtag)) $formconfirm .= "</form>\n";
4598
			$formconfirm .= '<br>';
4599
4600
			if (empty($conf->use_javascript_ajax)) {
4601
				$formconfirm .= '<!-- code to disable button to avoid double clic -->';
4602
				$formconfirm .= '<script type="text/javascript">'."\n";
4603
				$formconfirm .= '
4604
				$(document).ready(function () {
4605
					$(".confirmvalidatebutton").on("click", function() {
4606
						console.log("We click on button");
4607
						$(this).attr("disabled", "disabled");
4608
						setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
4609
						//console.log($(this).closest("form"));
4610
						$(this).closest("form").submit();
4611
					});
4612
				});
4613
				';
4614
				$formconfirm .= '</script>'."\n";
4615
			}
4616
4617
			$formconfirm .= "<!-- end formconfirm -->\n";
4618
		}
4619
4620
		return $formconfirm;
4621
	}
4622
4623
4624
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4625
	/**
4626
	 *    Show a form to select a project
4627
	 *
4628
	 *    @param	int		$page        		Page
4629
	 *    @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)
4630
	 *    @param    int		$selected    		Id pre-selected project
4631
	 *    @param    string	$htmlname    		Name of select field
4632
	 *    @param	int		$discard_closed		Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable)
4633
	 *    @param	int		$maxlength			Max length
4634
	 *    @param	int		$forcefocus			Force focus on field (works with javascript only)
4635
	 *    @param    int     $nooutput           No print is done. String is returned.
4636
	 *    @return	string                      Return html content
4637
	 */
4638
	public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0)
4639
	{
4640
		// phpcs:enable
4641
		global $langs;
4642
4643
		require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
4644
		require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
4645
4646
		$out = '';
4647
4648
		$formproject = new FormProjets($this->db);
4649
4650
		$langs->load("project");
4651
		if ($htmlname != "none")
4652
		{
4653
			$out .= "\n";
4654
			$out .= '<form method="post" action="'.$page.'">';
4655
			$out .= '<input type="hidden" name="action" value="classin">';
4656
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
4657
			$out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1);
4658
			$out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
4659
			$out .= '</form>';
4660
		} else {
4661
			if ($selected)
4662
			{
4663
				$projet = new Project($this->db);
4664
				$projet->fetch($selected);
4665
				//print '<a href="'.DOL_URL_ROOT.'/projet/card.php?id='.$selected.'">'.$projet->title.'</a>';
4666
				$out .= $projet->getNomUrl(0, '', 1);
4667
			} else {
4668
				$out .= "&nbsp;";
4669
			}
4670
		}
4671
4672
		if (empty($nooutput))
4673
		{
4674
			print $out;
4675
			return '';
4676
		}
4677
		return $out;
4678
	}
4679
4680
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4681
	/**
4682
	 *	Show a form to select payment conditions
4683
	 *
4684
	 *  @param	int		$page        	Page
4685
	 *  @param  string	$selected    	Id condition pre-selectionne
4686
	 *  @param  string	$htmlname    	Name of select html field
4687
	 *	@param	int		$addempty		Add empty entry
4688
	 *  @return	void
4689
	 */
4690
	public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0)
4691
	{
4692
		// phpcs:enable
4693
		global $langs;
4694
		if ($htmlname != "none")
4695
		{
4696
			print '<form method="post" action="'.$page.'">';
4697
			print '<input type="hidden" name="action" value="setconditions">';
4698
			print '<input type="hidden" name="token" value="'.newToken().'">';
4699
			$this->select_conditions_paiements($selected, $htmlname, -1, $addempty);
4700
			print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
4701
			print '</form>';
4702
		} else {
4703
			if ($selected)
4704
			{
4705
				$this->load_cache_conditions_paiements();
4706
				print $this->cache_conditions_paiements[$selected]['label'];
4707
			} else {
4708
				print "&nbsp;";
4709
			}
4710
		}
4711
	}
4712
4713
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4714
	/**
4715
	 *  Show a form to select a delivery delay
4716
	 *
4717
	 *  @param  int		$page        	Page
4718
	 *  @param  string	$selected    	Id condition pre-selectionne
4719
	 *  @param  string	$htmlname    	Name of select html field
4720
	 *	@param	int		$addempty		Ajoute entree vide
4721
	 *  @return	void
4722
	 */
4723
	public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
4724
	{
4725
		// phpcs:enable
4726
		global $langs;
4727
		if ($htmlname != "none")
4728
		{
4729
			print '<form method="post" action="'.$page.'">';
4730
			print '<input type="hidden" name="action" value="setavailability">';
4731
			print '<input type="hidden" name="token" value="'.newToken().'">';
4732
			$this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
4733
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
4734
			print '</form>';
4735
		} else {
4736
			if ($selected)
4737
			{
4738
				$this->load_cache_availability();
4739
				print $this->cache_availability[$selected]['label'];
4740
			} else {
4741
				print "&nbsp;";
4742
			}
4743
		}
4744
	}
4745
4746
	/**
4747
	 *  Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
4748
	 *  List found into table c_input_reason loaded by loadCacheInputReason
4749
	 *
4750
	 *  @param  string	$page        	Page
4751
	 *  @param  string	$selected    	Id condition pre-selectionne
4752
	 *  @param  string	$htmlname    	Name of select html field
4753
	 *  @param	int		$addempty		Add empty entry
4754
	 *  @return	void
4755
	 */
4756
	public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
4757
	{
4758
		global $langs;
4759
		if ($htmlname != "none")
4760
		{
4761
			print '<form method="post" action="'.$page.'">';
4762
			print '<input type="hidden" name="action" value="setdemandreason">';
4763
			print '<input type="hidden" name="token" value="'.newToken().'">';
4764
			$this->selectInputReason($selected, $htmlname, -1, $addempty);
4765
			print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
4766
			print '</form>';
4767
		} else {
4768
			if ($selected)
4769
			{
4770
				$this->loadCacheInputReason();
4771
				foreach ($this->cache_demand_reason as $key => $val)
4772
				{
4773
					if ($val['id'] == $selected)
4774
					{
4775
						print $val['label'];
4776
						break;
4777
					}
4778
				}
4779
			} else {
4780
				print "&nbsp;";
4781
			}
4782
		}
4783
	}
4784
4785
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4786
	/**
4787
	 *    Show a form + html select a date
4788
	 *
4789
	 *    @param	string		$page        	Page
4790
	 *    @param	string		$selected    	Date preselected
4791
	 *    @param    string		$htmlname    	Html name of date input fields or 'none'
4792
	 *    @param    int			$displayhour 	Display hour selector
4793
	 *    @param    int			$displaymin		Display minutes selector
4794
	 *    @param	int			$nooutput		1=No print output, return string
4795
	 *    @return	string
4796
	 *    @see		selectDate()
4797
	 */
4798
	public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0)
4799
	{
4800
		// phpcs:enable
4801
		global $langs;
4802
4803
		$ret = '';
4804
4805
		if ($htmlname != "none")
4806
		{
4807
			$ret .= '<form method="post" action="'.$page.'" name="form'.$htmlname.'">';
4808
			$ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
4809
			$ret .= '<input type="hidden" name="token" value="'.newToken().'">';
4810
			$ret .= '<table class="nobordernopadding" cellpadding="0" cellspacing="0">';
4811
			$ret .= '<tr><td>';
4812
			$ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
4813
			$ret .= '</td>';
4814
			$ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
4815
			$ret .= '</tr></table></form>';
4816
		} else {
4817
			if ($displayhour) $ret .= dol_print_date($selected, 'dayhour');
4818
			else $ret .= dol_print_date($selected, 'day');
4819
		}
4820
4821
		if (empty($nooutput)) print $ret;
4822
		return $ret;
4823
	}
4824
4825
4826
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4827
	/**
4828
	 *  Show a select form to choose a user
4829
	 *
4830
	 *  @param	string	$page        	Page
4831
	 *  @param  string	$selected    	Id of user preselected
4832
	 *  @param  string	$htmlname    	Name of input html field. If 'none', we just output the user link.
4833
	 *  @param  array	$exclude		List of users id to exclude
4834
	 *  @param  array	$include        List of users id to include
4835
	 *  @return	void
4836
	 */
4837
	public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
4838
	{
4839
		// phpcs:enable
4840
		global $langs;
4841
4842
		if ($htmlname != "none")
4843
		{
4844
			print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
4845
			print '<input type="hidden" name="action" value="set'.$htmlname.'">';
4846
			print '<input type="hidden" name="token" value="'.newToken().'">';
4847
			print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
4848
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4849
			print '</form>';
4850
		} else {
4851
			if ($selected)
4852
			{
4853
				require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
4854
				$theuser = new User($this->db);
4855
				$theuser->fetch($selected);
4856
				print $theuser->getNomUrl(1);
4857
			} else {
4858
				print "&nbsp;";
4859
			}
4860
		}
4861
	}
4862
4863
4864
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4865
	/**
4866
	 *    Show form with payment mode
4867
	 *
4868
	 *    @param	string	$page        	Page
4869
	 *    @param    int		$selected    	Id mode pre-selectionne
4870
	 *    @param    string	$htmlname    	Name of select html field
4871
	 *    @param  	string	$filtertype		To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz))
4872
	 *    @param    int     $active         Active or not, -1 = all
4873
	 *    @param   int     $addempty       1=Add empty entry
4874
	 *    @return	void
4875
	 */
4876
	public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0)
4877
	{
4878
		// phpcs:enable
4879
		global $langs;
4880
		if ($htmlname != "none")
4881
		{
4882
			print '<form method="POST" action="'.$page.'">';
4883
			print '<input type="hidden" name="action" value="setmode">';
4884
			print '<input type="hidden" name="token" value="'.newToken().'">';
4885
			$this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active);
4886
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4887
			print '</form>';
4888
		} else {
4889
			if ($selected)
4890
			{
4891
				$this->load_cache_types_paiements();
4892
				print $this->cache_types_paiements[$selected]['label'];
4893
			} else {
4894
				print "&nbsp;";
4895
			}
4896
		}
4897
	}
4898
4899
	/**
4900
	 *    Show form with transport mode
4901
	 *
4902
	 *    @param	string	$page        	Page
4903
	 *    @param    int		$selected    	Id mode pre-select
4904
	 *    @param    string	$htmlname    	Name of select html field
4905
	 *    @param    int     $active         Active or not, -1 = all
4906
	 *    @param    int     $addempty       1=Add empty entry
4907
	 *    @return	void
4908
	 */
4909
	public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
4910
	{
4911
		global $langs;
4912
		if ($htmlname != "none")
4913
		{
4914
			print '<form method="POST" action="'.$page.'">';
4915
			print '<input type="hidden" name="action" value="setmode">';
4916
			print '<input type="hidden" name="token" value="'.newToken().'">';
4917
			$this->selectTransportMode($selected, $htmlname, 2, $addempty, 0, 0, $active);
4918
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4919
			print '</form>';
4920
		}
4921
		else {
4922
			if ($selected)
4923
			{
4924
				$this->load_cache_transport_mode();
4925
				print $this->cache_transport_mode[$selected]['label'];
4926
			} else {
4927
				print "&nbsp;";
4928
			}
4929
		}
4930
	}
4931
4932
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4933
	/**
4934
	 *    Show form with multicurrency code
4935
	 *
4936
	 *    @param	string	$page        	Page
4937
	 *    @param    string	$selected    	code pre-selectionne
4938
	 *    @param    string	$htmlname    	Name of select html field
4939
	 *    @return	void
4940
	 */
4941
	public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
4942
	{
4943
		// phpcs:enable
4944
		global $langs;
4945
		if ($htmlname != "none")
4946
		{
4947
			print '<form method="POST" action="'.$page.'">';
4948
			print '<input type="hidden" name="action" value="setmulticurrencycode">';
4949
			print '<input type="hidden" name="token" value="'.newToken().'">';
4950
			print $this->selectMultiCurrency($selected, $htmlname, 0);
4951
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4952
			print '</form>';
4953
		} else {
4954
			dol_include_once('/core/lib/company.lib.php');
4955
			print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
4956
		}
4957
	}
4958
4959
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4960
	/**
4961
	 *    Show form with multicurrency rate
4962
	 *
4963
	 *    @param	string	$page        	Page
4964
	 *    @param    double	$rate	    	Current rate
4965
	 *    @param    string	$htmlname    	Name of select html field
4966
	 *    @param    string  $currency       Currency code to explain the rate
4967
	 *    @return	void
4968
	 */
4969
	public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
4970
	{
4971
		// phpcs:enable
4972
		global $langs, $mysoc, $conf;
4973
4974
		if ($htmlname != "none")
4975
		{
4976
			print '<form method="POST" action="'.$page.'">';
4977
			print '<input type="hidden" name="action" value="setmulticurrencyrate">';
4978
			print '<input type="hidden" name="token" value="'.newToken().'">';
4979
			print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
4980
			print '<select name="calculation_mode">';
4981
			print '<option value="1">'.$currency.' > '.$conf->currency.'</option>';
4982
			print '<option value="2">'.$conf->currency.' > '.$currency.'</option>';
4983
			print '</select> ';
4984
			print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4985
			print '</form>';
4986
		} else {
4987
			if (!empty($rate))
4988
			{
4989
				print price($rate, 1, $langs, 1, 0);
4990
				if ($currency && $rate != 1) print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
4991
			} else {
4992
				print 1;
4993
			}
4994
		}
4995
	}
4996
4997
4998
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4999
	/**
5000
	 *	Show a select box with available absolute discounts
5001
	 *
5002
	 *  @param  string	$page        	Page URL where form is shown
5003
	 *  @param  int		$selected    	Value pre-selected
5004
	 *	@param  string	$htmlname    	Name of SELECT component. If 'none', not changeable. Example 'remise_id'.
5005
	 *	@param	int		$socid			Third party id
5006
	 * 	@param	float	$amount			Total amount available
5007
	 * 	@param	string	$filter			SQL filter on discounts
5008
	 * 	@param	int		$maxvalue		Max value for lines that can be selected
5009
	 *  @param  string	$more           More string to add
5010
	 *  @param  int     $hidelist       1=Hide list
5011
	 *  @param	int		$discount_type	0 => customer discount, 1 => supplier discount
5012
	 *  @return	void
5013
	 */
5014
	public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5015
	{
5016
		// phpcs:enable
5017
		global $conf, $langs;
5018
		if ($htmlname != "none")
5019
		{
5020
			print '<form method="post" action="'.$page.'">';
5021
			print '<input type="hidden" name="action" value="setabsolutediscount">';
5022
			print '<input type="hidden" name="token" value="'.newToken().'">';
5023
			print '<div class="inline-block">';
5024
			if (!empty($discount_type)) {
5025
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS))
5026
				{
5027
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5028
					else $translationKey = 'HasCreditNoteFromSupplier';
5029
				} else {
5030
					if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") $translationKey = 'HasAbsoluteDiscountFromSupplier';
5031
					else $translationKey = 'HasCreditNoteFromSupplier';
5032
				}
5033
			} else {
5034
				if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS))
5035
				{
5036
					if (!$filter || $filter == "fk_facture_source IS NULL") $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5037
					else $translationKey = 'CompanyHasCreditNote';
5038
				} else {
5039
					if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") $translationKey = 'CompanyHasAbsoluteDiscount';
5040
					else $translationKey = 'CompanyHasCreditNote';
5041
				}
5042
			}
5043
			print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5044
			if (empty($hidelist)) print ' ';
5045
			print '</div>';
5046
			if (empty($hidelist))
5047
			{
5048
				print '<div class="inline-block" style="padding-right: 10px">';
5049
				$newfilter = 'discount_type='.intval($discount_type);
5050
				if (!empty($discount_type)) {
5051
					$newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5052
				} else {
5053
					$newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5054
				}
5055
				if ($filter) $newfilter .= ' AND ('.$filter.')';
5056
				$nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5057
				if ($nbqualifiedlines > 0)
5058
				{
5059
					print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5060
					if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')")
5061
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5062
					if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')")
5063
						print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5064
5065
					print '>';
5066
				}
5067
				print '</div>';
5068
			}
5069
			if ($more)
5070
			{
5071
				print '<div class="inline-block">';
5072
				print $more;
5073
				print '</div>';
5074
			}
5075
			print '</form>';
5076
		} else {
5077
			if ($selected)
5078
			{
5079
				print $selected;
5080
			} else {
5081
				print "0";
5082
			}
5083
		}
5084
	}
5085
5086
5087
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5088
	/**
5089
	 *  Show forms to select a contact
5090
	 *
5091
	 *  @param	string		$page        	Page
5092
	 *  @param	Societe		$societe		Filter on third party
5093
	 *  @param    int			$selected    	Id contact pre-selectionne
5094
	 *  @param    string		$htmlname    	Name of HTML select. If 'none', we just show contact link.
5095
	 *  @return	void
5096
	 */
5097
	public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5098
	{
5099
		// phpcs:enable
5100
		global $langs, $conf;
5101
5102
		if ($htmlname != "none")
5103
		{
5104
			print '<form method="post" action="'.$page.'">';
5105
			print '<input type="hidden" name="action" value="set_contact">';
5106
			print '<input type="hidden" name="token" value="'.newToken().'">';
5107
			print '<table class="nobordernopadding" cellpadding="0" cellspacing="0">';
5108
			print '<tr><td>';
5109
			print $this->selectcontacts($societe->id, $selected, $htmlname);
5110
			$num = $this->num;
5111
			if ($num == 0)
5112
			{
5113
				$addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5114
				print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5115
			}
5116
			print '</td>';
5117
			print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5118
			print '</tr></table></form>';
5119
		} else {
5120
			if ($selected)
5121
			{
5122
				require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5123
				$contact = new Contact($this->db);
5124
				$contact->fetch($selected);
5125
				print $contact->getFullName($langs);
5126
			} else {
5127
				print "&nbsp;";
5128
			}
5129
		}
5130
	}
5131
5132
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5133
	/**
5134
	 *  Output html select to select thirdparty
5135
	 *
5136
	 *  @param	string	$page       	Page
5137
	 *  @param  string	$selected   	Id preselected
5138
	 *  @param  string	$htmlname		Name of HTML select
5139
	 *  @param  string	$filter         optional filters criteras
5140
	 *	@param	int		$showempty		Add an empty field
5141
	 * 	@param	int		$showtype		Show third party type in combolist (customer, prospect or supplier)
5142
	 * 	@param	int		$forcecombo		Force to use combo box
5143
	 *  @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')))
5144
	 *  @param  int     $nooutput       No print output. Return it only.
5145
	 *  @return	void|string
5146
	 */
5147
	public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0)
5148
	{
5149
		// phpcs:enable
5150
		global $langs;
5151
5152
		$out = '';
5153
		if ($htmlname != "none")
5154
		{
5155
			$out .= '<form method="post" action="'.$page.'">';
5156
			$out .= '<input type="hidden" name="action" value="set_thirdparty">';
5157
			$out .= '<input type="hidden" name="token" value="'.newToken().'">';
5158
			$out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events);
5159
			$out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5160
			$out .= '</form>';
5161
		} else {
5162
			if ($selected)
5163
			{
5164
				require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5165
				$soc = new Societe($this->db);
5166
				$soc->fetch($selected);
5167
				$out .= $soc->getNomUrl($langs);
5168
			} else {
5169
				$out .= "&nbsp;";
5170
			}
5171
		}
5172
5173
		if ($nooutput) return $out;
5174
		else print $out;
5175
	}
5176
5177
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5178
	/**
5179
	 *    Retourne la liste des devises, dans la langue de l'utilisateur
5180
	 *
5181
	 *    @param	string	$selected    preselected currency code
5182
	 *    @param    string	$htmlname    name of HTML select list
5183
	 *    @deprecated
5184
	 *    @return	void
5185
	 */
5186
	public function select_currency($selected = '', $htmlname = 'currency_id')
5187
	{
5188
		// phpcs:enable
5189
		print $this->selectCurrency($selected, $htmlname);
5190
	}
5191
5192
	/**
5193
	 *  Retourne la liste des devises, dans la langue de l'utilisateur
5194
	 *
5195
	 *  @param	string	$selected    preselected currency code
5196
	 *  @param  string	$htmlname    name of HTML select list
5197
	 *  @param  string  $mode        0 = Add currency symbol into label, 1 = Add 3 letter iso code
5198
	 * 	@return	string
5199
	 */
5200
	public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0)
5201
	{
5202
		global $conf, $langs, $user;
5203
5204
		$langs->loadCacheCurrencies('');
5205
5206
		$out = '';
5207
5208
		if ($selected == 'euro' || $selected == 'euros') $selected = 'EUR'; // Pour compatibilite
5209
5210
		$out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
5211
		foreach ($langs->cache_currencies as $code_iso => $currency)
5212
		{
5213
			$labeltoshow = $currency['label'];
5214
			if ($mode == 1)
5215
			{
5216
				$labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
5217
			} else {
5218
				$labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
5219
			}
5220
5221
			if ($selected && $selected == $code_iso)
5222
			{
5223
				$out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
5224
			} else {
5225
				$out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
5226
			}
5227
			$out .= $labeltoshow;
5228
			$out .= '</option>';
5229
		}
5230
		$out .= '</select>';
5231
		if ($user->admin) $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5232
5233
		// Make select dynamic
5234
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5235
		$out .= ajax_combobox($htmlname);
5236
5237
		return $out;
5238
	}
5239
5240
	/**
5241
	 *	Return array of currencies in user language
5242
	 *
5243
	 *  @param	string	$selected    preselected currency code
5244
	 *  @param  string	$htmlname    name of HTML select list
5245
	 *  @param  integer	$useempty    1=Add empty line
5246
	 *  @param string $filter Optional filters criteras (example: 'code <> x', ' in (1,3)')
5247
	 *  @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
5248
	 * 	@return	string
5249
	 */
5250
	public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false)
5251
	{
5252
		global $db, $conf, $langs, $user;
5253
5254
		$langs->loadCacheCurrencies(''); // Load ->cache_currencies
5255
5256
		$TCurrency = array();
5257
5258
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency';
5259
		$sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
5260
		if ($filter) $sql .= " AND ".$filter;
5261
		$resql = $this->db->query($sql);
5262
		if ($resql)
5263
		{
5264
			while ($obj = $this->db->fetch_object($resql)) $TCurrency[$obj->code] = $obj->code;
5265
		}
5266
5267
		$out = '';
5268
		$out .= '<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">';
5269
		if ($useempty) $out .= '<option value="">&nbsp;</option>';
5270
		// If company current currency not in table, we add it into list. Should always be available.
5271
		if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency)
5272
		{
5273
			$TCurrency[$conf->currency] = $conf->currency;
5274
		}
5275
		if (count($TCurrency) > 0)
5276
		{
5277
			foreach ($langs->cache_currencies as $code_iso => $currency)
5278
			{
5279
				if (isset($TCurrency[$code_iso]))
5280
				{
5281
					if (!empty($selected) && $selected == $code_iso) $out .= '<option value="'.$code_iso.'" selected="selected">';
5282
					else $out .= '<option value="'.$code_iso.'">';
5283
5284
					$out .= $currency['label'];
5285
					$out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
5286
					$out .= '</option>';
5287
				}
5288
			}
5289
		}
5290
5291
		$out .= '</select>';
5292
		// Make select dynamic
5293
		include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5294
		$out .= ajax_combobox($htmlname);
5295
5296
		return $out;
5297
	}
5298
5299
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5300
	/**
5301
	 *  Load into the cache vat rates of a country
5302
	 *
5303
	 *  @param	string	$country_code		Country code with quotes ("'CA'", or "'CA,IN,...'")
5304
	 *  @return	int							Nb of loaded lines, 0 if already loaded, <0 if KO
5305
	 */
5306
	public function load_cache_vatrates($country_code)
5307
	{
5308
		// phpcs:enable
5309
		global $langs;
5310
5311
		$num = count($this->cache_vatrates);
5312
		if ($num > 0) return $num; // Cache already loaded
5313
5314
		dol_syslog(__METHOD__, LOG_DEBUG);
5315
5316
		$sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
5317
		$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5318
		$sql .= " WHERE t.fk_pays = c.rowid";
5319
		$sql .= " AND t.active > 0";
5320
		$sql .= " AND c.code IN (".$country_code.")";
5321
		$sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
5322
5323
		$resql = $this->db->query($sql);
5324
		if ($resql)
5325
		{
5326
			$num = $this->db->num_rows($resql);
5327
			if ($num)
5328
			{
5329
				for ($i = 0; $i < $num; $i++)
5330
				{
5331
					$obj = $this->db->fetch_object($resql);
5332
					$this->cache_vatrates[$i]['rowid']	= $obj->rowid;
5333
					$this->cache_vatrates[$i]['code'] = $obj->code;
5334
					$this->cache_vatrates[$i]['txtva']	= $obj->taux;
5335
					$this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
5336
					$this->cache_vatrates[$i]['localtax1']	    = $obj->localtax1;
5337
					$this->cache_vatrates[$i]['localtax1_type']	= $obj->localtax1_type;
5338
					$this->cache_vatrates[$i]['localtax2']	    = $obj->localtax2;
5339
					$this->cache_vatrates[$i]['localtax2_type']	= $obj->localtax1_type;
5340
5341
					$this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
5342
					$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
5343
					$positiverates = '';
5344
					if ($obj->taux) $positiverates .= ($positiverates ? '/' : '').$obj->taux;
5345
					if ($obj->localtax1) $positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
5346
					if ($obj->localtax2) $positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
5347
					if (empty($positiverates)) $positiverates = '0';
5348
					$this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
5349
				}
5350
5351
				return $num;
5352
			} else {
5353
				$this->error = '<font class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</font>';
5354
				return -1;
5355
			}
5356
		} else {
5357
			$this->error = '<font class="error">'.$this->db->error().'</font>';
5358
			return -2;
5359
		}
5360
	}
5361
5362
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5363
	/**
5364
	 *  Output an HTML select vat rate.
5365
	 *  The name of this function should be selectVat. We keep bad name for compatibility purpose.
5366
	 *
5367
	 *  @param	string	      $htmlname           Name of HTML select field
5368
	 *  @param  float|string  $selectedrate       Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing.
5369
	 *  @param  Societe	      $societe_vendeuse   Thirdparty seller
5370
	 *  @param  Societe	      $societe_acheteuse  Thirdparty buyer
5371
	 *  @param  int		      $idprod             Id product. O if unknown of NA.
5372
	 *  @param  int		      $info_bits          Miscellaneous information on line (1 for NPR)
5373
	 *  @param  int|string    $type               ''=Unknown, 0=Product, 1=Service (Used if idprod not defined)
5374
	 *                  		                  Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5375
	 *                  					      Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5376
	 *                  					      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.
5377
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle.
5378
	 *                                            Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle.
5379
	 *                  					      Sinon la TVA proposee par defaut=0. Fin de regle.
5380
	 *  @param	bool	     $options_only		  Return HTML options lines only (for ajax treatment)
5381
	 *  @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
5382
	 *  @return	string
5383
	 */
5384
	public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
5385
	{
5386
		// phpcs:enable
5387
		global $langs, $conf, $mysoc;
5388
5389
		$langs->load('errors');
5390
5391
		$return = '';
5392
5393
		// Define defaultnpr, defaultttx and defaultcode
5394
		$defaultnpr = ($info_bits & 0x01);
5395
		$defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
5396
		$defaulttx = str_replace('*', '', $selectedrate);
5397
		$defaultcode = '';
5398
		if (preg_match('/\((.*)\)/', $defaulttx, $reg))
5399
		{
5400
			$defaultcode = $reg[1];
5401
			$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5402
		}
5403
		//var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
5404
5405
		// Check parameters
5406
		if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code)
5407
		{
5408
			if ($societe_vendeuse->id == $mysoc->id)
5409
			{
5410
				$return .= '<font class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</font>';
5411
			} else {
5412
				$return .= '<font class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</font>';
5413
			}
5414
			return $return;
5415
		}
5416
5417
		//var_dump($societe_acheteuse);
5418
		//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";
5419
		//exit;
5420
5421
		// Define list of countries to use to search VAT rates to show
5422
		// First we defined code_country to use to find list
5423
		if (is_object($societe_vendeuse))
5424
		{
5425
			$code_country = "'".$societe_vendeuse->country_code."'";
5426
		} else {
5427
			$code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
5428
		}
5429
		if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))    // If option to have vat for end customer for services is on
5430
		{
5431
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
5432
			if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany())))
5433
			{
5434
				// We also add the buyer
5435
				if (is_numeric($type))
5436
				{
5437
					if ($type == 1) // We know product is a service
5438
					{
5439
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5440
					}
5441
				} elseif (!$idprod)  // We don't know type of product
5442
				{
5443
					$code_country .= ",'".$societe_acheteuse->country_code."'";
5444
				} else {
5445
					$prodstatic = new Product($this->db);
5446
					$prodstatic->fetch($idprod);
5447
					if ($prodstatic->type == Product::TYPE_SERVICE)   // We know product is a service
5448
					{
5449
						$code_country .= ",'".$societe_acheteuse->country_code."'";
5450
					}
5451
				}
5452
			}
5453
		}
5454
5455
		// Now we get list
5456
		$num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
5457
5458
		if ($num > 0)
5459
		{
5460
			// Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
5461
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0)
5462
			{
5463
				$tmpthirdparty = new Societe($this->db);
5464
				$defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5465
				$defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
5466
				if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
5467
					$defaultcode = $reg[1];
5468
					$defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
5469
				}
5470
				if (empty($defaulttx)) $defaultnpr = 0;
5471
			}
5472
5473
			// Si taux par defaut n'a pu etre determine, on prend dernier de la liste.
5474
			// Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant
5475
			if ($defaulttx < 0 || dol_strlen($defaulttx) == 0)
5476
			{
5477
				if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) $defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
5478
				else $defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS);
5479
			}
5480
5481
			// Disabled if seller is not subject to VAT
5482
			$disabled = false; $title = '';
5483
			if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0")
5484
			{
5485
				// Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
5486
				// of using supplier invoices (this is a very bad idea !)
5487
				if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT))
5488
				{
5489
					$title = ' title="'.$langs->trans('VATIsNotUsed').'"';
5490
					$disabled = true;
5491
				}
5492
			}
5493
5494
			if (!$options_only) $return .= '<select class="flat minwidth75imp" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
5495
5496
			$selectedfound = false;
5497
			foreach ($this->cache_vatrates as $rate)
5498
			{
5499
				// Keep only 0 if seller is not subject to VAT
5500
				if ($disabled && $rate['txtva'] != 0) continue;
5501
5502
				// Define key to use into select list
5503
				$key = $rate['txtva'];
5504
				$key .= $rate['nprtva'] ? '*' : '';
5505
				if ($mode > 0 && $rate['code']) $key .= ' ('.$rate['code'].')';
5506
				if ($mode < 0) $key = $rate['rowid'];
5507
5508
				$return .= '<option value="'.$key.'"';
5509
				if (!$selectedfound)
5510
				{
5511
					if ($defaultcode) // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
5512
					{
5513
						if ($defaultcode == $rate['code'])
5514
						{
5515
							$return .= ' selected';
5516
							$selectedfound = true;
5517
						}
5518
					} elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr)
5519
			   		{
5520
			   			$return .= ' selected';
5521
			   			$selectedfound = true;
5522
					}
5523
				}
5524
				$return .= '>';
5525
				//if (! empty($conf->global->MAIN_VAT_SHOW_POSITIVE_RATES))
5526
				if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES))
5527
				{
5528
					$return .= $rate['labelpositiverates'];
5529
				} else {
5530
					$return .= vatrate($rate['label']);
5531
				}
5532
				//$return.=($rate['code']?' '.$rate['code']:'');
5533
				$return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the *  (old behaviour only if new vat code is not used)
5534
5535
				$return .= '</option>';
5536
			}
5537
5538
			if (!$options_only) $return .= '</select>';
5539
		} else {
5540
			$return .= $this->error;
5541
		}
5542
5543
		$this->num = $num;
5544
		return $return;
5545
	}
5546
5547
5548
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5549
	/**
5550
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5551
	 *  Fields are preselected with :
5552
	 *            	- set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5553
	 *            	- local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5554
	 *            	- Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5555
	 *
5556
	 *	@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).
5557
	 *	@param	string		$prefix			Prefix for fields name
5558
	 *	@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
5559
	 *	@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
5560
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5561
	 *	@param	string		$form_name 		Not used
5562
	 *	@param	int			$d				1=Show days, month, years
5563
	 * 	@param	int			$addnowlink		Add a link "Now"
5564
	 * 	@param	int			$nooutput		Do not output html string but return it
5565
	 * 	@param 	int			$disabled		Disable input fields
5566
	 *  @param  int			$fullday        When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59
5567
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another select_date field.
5568
	 *  @param  datetime    $adddateof      Add a link "Date of invoice" using the following date.
5569
	 *  @return	string|void					Nothing or string if nooutput is 1
5570
	 *  @deprecated
5571
	 *  @see    selectDate(), form_date(), select_month(), select_year(), select_dayofweek()
5572
	 */
5573
	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 = '')
5574
	{
5575
		// phpcs:enable
5576
		$retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
5577
		if (!empty($nooutput)) {
5578
			return $retstring;
5579
		}
5580
		print $retstring;
5581
		return;
5582
	}
5583
5584
	/**
5585
	 *  Show 2 HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5586
	 *  Fields are preselected with :
5587
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5588
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5589
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5590
	 *
5591
	 *  @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).
5592
	 *  @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).
5593
	 *  @param	string		$prefix			Prefix for fields name
5594
	 *  @param	string		$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5595
	 * 	@return string                      Html for selectDate
5596
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
5597
	 */
5598
	public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0)
5599
	{
5600
		global $langs;
5601
5602
		$ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
5603
		$ret .= '<br>';
5604
		$ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
5605
		return $ret;
5606
	}
5607
5608
	/**
5609
	 *  Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes.
5610
	 *  Fields are preselected with :
5611
	 *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
5612
	 *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
5613
	 *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
5614
	 *
5615
	 *  @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).
5616
	 *  @param	string		$prefix			Prefix for fields name
5617
	 *  @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
5618
	 *	@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
5619
	 *	@param	int			$empty			0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
5620
	 *	@param	string		$form_name 		Not used
5621
	 *	@param	int			$d				1=Show days, month, years
5622
	 * 	@param	int			$addnowlink		Add a link "Now", 1 with server time, 2 with local computer time
5623
	 * 	@param 	int			$disabled		Disable input fields
5624
	 *  @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')
5625
	 *  @param	string		$addplusone		Add a link "+1 hour". Value must be name of another selectDate field.
5626
	 *  @param  datetime    $adddateof      Add a link "Date of ..." using the following date. See also $labeladddateof for the label used.
5627
	 *  @param  string      $openinghours   Specify hour start and hour end for the select ex 8,20
5628
	 *  @param  int         $stepminutes    Specify step for minutes between 1 and 30
5629
	 *  @param	string		$labeladddateof Label to use for the $adddateof parameter.
5630
	 *  @param	string 		$placeholder    Placeholder
5631
	 *  @param	mixed		$gm				'auto' (for backward compatibility, avoid this), 'gmt' or 'tzserver' or 'tzuserrel'
5632
	 * 	@return string                      Html for selectDate
5633
	 *  @see    form_date(), select_month(), select_year(), select_dayofweek()
5634
	 */
5635
	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')
5636
	{
5637
		global $conf, $langs;
5638
5639
		if ($gm === 'auto') {
5640
			$gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
5641
		}
5642
5643
		$retstring = '';
5644
5645
		if ($prefix == '') $prefix = 're';
5646
		if ($h == '') $h = 0;
5647
		if ($m == '') $m = 0;
5648
		$emptydate = 0;
5649
		$emptyhours = 0;
5650
		if ($stepminutes <= 0 || $stepminutes > 30) $stepminutes = 1;
5651
		if ($empty == 1) { $emptydate = 1; $emptyhours = 1; }
5652
		if ($empty == 2) { $emptydate = 0; $emptyhours = 1; }
5653
		$orig_set_time = $set_time;
5654
5655
		if ($set_time === '' && $emptydate == 0)
5656
		{
5657
			include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
5658
			if ($gm == 'tzuser' || $gm == 'tzuserrel') {
5659
				$set_time = dol_now($gm);
5660
			} else {
5661
				$set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
5662
			}
5663
		}
5664
5665
		// Analysis of the pre-selection date
5666
		$reg = array();
5667
		if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg))	// deprecated usage
5668
		{
5669
			// Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
5670
			$syear	= (!empty($reg[1]) ? $reg[1] : '');
5671
			$smonth = (!empty($reg[2]) ? $reg[2] : '');
5672
			$sday	= (!empty($reg[3]) ? $reg[3] : '');
5673
			$shour	= (!empty($reg[4]) ? $reg[4] : '');
5674
			$smin	= (!empty($reg[5]) ? $reg[5] : '');
5675
		} elseif (strval($set_time) != '' && $set_time != -1)
5676
		{
5677
			// set_time est un timestamps (0 possible)
5678
			$syear = dol_print_date($set_time, "%Y", $gm);
5679
			$smonth = dol_print_date($set_time, "%m", $gm);
5680
			$sday = dol_print_date($set_time, "%d", $gm);
5681
			if ($orig_set_time != '')
5682
			{
5683
				$shour = dol_print_date($set_time, "%H", $gm);
5684
				$smin = dol_print_date($set_time, "%M", $gm);
5685
				$ssec = dol_print_date($set_time, "%S", $gm);
5686
			} else {
5687
				$shour = '';
5688
				$smin = '';
5689
				$ssec = '';
5690
			}
5691
		} else {
5692
			// Date est '' ou vaut -1
5693
			$syear = '';
5694
			$smonth = '';
5695
			$sday = '';
5696
			$shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
5697
			$smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
5698
			$ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
5699
		}
5700
		if ($h == 3) $shour = '';
5701
		if ($m == 3) $smin = '';
5702
5703
		// You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
5704
		$usecalendar = 'combo';
5705
		if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
5706
			$usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
5707
		}
5708
5709
		if ($d)
5710
		{
5711
			// Show date with popup
5712
			if ($usecalendar != 'combo')
5713
			{
5714
				$formated_date = '';
5715
				//print "e".$set_time." t ".$conf->format_date_short;
5716
				if (strval($set_time) != '' && $set_time != -1)
5717
				{
5718
					//$formated_date=dol_print_date($set_time,$conf->format_date_short);
5719
					$formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
5720
				}
5721
5722
				// Calendrier popup version eldy
5723
				if ($usecalendar == "eldy")
5724
				{
5725
					// Zone de saisie manuelle de la date
5726
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
5727
					$retstring .= ($disabled ? ' disabled' : '');
5728
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
5729
					$retstring .= '>';
5730
5731
					// Icone calendar
5732
					$retstringbuttom = '';
5733
					if (!$disabled) {
5734
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
5735
						$base = DOL_URL_ROOT.'/core/';
5736
						$retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
5737
						$retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
5738
					} else {
5739
						$retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
5740
					}
5741
					$retstring = $retstringbuttom.$retstring;
5742
5743
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
5744
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
5745
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
5746
				} elseif ($usecalendar == 'jquery')
5747
				{
5748
					if (!$disabled)
5749
					{
5750
						// Output javascript for datepicker
5751
						$retstring .= "<script type='text/javascript'>";
5752
						$retstring .= "$(function(){ $('#".$prefix."').datepicker({
5753
							dateFormat: '".$langs->trans("FormatDateShortJQueryInput")."',
5754
							autoclose: true,
5755
							todayHighlight: true,";
5756
						if (!empty($conf->dol_use_jmobile))
5757
						{
5758
							$retstring .= "
5759
								beforeShow: function (input, datePicker) {
5760
									input.disabled = true;
5761
								},
5762
								onClose: function (dateText, datePicker) {
5763
									this.disabled = false;
5764
								},
5765
								";
5766
						}
5767
						// Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
5768
						if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS))
5769
						{
5770
							$retstring .= "
5771
								showOn: 'button',	/* both has problem with autocompletion */
5772
								buttonImage: '".DOL_URL_ROOT."/theme/".$conf->theme."/img/object_calendarday.png',
5773
								buttonImageOnly: true";
5774
						}
5775
						$retstring .= "
5776
							}) });";
5777
						$retstring .= "</script>";
5778
					}
5779
5780
					// Zone de saisie manuelle de la date
5781
					$retstring .= '<div class="nowrap inline-block divfordateinput">';
5782
					$retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
5783
					$retstring .= ($disabled ? ' disabled' : '');
5784
					$retstring .= ($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '');
5785
					$retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
5786
					$retstring .= '>';
5787
5788
					// Icone calendrier
5789
					if (!$disabled)
5790
					{
5791
						/* Not required. Managed by option buttonImage of jquery
5792
                		$retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
5793
                		$retstring.="<script type='text/javascript'>";
5794
                		$retstring.="jQuery(document).ready(function() {";
5795
                		$retstring.='	jQuery("#'.$prefix.'id").click(function() {';
5796
                		$retstring.="    	jQuery('#".$prefix."').focus();";
5797
                		$retstring.='    });';
5798
                		$retstring.='});';
5799
                		$retstring.="</script>";*/
5800
					} else {
5801
						$retstringbutton = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
5802
						$retsring = $retstringbutton.$retstring;
5803
					}
5804
5805
					$retstring .= '</div>';
5806
					$retstring .= '<input type="hidden" id="'.$prefix.'day"   name="'.$prefix.'day"   value="'.$sday.'">'."\n";
5807
					$retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
5808
					$retstring .= '<input type="hidden" id="'.$prefix.'year"  name="'.$prefix.'year"  value="'.$syear.'">'."\n";
5809
				} else {
5810
					$retstring .= "Bad value of MAIN_POPUP_CALENDAR";
5811
				}
5812
			}
5813
			// Show date with combo selects
5814
			else {
5815
				// Day
5816
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
5817
5818
				if ($emptydate || $set_time == -1)
5819
				{
5820
					$retstring .= '<option value="0" selected>&nbsp;</option>';
5821
				}
5822
5823
				for ($day = 1; $day <= 31; $day++)
5824
				{
5825
					$retstring .= '<option value="'.$day.'"'.($day == $sday ? ' selected' : '').'>'.$day.'</option>';
5826
				}
5827
5828
				$retstring .= "</select>";
5829
5830
				$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
5831
				if ($emptydate || $set_time == -1)
5832
				{
5833
					$retstring .= '<option value="0" selected>&nbsp;</option>';
5834
				}
5835
5836
				// Month
5837
				for ($month = 1; $month <= 12; $month++)
5838
				{
5839
					$retstring .= '<option value="'.$month.'"'.($month == $smonth ? ' selected' : '').'>';
5840
					$retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
5841
					$retstring .= "</option>";
5842
				}
5843
				$retstring .= "</select>";
5844
5845
				// Year
5846
				if ($emptydate || $set_time == -1)
5847
				{
5848
					$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.'">';
5849
				} else {
5850
					$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
5851
5852
					for ($year = $syear - 10; $year < $syear + 10; $year++)
5853
					{
5854
						$retstring .= '<option value="'.$year.'"'.($year == $syear ? ' selected' : '').'>'.$year.'</option>';
5855
					}
5856
					$retstring .= "</select>\n";
5857
				}
5858
			}
5859
		}
5860
5861
		if ($d && $h) {
5862
			$retstring .= ($h == 2 ? '<br>' : ' ');
5863
			$retstring .= '<span class="nowraponall">';
5864
		}
5865
5866
		if ($h)
5867
		{
5868
			$hourstart = 0;
5869
			$hourend = 24;
5870
			if ($openinghours != '') {
5871
				$openinghours = explode(',', $openinghours);
5872
				$hourstart = $openinghours[0];
5873
				$hourend = $openinghours[1];
5874
				if ($hourend < $hourstart) $hourend = $hourstart;
5875
			}
5876
			// Show hour
5877
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'hour' : '').'" id="'.$prefix.'hour" name="'.$prefix.'hour">';
5878
			if ($emptyhours) $retstring .= '<option value="-1">&nbsp;</option>';
5879
			for ($hour = $hourstart; $hour < $hourend; $hour++)
5880
			{
5881
				if (strlen($hour) < 2) $hour = "0".$hour;
5882
				$retstring .= '<option value="'.$hour.'"'.(($hour == $shour) ? ' selected' : '').'>'.$hour;
5883
				//$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
5884
				$retstring .= '</option>';
5885
			}
5886
			$retstring .= '</select>';
5887
			//if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
5888
			if ($m) $retstring .= ":";
5889
		}
5890
5891
		if ($m)
5892
		{
5893
			// Show minutes
5894
			$retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'min' : '').'" id="'.$prefix.'min" name="'.$prefix.'min">';
5895
			if ($emptyhours) $retstring .= '<option value="-1">&nbsp;</option>';
5896
			for ($min = 0; $min < 60; $min += $stepminutes)
5897
			{
5898
				if (strlen($min) < 2) $min = "0".$min;
5899
				$retstring .= '<option value="'.$min.'"'.(($min == $smin) ? ' selected' : '').'>'.$min.(empty($conf->dol_optimize_smallscreen) ? '' : '').'</option>';
5900
			}
5901
			$retstring .= '</select>';
5902
5903
			$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...
5904
		}
5905
5906
		if ($d && $h) {
5907
			$retstring .= '</span>';
5908
		}
5909
5910
		// Add a "Now" link
5911
		if ($conf->use_javascript_ajax && $addnowlink)
5912
		{
5913
			// Script which will be inserted in the onClick of the "Now" link
5914
			$reset_scripts = "";
5915
			if ($addnowlink == 2) // local computer time
5916
			{
5917
				// pad add leading 0 on numbers
5918
				$reset_scripts .= "Number.prototype.pad = function(size) {
5919
                        var s = String(this);
5920
                        while (s.length < (size || 2)) {s = '0' + s;}
5921
                        return s;
5922
                    };
5923
                    var d = new Date();";
5924
			}
5925
5926
			// Generate the date part, depending on the use or not of the javascript calendar
5927
			if ($addnowlink == 1) // server time expressed in user time setup
5928
			{
5929
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day', 'tzuser').'\');';
5930
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d', 'tzuser').'\');';
5931
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m', 'tzuser').'\');';
5932
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y', 'tzuser').'\');';
5933
			} elseif ($addnowlink == 2)
5934
			{
5935
				/* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
5936
            	 * This break application for foreign languages.
5937
                $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
5938
                $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
5939
                $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
5940
                $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
5941
                */
5942
				$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day', 'tzuser').'\');';
5943
				$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d', 'tzuser').'\');';
5944
				$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m', 'tzuser').'\');';
5945
				$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y', 'tzuser').'\');';
5946
			}
5947
			/*if ($usecalendar == "eldy")
5948
            {
5949
                $base=DOL_URL_ROOT.'/core/';
5950
                $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
5951
            }
5952
            else
5953
            {
5954
                $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
5955
                $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
5956
                $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
5957
            }*/
5958
			// Update the hour part
5959
			if ($h)
5960
			{
5961
				if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
5962
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
5963
				if ($addnowlink == 1)
5964
				{
5965
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H', 'tzuser').'\');';
5966
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
5967
				} elseif ($addnowlink == 2)
5968
				{
5969
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());';
5970
					$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').change();';
5971
				}
5972
5973
				if ($fullday) $reset_scripts .= ' } ';
5974
			}
5975
			// Update the minute part
5976
			if ($m)
5977
			{
5978
				if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
5979
				//$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
5980
				if ($addnowlink == 1)
5981
				{
5982
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M', 'tzuser').'\');';
5983
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
5984
				} elseif ($addnowlink == 2)
5985
				{
5986
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());';
5987
					$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').change();';
5988
				}
5989
				if ($fullday) $reset_scripts .= ' } ';
5990
			}
5991
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
5992
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen))
5993
			{
5994
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonNow" type="button" name="_useless" value="now" onClick="'.$reset_scripts.'">';
5995
				$retstring .= $langs->trans("Now");
5996
				$retstring .= '</button> ';
5997
			}
5998
		}
5999
6000
		// Add a "Plus one hour" link
6001
		if ($conf->use_javascript_ajax && $addplusone)
6002
		{
6003
			// Script which will be inserted in the onClick of the "Add plusone" link
6004
			$reset_scripts = "";
6005
6006
			// Generate the date part, depending on the use or not of the javascript calendar
6007
			$reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day').'\');';
6008
			$reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d').'\');';
6009
			$reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m').'\');';
6010
			$reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y').'\');';
6011
			// Update the hour part
6012
			if ($h)
6013
			{
6014
				if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6015
				$reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H').'\');';
6016
				if ($fullday) $reset_scripts .= ' } ';
6017
			}
6018
			// Update the minute part
6019
			if ($m)
6020
			{
6021
				if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
6022
				$reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M').'\');';
6023
				if ($fullday) $reset_scripts .= ' } ';
6024
			}
6025
			// If reset_scripts is not empty, print the link with the reset_scripts in the onClick
6026
			if ($reset_scripts && empty($conf->dol_optimize_smallscreen))
6027
			{
6028
				$retstring .= ' <button class="dpInvisibleButtons datenowlink" id="'.$prefix.'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="'.$reset_scripts.'">';
6029
				$retstring .= $langs->trans("DateStartPlusOne");
6030
				$retstring .= '</button> ';
6031
			}
6032
		}
6033
6034
		// Add a "Plus one hour" link
6035
		if ($conf->use_javascript_ajax && $adddateof)
6036
		{
6037
			$tmparray = dol_getdate($adddateof);
6038
			if (empty($labeladddateof)) $labeladddateof = $langs->trans("DateInvoice");
6039
			$retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="jQuery(\'#re\').val(\''.dol_print_date($adddateof, 'day').'\');jQuery(\'#reday\').val(\''.$tmparray['mday'].'\');jQuery(\'#remonth\').val(\''.$tmparray['mon'].'\');jQuery(\'#reyear\').val(\''.$tmparray['year'].'\');">'.$labeladddateof.'</a>';
6040
		}
6041
6042
		return $retstring;
6043
	}
6044
6045
	/**
6046
	 * selectTypeDuration
6047
	 *
6048
	 * @param   string   	$prefix     	Prefix
6049
	 * @param   string   	$selected   	Selected duration type
6050
	 * @param	array		$excludetypes	Array of duration types to exclude. Example array('y', 'm')
6051
	 * @return  string      	         	HTML select string
6052
	 */
6053
	public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
6054
	{
6055
		global $langs;
6056
6057
		$TDurationTypes = array(
6058
			'y'=>$langs->trans('Years'),
6059
			'm'=>$langs->trans('Month'),
6060
			'w'=>$langs->trans('Weeks'),
6061
			'd'=>$langs->trans('Days'),
6062
			'h'=>$langs->trans('Hours'),
6063
			'i'=>$langs->trans('Minutes')
6064
		);
6065
6066
		// Removed undesired duration types
6067
		foreach ($excludetypes as $value) {
6068
			unset($TDurationTypes[$value]);
6069
		}
6070
6071
		$retstring = '<select class="flat" id="select_'.$prefix.'type_duration" name="'.$prefix.'type_duration">';
6072
		foreach ($TDurationTypes as $key => $typeduration) {
6073
			$retstring .= '<option value="'.$key.'"';
6074
			if ($key == $selected) {
6075
				$retstring .= " selected";
6076
			}
6077
			$retstring .= ">".$typeduration."</option>";
6078
		}
6079
		$retstring .= "</select>";
6080
6081
		$retstring .= ajax_combobox('select_'.$prefix.'type_duration');
6082
6083
		return $retstring;
6084
	}
6085
6086
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6087
	/**
6088
	 *  Function to show a form to select a duration on a page
6089
	 *
6090
	 *	@param	string		$prefix   		Prefix for input fields
6091
	 *	@param  int			$iSecond  		Default preselected duration (number of seconds or '')
6092
	 * 	@param	int			$disabled       Disable the combo box
6093
	 * 	@param	string		$typehour		If 'select' then input hour and input min is a combo,
6094
	 *						            	If 'text' input hour is in text and input min is a text,
6095
	 *						            	If 'textselect' input hour is in text and input min is a combo
6096
	 *  @param	integer		$minunderhours	If 1, show minutes selection under the hours
6097
	 * 	@param	int			$nooutput		Do not output html string but return it
6098
	 *  @return	string|void
6099
	 */
6100
	public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
6101
	{
6102
		// phpcs:enable
6103
		global $langs;
6104
6105
		$retstring = '';
6106
6107
		$hourSelected = 0; $minSelected = 0;
6108
6109
		// Hours
6110
		if ($iSecond != '')
6111
		{
6112
			require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6113
6114
			$hourSelected = convertSecondToTime($iSecond, 'allhour');
6115
			$minSelected = convertSecondToTime($iSecond, 'min');
6116
		}
6117
6118
		if ($typehour == 'select') {
6119
			$retstring .= '<select class="flat" id="select_'.$prefix.'hour" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').'>';
6120
			for ($hour = 0; $hour < 25; $hour++)	// For a duration, we allow 24 hours
6121
			{
6122
				$retstring .= '<option value="'.$hour.'"';
6123
				if ($hourSelected == $hour)
6124
				{
6125
					$retstring .= " selected";
6126
				}
6127
				$retstring .= ">".$hour."</option>";
6128
			}
6129
			$retstring .= "</select>";
6130
		} elseif ($typehour == 'text' || $typehour == 'textselect') {
6131
			$retstring .= '<input placeholder="'.$langs->trans('HourShort').'" type="number" min="0" name="'.$prefix.'hour"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputhour" value="'.(($hourSelected != '') ? ((int) $hourSelected) : '').'">';
6132
		} else {
6133
			return 'BadValueForParameterTypeHour';
6134
		}
6135
6136
		if ($typehour != 'text') $retstring .= ' '.$langs->trans('HourShort');
6137
		else $retstring .= '<span class="hideonsmartphone">:</span>';
6138
6139
		// Minutes
6140
		if ($minunderhours) $retstring .= '<br>';
6141
		else $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
6142
6143
		if ($typehour == 'select' || $typehour == 'textselect')
6144
		{
6145
			$retstring .= '<select class="flat" id="select_'.$prefix.'min" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').'>';
6146
			for ($min = 0; $min <= 55; $min = $min + 5)
6147
			{
6148
				$retstring .= '<option value="'.$min.'"';
6149
				if ($minSelected == $min) $retstring .= ' selected';
6150
				$retstring .= '>'.$min.'</option>';
6151
			}
6152
			$retstring .= "</select>";
6153
		} elseif ($typehour == 'text')
6154
		{
6155
			$retstring .= '<input placeholder="'.$langs->trans('MinuteShort').'" type="number" min="0" name="'.$prefix.'min"'.($disabled ? ' disabled' : '').' class="flat maxwidth50 inputminute" value="'.(($minSelected != '') ? ((int) $minSelected) : '').'">';
6156
		}
6157
6158
		if ($typehour != 'text') $retstring .= ' '.$langs->trans('MinuteShort');
6159
6160
		//$retstring.="&nbsp;";
6161
6162
		if (!empty($nooutput)) return $retstring;
6163
6164
		print $retstring;
6165
		return;
6166
	}
6167
6168
6169
	/**
6170
	 * Generic method to select a component from a combo list.
6171
	 * Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.
6172
	 * This is the generic method that will replace all specific existing methods.
6173
	 *
6174
	 * @param 	string			$objectdesc			ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]
6175
	 * @param	string			$htmlname			Name of HTML select component
6176
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6177
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6178
	 * @param	string			$searchkey			Search criteria
6179
	 * @param	string			$placeholder		Place holder
6180
	 * @param	string			$morecss			More CSS
6181
	 * @param	string			$moreparams			More params provided to ajax call
6182
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6183
	 * @param	int				$disabled			1=Html component is disabled
6184
	 * @param	string	        $selected_input_value	Value of preselected input text (for use with ajax)
6185
	 * @return	string								Return HTML string
6186
	 * @see selectForFormsList() select_thirdparty_list()
6187
	 */
6188
	public function selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '')
6189
	{
6190
		global $conf, $user;
6191
6192
		$objecttmp = null;
6193
6194
		$InfoFieldList = explode(":", $objectdesc);
6195
		$classname = $InfoFieldList[0];
6196
		$classpath = $InfoFieldList[1];
6197
		$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
6198
		$filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
6199
6200
		if (!empty($classpath))
6201
		{
6202
			dol_include_once($classpath);
6203
6204
			if ($classname && class_exists($classname))
6205
			{
6206
				$objecttmp = new $classname($this->db);
6207
				// Make some replacement
6208
				$sharedentities = getEntity(strtolower($classname));
6209
				$objecttmp->filter = str_replace(
6210
					array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
6211
					array($conf->entity, $sharedentities, $user->id),
6212
					$filter);
6213
			}
6214
		}
6215
		if (!is_object($objecttmp))
6216
		{
6217
			dol_syslog('Error bad setup of type for field '.$InfoFieldList, LOG_WARNING);
6218
			return 'Error bad setup of type for field '.join(',', $InfoFieldList);
6219
		}
6220
6221
		//var_dump($objecttmp->filter);
6222
		$prefixforautocompletemode = $objecttmp->element;
6223
		if ($prefixforautocompletemode == 'societe') $prefixforautocompletemode = 'company';
6224
		if ($prefixforautocompletemode == 'product') $prefixforautocompletemode = 'produit';
6225
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6226
6227
		dol_syslog(get_class($this)."::selectForForms object->filter=".$objecttmp->filter, LOG_DEBUG);
6228
		$out = '';
6229
		if (!empty($conf->use_javascript_ajax) && !empty($conf->global->$confkeyforautocompletemode) && !$forcecombo)
6230
		{
6231
			// No immediate load of all database
6232
			$placeholder = '';
6233
			if ($preselectedvalue && empty($selected_input_value))
6234
			{
6235
				$objecttmp->fetch($preselectedvalue);
6236
				$selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
6237
				//unset($objecttmp);
6238
			}
6239
6240
			$objectdesc = $classname.':'.$classpath.':'.$addcreatebuttonornot.':'.$filter;
6241
			$urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php';
6242
6243
			// No immediate load of all database
6244
			$urloption = 'htmlname='.$htmlname.'&outjson=1&objectdesc='.$objectdesc.'&filter='.urlencode($objecttmp->filter);
6245
			// Activate the auto complete using ajax call.
6246
			$out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
6247
			$out .= '<style type="text/css">.ui-autocomplete { z-index: 250; }</style>';
6248
			if ($placeholder) $placeholder = ' placeholder="'.$placeholder.'"';
6249
			$out .= '<input type="text" class="'.$morecss.'"'.($disabled ? ' disabled="disabled"' : '').' name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' />';
6250
		} else {
6251
			// Immediate load of table record. Note: filter is inside $objecttmp->filter
6252
			$out .= $this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled);
6253
		}
6254
6255
		return $out;
6256
	}
6257
6258
	/**
6259
	 * Function to forge a SQL criteria
6260
	 *
6261
	 * @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"
6262
	 * @return string                  Forged criteria. Example: "t.field like 'abc%'"
6263
	 */
6264
	protected static function forgeCriteriaCallback($matches)
6265
	{
6266
		global $db;
6267
6268
		//dol_syslog("Convert matches ".$matches[1]);
6269
		if (empty($matches[1])) return '';
6270
		$tmp = explode(':', $matches[1]);
6271
		if (count($tmp) < 3) return '';
6272
6273
		$tmpescaped = $tmp[2];
6274
		$regbis = array();
6275
		if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis))
6276
		{
6277
			$tmpescaped = "'".$db->escape($regbis[1])."'";
6278
		} else {
6279
			$tmpescaped = $db->escape($tmpescaped);
6280
		}
6281
		return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped;
6282
	}
6283
6284
	/**
6285
	 * Output html form to select an object.
6286
	 * Note, this function is called by selectForForms or by ajax selectobject.php
6287
	 *
6288
	 * @param 	Object			$objecttmp			Object to knwo the table to scan for combo.
6289
	 * @param	string			$htmlname			Name of HTML select component
6290
	 * @param	int				$preselectedvalue	Preselected value (ID of element)
6291
	 * @param	string			$showempty			''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
6292
	 * @param	string			$searchkey			Search value
6293
	 * @param	string			$placeholder		Place holder
6294
	 * @param	string			$morecss			More CSS
6295
	 * @param	string			$moreparams			More params provided to ajax call
6296
	 * @param	int				$forcecombo			Force to load all values and output a standard combobox (with no beautification)
6297
	 * @param	int				$outputmode			0=HTML select string, 1=Array
6298
	 * @param	int				$disabled			1=Html component is disabled
6299
	 * @return	string|array						Return HTML string
6300
	 * @see selectForForms()
6301
	 */
6302
	public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0)
6303
	{
6304
		global $conf, $langs, $user, $hookmanager;
6305
6306
		//print "$objecttmp->filter, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled";
6307
6308
		$prefixforautocompletemode = $objecttmp->element;
6309
		if ($prefixforautocompletemode == 'societe') $prefixforautocompletemode = 'company';
6310
		$confkeyforautocompletemode = strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
6311
6312
		if (!empty($objecttmp->fields))	// For object that declare it, it is better to use declared fields (like societe, contact, ...)
6313
		{
6314
			$tmpfieldstoshow = '';
6315
			foreach ($objecttmp->fields as $key => $val)
6316
			{
6317
				if (!dol_eval($val['enabled'], 1, 1)) continue;
6318
				if ($val['showoncombobox']) $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '').'t.'.$key;
6319
			}
6320
			if ($tmpfieldstoshow) $fieldstoshow = $tmpfieldstoshow;
6321
		} else {
6322
			// For backward compatibility
6323
			$objecttmp->fields['ref'] = array('type'=>'varchar(30)', 'label'=>'Ref', 'showoncombobox'=>1);
6324
		}
6325
6326
		if (empty($fieldstoshow))
6327
		{
6328
			if (isset($objecttmp->fields['ref'])) {
6329
				$fieldstoshow = 't.ref';
6330
			} else {
6331
				$langs->load("errors");
6332
				$this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
6333
				return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
6334
			}
6335
		}
6336
6337
		$out = '';
6338
		$outarray = array();
6339
6340
		$num = 0;
6341
6342
		// Search data
6343
		$sql = "SELECT t.rowid, ".$fieldstoshow." FROM ".MAIN_DB_PREFIX.$objecttmp->table_element." as t";
6344
		if (isset($objecttmp->ismultientitymanaged) && !is_numeric($objecttmp->ismultientitymanaged)) {
6345
			$tmparray = explode('@', $objecttmp->ismultientitymanaged);
6346
			$sql .= ' INNER JOIN '.MAIN_DB_PREFIX.$tmparray[1].' as parenttable ON parenttable.rowid = t.'.$tmparray[0];
6347
		}
6348
		if ($objecttmp->ismultientitymanaged == 'fk_soc@societe')
6349
			if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
6350
6351
		// Add where from hooks
6352
        $parameters = array();
6353
        $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
6354
        if (!empty($hookmanager->resPrint)) $sql .= $hookmanager->resPrint;
6355
		else {
6356
		    $sql .= " WHERE 1=1";
6357
            if (isset($objecttmp->ismultientitymanaged) && $objecttmp->ismultientitymanaged == 1) $sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
6358
            if (isset($objecttmp->ismultientitymanaged) && !is_numeric($objecttmp->ismultientitymanaged)) {
6359
                $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...
6360
            }
6361
            if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
6362
                if ($objecttmp->element == 'societe') $sql .= " AND t.rowid = ".$user->socid;
6363
                else $sql .= " AND t.fk_soc = ".$user->socid;
6364
            }
6365
            if ($searchkey != '') $sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
6366
            if ($objecttmp->ismultientitymanaged == 'fk_soc@societe') {
6367
                if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = ".$user->id;
6368
            }
6369
            if ($objecttmp->filter) {	 // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
6370
                /*if (! DolibarrApi::_checkFilters($objecttmp->filter))
6371
                {
6372
                    throw new RestException(503, 'Error when validating parameter sqlfilters '.$objecttmp->filter);
6373
                }*/
6374
                $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
6375
                $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'Form::forgeCriteriaCallback', $objecttmp->filter).")";
6376
            }
6377
        }
6378
		$sql .= $this->db->order($fieldstoshow, "ASC");
6379
		//$sql.=$this->db->plimit($limit, 0);
6380
		//print $sql;
6381
6382
		// Build output string
6383
		$resql = $this->db->query($sql);
6384
		if ($resql)
6385
		{
6386
			// Construct $out and $outarray
6387
			$out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').($moreparams ? ' '.$moreparams : '').' name="'.$htmlname.'">'."\n";
6388
6389
			// 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
6390
			$textifempty = '&nbsp;';
6391
6392
			//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
6393
			if (!empty($conf->global->$confkeyforautocompletemode))
6394
			{
6395
				if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
6396
				else $textifempty .= $langs->trans("All");
6397
			}
6398
			if ($showempty) $out .= '<option value="-1">'.$textifempty.'</option>'."\n";
6399
6400
			$num = $this->db->num_rows($resql);
6401
			$i = 0;
6402
			if ($num)
6403
			{
6404
				while ($i < $num)
6405
				{
6406
					$obj = $this->db->fetch_object($resql);
6407
					$label = '';
6408
					$tmparray = explode(',', $fieldstoshow);
6409
					foreach ($tmparray as $key => $val)
6410
					{
6411
						$val = preg_replace('/t\./', '', $val);
6412
						$label .= (($label && $obj->$val) ? ' - ' : '').$obj->$val;
6413
					}
6414
					if (empty($outputmode))
6415
					{
6416
						if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
6417
							$out .= '<option value="'.$obj->rowid.'" selected>'.$label.'</option>';
6418
						} else {
6419
							$out .= '<option value="'.$obj->rowid.'">'.$label.'</option>';
6420
						}
6421
					} else {
6422
						array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label));
6423
					}
6424
6425
					$i++;
6426
					if (($i % 10) == 0) $out .= "\n";
6427
				}
6428
			}
6429
6430
			$out .= '</select>'."\n";
6431
6432
			if (!$forcecombo) {
6433
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6434
				$out .= ajax_combobox($htmlname, null, $conf->global->$confkeyforautocompletemode);
6435
			}
6436
		} else {
6437
			dol_print_error($this->db);
6438
		}
6439
6440
		$this->result = array('nbofelement'=>$num);
6441
6442
		if ($outputmode) return $outarray;
6443
		return $out;
6444
	}
6445
6446
6447
	/**
6448
	 *	Return a HTML select string, built from an array of key+value.
6449
	 *  Note: Do not apply langs->trans function on returned content, content may be entity encoded twice.
6450
	 *
6451
	 *	@param	string			$htmlname			Name of html select area. Must start with "multi" if this is a multiselect
6452
	 *	@param	array			$array				Array like array(key => value) or array(key=>array('label'=>..., 'data-...'=>..., 'disabled'=>..., 'css'=>...))
6453
	 *	@param	string|string[]	$id					Preselected key or preselected keys for multiselect
6454
	 *	@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.
6455
	 *	@param	int				$key_in_label		1 to show key into label with format "[key] value"
6456
	 *	@param	int				$value_as_key		1 to use value as key
6457
	 *	@param  string			$moreparam			Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
6458
	 *	@param  int				$translate			1=Translate and encode value
6459
	 * 	@param	int				$maxlen				Length maximum for labels
6460
	 * 	@param	int				$disabled			Html select box is disabled
6461
	 *  @param	string			$sort				'ASC' or 'DESC' = Sort on label, '' or 'NONE' or 'POS' = Do not sort, we keep original order
6462
	 *  @param	string			$morecss			Add more class to css styles
6463
	 *  @param	int				$addjscombo			Add js combo
6464
	 *  @param  string          $moreparamonempty	Add more param on the empty option line. Not used if show_empty not set
6465
	 *  @param  int             $disablebademail	1=Check if a not valid email, 2=Check string '---', and if found into value, disable and colorize entry
6466
	 *  @param  int             $nohtmlescape		No html escaping.
6467
	 * 	@return	string								HTML select string.
6468
	 *  @see multiselectarray(), selectArrayAjax(), selectArrayFilter()
6469
	 */
6470
	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)
6471
	{
6472
		global $conf, $langs;
6473
6474
		// Do we want a multiselect ?
6475
		//$jsbeautify = 0;
6476
		//if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
6477
		$jsbeautify = 1;
6478
6479
		if ($value_as_key) $array = array_combine($array, $array);
6480
6481
		$out = '';
6482
6483
		if ($addjscombo < 0) {
6484
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $addjscombo = 1;
6485
			else $addjscombo = 0;
6486
		}
6487
6488
		// Add code for jquery to use multiselect
6489
		if ($addjscombo && $jsbeautify) {
6490
			// Enhance with select2
6491
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6492
			$out .= ajax_combobox($htmlname);
6493
		}
6494
6495
		$out .= '<select id="'.preg_replace('/^\./', '', $htmlname).'" '.($disabled ? 'disabled="disabled" ' : '').'class="flat '.(preg_replace('/^\./', '', $htmlname)).($morecss ? ' '.$morecss : '').'"';
6496
		$out .= ' name="'.preg_replace('/^\./', '', $htmlname).'" '.($moreparam ? $moreparam : '');
6497
		$out .= '>';
6498
6499
		if ($show_empty) {
6500
			$textforempty = ' ';
6501
			if (!empty($conf->use_javascript_ajax)) $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
6502
			if (!is_numeric($show_empty)) $textforempty = $show_empty;
6503
			$out .= '<option class="optiongrey" '.($moreparamonempty ? $moreparamonempty.' ' : '').'value="'.($show_empty < 0 ? $show_empty : -1).'"'.($id == $show_empty ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
6504
		}
6505
6506
		if (is_array($array)) {
6507
			// Translate
6508
			if ($translate)
6509
			{
6510
				foreach ($array as $key => $value)
6511
				{
6512
					if (!is_array($value)) $array[$key] = $langs->trans($value);
6513
					else $array[$key]['label'] = $langs->trans($value['label']);
6514
				}
6515
			}
6516
6517
			// Sort
6518
			if ($sort == 'ASC') asort($array);
6519
			elseif ($sort == 'DESC') arsort($array);
6520
6521
			foreach ($array as $key => $tmpvalue)
6522
			{
6523
				if (is_array($tmpvalue)) {
6524
					$value = $tmpvalue['label'];
6525
					$disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
6526
					$style = empty($tmpvalue['css']) ? ' class="'.$tmpvalue['css'].'"' : '';
6527
				} else {
6528
					$value = $tmpvalue;
6529
					$disabled = ''; $style = '';
6530
				}
6531
				if (!empty($disablebademail)) {
6532
					if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
6533
						|| ($disablebademail == 2 && preg_match('/---/', $value)))
6534
					{
6535
						$disabled = ' disabled';
6536
						$style = ' class="warning"';
6537
					}
6538
				}
6539
6540
				if ($key_in_label) {
6541
					if (empty($nohtmlescape)) $selectOptionValue = dol_escape_htmltag($key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value));
6542
					else $selectOptionValue = $key.' - '.($maxlen ?dol_trunc($value, $maxlen) : $value);
6543
				} else {
6544
					if (empty($nohtmlescape)) $selectOptionValue = dol_escape_htmltag($maxlen ?dol_trunc($value, $maxlen) : $value);
6545
					else $selectOptionValue = $maxlen ?dol_trunc($value, $maxlen) : $value;
6546
					if ($value == '' || $value == '-') $selectOptionValue = '&nbsp;';
6547
				}
6548
6549
				$out .= '<option value="'.$key.'"';
6550
				$out .= $style.$disabled;
6551
				if (is_array($id)) {
6552
					if (in_array($key, $id) && !$disabled) $out .= ' selected'; // To preselect a value
6553
				} else {
6554
					$id = (string) $id; // if $id = 0, then $id = '0'
6555
					if ($id != '' && $id == $key && !$disabled) $out .= ' selected'; // To preselect a value
6556
				}
6557
				if ($nohtmlescape) $out .= ' data-html="'.dol_escape_htmltag($selectOptionValue).'"';
6558
				if (is_array($tmpvalue))
6559
				{
6560
					foreach ($tmpvalue as $keyforvalue => $valueforvalue)
6561
					{
6562
						if (preg_match('/^data-/', $keyforvalue)) $out .= ' '.$keyforvalue.'="'.$valueforvalue.'"';
6563
					}
6564
				}
6565
				$out .= '>';
6566
				//var_dump($selectOptionValue);
6567
				$out .= $selectOptionValue;
6568
				$out .= "</option>\n";
6569
			}
6570
		}
6571
6572
		$out .= "</select>";
6573
		return $out;
6574
	}
6575
6576
6577
	/**
6578
	 *	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.
6579
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
6580
	 *
6581
	 *	@param	string	$htmlname       		Name of html select area
6582
	 *	@param	string	$url					Url. Must return a json_encode of array(key=>array('text'=>'A text', 'url'=>'An url'), ...)
6583
	 *	@param	string	$id             		Preselected key
6584
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
6585
	 *	@param  string	$moreparamtourl 		Add more parameters onto the Ajax called URL
6586
	 * 	@param	int		$disabled				Html select box is disabled
6587
	 *  @param	int		$minimumInputLength		Minimum Input Length
6588
	 *  @param	string	$morecss				Add more class to css styles
6589
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
6590
	 *  @param  string  $placeholder            String to use as placeholder
6591
	 *  @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)
6592
	 * 	@return	string   						HTML select string
6593
	 *  @see selectArrayFilter(), ajax_combobox() in ajax.lib.php
6594
	 */
6595
	public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
6596
	{
6597
		global $conf, $langs;
6598
		global $delayedhtmlcontent;
6599
6600
		// TODO Use an internal dolibarr component instead of select2
6601
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) return '';
6602
6603
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"></select>';
6604
6605
		$tmpplugin = 'select2';
6606
		$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
6607
	    	<script>
6608
	    	$(document).ready(function () {
6609
6610
    	        '.($callurlonselect ? 'var saveRemoteData = [];' : '').'
6611
6612
                $(".'.$htmlname.'").select2({
6613
			    	ajax: {
6614
				    	dir: "ltr",
6615
				    	url: "'.$url.'",
6616
				    	dataType: \'json\',
6617
				    	delay: 250,
6618
				    	data: function (params) {
6619
				    		return {
6620
						    	q: params.term, 	// search term
6621
				    			page: params.page
6622
				    		};
6623
			    		},
6624
			    		processResults: function (data) {
6625
			    			// parse the results into the format expected by Select2.
6626
			    			// since we are using custom formatting functions we do not need to alter the remote JSON data
6627
			    			//console.log(data);
6628
							saveRemoteData = data;
6629
				    	    /* format json result for select2 */
6630
				    	    result = []
6631
				    	    $.each( data, function( key, value ) {
6632
				    	       result.push({id: key, text: value.text});
6633
                            });
6634
			    			//return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
6635
			    			//console.log(result);
6636
			    			return {results: result, more: false}
6637
			    		},
6638
			    		cache: true
6639
			    	},
6640
	 				language: select2arrayoflanguage,
6641
					containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
6642
				    placeholder: "'.dol_escape_js($placeholder).'",
6643
			    	escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
6644
			    	minimumInputLength: '.$minimumInputLength.',
6645
			        formatResult: function(result, container, query, escapeMarkup) {
6646
                        return escapeMarkup(result.text);
6647
                    },
6648
			    });
6649
6650
                '.($callurlonselect ? '
6651
                /* Code to execute a GET when we select a value */
6652
                $(".'.$htmlname.'").change(function() {
6653
			    	var selected = $(".'.$htmlname.'").val();
6654
                	console.log("We select in selectArrayAjax the entry "+selected)
6655
			        $(".'.$htmlname.'").val("");  /* reset visible combo value */
6656
    			    $.each( saveRemoteData, function( key, value ) {
6657
    				        if (key == selected)
6658
    			            {
6659
    			                 console.log("selectArrayAjax - Do a redirect to "+value.url)
6660
    			                 location.assign(value.url);
6661
    			            }
6662
                    });
6663
    			});' : '').'
6664
6665
    	   });
6666
	       </script>';
6667
6668
		if ($acceptdelayedhtml)
6669
		{
6670
			$delayedhtmlcontent .= $outdelayed;
6671
		} else {
6672
			$out .= $outdelayed;
6673
		}
6674
		return $out;
6675
	}
6676
6677
	/**
6678
	 *  Return a HTML select string, built from an array of key+value, but content returned into select is defined into $array parameter.
6679
	 *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
6680
	 *
6681
	 *  @param  string	$htmlname               Name of html select area
6682
	 *	@param	array	$array					Array (key=>array('text'=>'A text', 'url'=>'An url'), ...)
6683
	 *	@param	string	$id             		Preselected key
6684
	 *	@param  string	$moreparam      		Add more parameters onto the select tag
6685
	 *	@param	int		$disableFiltering		If set to 1, results are not filtered with searched string
6686
	 * 	@param	int		$disabled				Html select box is disabled
6687
	 *  @param	int		$minimumInputLength		Minimum Input Length
6688
	 *  @param	string	$morecss				Add more class to css styles
6689
	 *  @param  int     $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
6690
	 *  @param  string  $placeholder            String to use as placeholder
6691
	 *  @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)
6692
	 *  @return	string   						HTML select string
6693
	 *  @see selectArrayAjax(), ajax_combobox() in ajax.lib.php
6694
	 */
6695
	public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
6696
	{
6697
		global $conf, $langs;
6698
		global $delayedhtmlcontent;
6699
6700
		// TODO Use an internal dolibarr component instead of select2
6701
		if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) return '';
6702
6703
		$out = '<select type="text" class="'.$htmlname.($morecss ? ' '.$morecss : '').'" '.($moreparam ? $moreparam.' ' : '').'name="'.$htmlname.'"><option></option></select>';
6704
6705
		$formattedarrayresult = array();
6706
6707
		foreach ($array as $key => $value) {
6708
			$o = new stdClass();
6709
			$o->id = $key;
6710
			$o->text = $value['text'];
6711
			$o->url = $value['url'];
6712
			$formattedarrayresult[] = $o;
6713
		}
6714
6715
		$tmpplugin = 'select2';
6716
		$outdelayed = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
6717
			<script>
6718
			$(document).ready(function () {
6719
				var data = '.json_encode($formattedarrayresult).';
6720
6721
				'.($callurlonselect ? 'var saveRemoteData = '.json_encode($array).';' : '').'
6722
6723
				$(".'.$htmlname.'").select2({
6724
					data: data,
6725
					language: select2arrayoflanguage,
6726
					containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
6727
					placeholder: "'.dol_escape_js($placeholder).'",
6728
					escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
6729
					minimumInputLength: '.$minimumInputLength.',
6730
					formatResult: function(result, container, query, escapeMarkup) {
6731
						return escapeMarkup(result.text);
6732
					},
6733
					matcher: function (params, data) {
6734
6735
						if(! data.id) return null;';
6736
6737
		if ($callurlonselect) {
6738
			$outdelayed .= '
6739
6740
						var urlBase = data.url;
6741
						var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
6742
						/* console.log("params.term="+params.term); */
6743
						/* console.log("params.term encoded="+encodeURIComponent(params.term)); */
6744
						saveRemoteData[data.id].url = urlBase + separ + "sall=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
6745
		}
6746
6747
		if (!$disableFiltering) {
6748
			$outdelayed .= '
6749
6750
						if(data.text.match(new RegExp(params.term))) {
6751
							return data;
6752
						}
6753
6754
						return null;';
6755
		} else {
6756
			$outdelayed .= '
6757
6758
						return data;';
6759
		}
6760
6761
		$outdelayed .= '
6762
					}
6763
				});
6764
6765
				'.($callurlonselect ? '
6766
				/* Code to execute a GET when we select a value */
6767
				$(".'.$htmlname.'").change(function() {
6768
					var selected = $(".'.$htmlname.'").val();
6769
					console.log("We select "+selected)
6770
6771
					$(".'.$htmlname.'").val("");  /* reset visible combo value */
6772
					$.each( saveRemoteData, function( key, value ) {
6773
						if (key == selected)
6774
						{
6775
							console.log("selectArrayFilter - Do a redirect to "+value.url)
6776
							location.assign(value.url);
6777
						}
6778
					});
6779
				});' : '').'
6780
6781
			});
6782
			</script>';
6783
6784
		if ($acceptdelayedhtml) {
6785
			$delayedhtmlcontent .= $outdelayed;
6786
		} else {
6787
			$out .= $outdelayed;
6788
		}
6789
		return $out;
6790
	}
6791
6792
	/**
6793
	 *	Show a multiselect form from an array.
6794
	 *
6795
	 *	@param	string	$htmlname		Name of select
6796
	 *	@param	array	$array			Array with key+value
6797
	 *	@param	array	$selected		Array with key+value preselected
6798
	 *	@param	int		$key_in_label   1 to show key like in "[key] value"
6799
	 *	@param	int		$value_as_key   1 to use value as key
6800
	 *	@param  string	$morecss        Add more css style
6801
	 *	@param  int		$translate		Translate and encode value
6802
	 *  @param	int		$width			Force width of select box. May be used only when using jquery couch. Example: 250, 95%
6803
	 *  @param	string	$moreattrib		Add more options on select component. Example: 'disabled'
6804
	 *  @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.
6805
	 *  @param	string	$placeholder	String to use as placeholder
6806
	 *  @param	int		$addjscombo		Add js combo
6807
	 *	@return	string					HTML multiselect string
6808
	 *  @see selectarray(), selectArrayAjax(), selectArrayFilter()
6809
	 */
6810
	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)
6811
	{
6812
		global $conf, $langs;
6813
6814
		$out = '';
6815
6816
		if ($addjscombo < 0) {
6817
			if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $addjscombo = 1;
6818
			else $addjscombo = 0;
6819
		}
6820
6821
		// Add code for jquery to use multiselect
6822
		if (!empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
6823
			$out .= "\n".'<!-- JS CODE TO ENABLE select for id '.$htmlname.', addjscombo='.$addjscombo.' -->';
6824
			$out .= "\n".'<script>'."\n";
6825
			if ($addjscombo == 1) {
6826
				$tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
6827
				$out .= 'function formatResult(record) {'."\n";
6828
				if ($elemtype == 'category') {
6829
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
6830
				} else {
6831
					$out .= 'return record.text;';
6832
				}
6833
				$out .= '};'."\n";
6834
				$out .= 'function formatSelection(record) {'."\n";
6835
				if ($elemtype == 'category') {
6836
					$out .= 'return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
6837
				} else {
6838
					$out .= 'return record.text;';
6839
				}
6840
				$out .= '};'."\n";
6841
				$out .= '$(document).ready(function () {
6842
							$(\'#'.$htmlname.'\').'.$tmpplugin.'({
6843
								dir: \'ltr\',
6844
								// Specify format function for dropdown item
6845
								formatResult: formatResult,
6846
							 	templateResult: formatResult,		/* For 4.0 */
6847
								// Specify format function for selected item
6848
								formatSelection: formatSelection,
6849
							 	templateSelection: formatSelection		/* For 4.0 */
6850
							});
6851
6852
							/* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
6853
								 the size only if component is not hidden by default on load */
6854
							$(\'#'.$htmlname.' + .select2\').addClass(\''.$morecss.'\');
6855
						});'."\n";
6856
			} elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
6857
				// Add other js lib
6858
				// TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
6859
				// ...
6860
				$out .= 'console.log(\'addjscombo=2 for htmlname='.$htmlname.'\');';
6861
				$out .= '$(document).ready(function () {
6862
							$(\'#'.$htmlname.'\').multiSelect({
6863
								containerHTML: \'<div class="multi-select-container">\',
6864
								menuHTML: \'<div class="multi-select-menu">\',
6865
								buttonHTML: \'<span class="multi-select-button '.$morecss.'">\',
6866
								menuItemHTML: \'<label class="multi-select-menuitem">\',
6867
								activeClass: \'multi-select-container--open\',
6868
								noneText: \''.$placeholder.'\'
6869
							});
6870
						})';
6871
			}
6872
			$out .= '</script>';
6873
		}
6874
6875
		// Try also magic suggest
6876
		$out .= '<select id="'.$htmlname.'" class="multiselect'.($morecss ? ' '.$morecss : '').'" multiple name="'.$htmlname.'[]"'.($moreattrib ? ' '.$moreattrib : '').($width ? ' style="width: '.(preg_match('/%/', $width) ? $width : $width.'px').'"' : '').'>'."\n";
6877
		if (is_array($array) && !empty($array)) {
6878
			if ($value_as_key) {
6879
				$array = array_combine($array, $array);
6880
			}
6881
6882
			if (!empty($array)) {
6883
				foreach ($array as $key => $value) {
6884
					$newval = ($translate ? $langs->trans($value) : $value);
6885
					$newval = ($key_in_label ? $key.' - '.$newval : $newval);
6886
6887
					$out .= '<option value="'.$key.'"';
6888
					if (is_array($selected) && !empty($selected) && in_array((string) $key, $selected) && ((string) $key != '')) {
6889
						$out .= ' selected';
6890
					}
6891
					$out .= ' data-html="'.dol_escape_htmltag($newval).'"';
6892
					$out .= '>';
6893
					$out .= dol_htmlentitiesbr($newval);
6894
					$out .= '</option>'."\n";
6895
				}
6896
			}
6897
		}
6898
		$out .= '</select>'."\n";
6899
6900
		return $out;
6901
	}
6902
6903
6904
	/**
6905
	 *	Show a multiselect dropbox from an array.
6906
	 *
6907
	 *	@param	string	$htmlname		Name of HTML field
6908
	 *	@param	array	$array			Array with array of fields we could show. This array may be modified according to setup of user.
6909
	 *  @param  string  $varpage        Id of context for page. Can be set by caller with $varpage=(empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage);
6910
	 *	@return	string					HTML multiselect string
6911
	 *  @see selectarray()
6912
	 */
6913
	public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage)
6914
	{
6915
		global $conf, $langs, $user, $extrafields;
6916
6917
		if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
6918
			return '';
6919
		}
6920
6921
		$tmpvar = "MAIN_SELECTEDFIELDS_".$varpage; // To get list of saved seleteced properties
6922
		if (!empty($user->conf->$tmpvar)) {
6923
			$tmparray = explode(',', $user->conf->$tmpvar);
6924
			foreach ($array as $key => $val) {
6925
				//var_dump($key);
6926
				//var_dump($tmparray);
6927
				if (in_array($key, $tmparray)) {
6928
					$array[$key]['checked'] = 1;
6929
				} else {
6930
					$array[$key]['checked'] = 0;
6931
				}
6932
			}
6933
		}
6934
6935
		$lis = '';
6936
		$listcheckedstring = '';
6937
6938
		foreach ($array as $key => $val) {
6939
			/* var_dump($val);
6940
            var_dump(array_key_exists('enabled', $val));
6941
            var_dump(!$val['enabled']);*/
6942
			if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
6943
				unset($array[$key]); // We don't want this field
6944
				continue;
6945
			}
6946
			if ($val['label']) {
6947
				if (!empty($val['langfile']) && is_object($langs)) {
6948
					$langs->load($val['langfile']);
6949
				}
6950
6951
				$lis .= '<li><input type="checkbox" id="checkbox'.$key.'" value="'.$key.'"'.(empty($val['checked']) ? '' : ' checked="checked"').'/><label for="checkbox'.$key.'">'.dol_escape_htmltag($langs->trans($val['label'])).'</label></li>';
6952
				$listcheckedstring .= (empty($val['checked']) ? '' : $key.',');
6953
			}
6954
		}
6955
6956
		$out = '<!-- Component multiSelectArrayWithCheckbox '.$htmlname.' -->
6957
6958
        <dl class="dropdown">
6959
            <dt>
6960
            <a href="#'.$htmlname.'">
6961
              '.img_picto('', 'list').'
6962
            </a>
6963
            <input type="hidden" class="'.$htmlname.'" name="'.$htmlname.'" value="'.$listcheckedstring.'">
6964
            </dt>
6965
            <dd class="dropdowndd">
6966
                <div class="multiselectcheckbox'.$htmlname.'">
6967
                    <ul class="ul'.$htmlname.'">
6968
                    '.$lis.'
6969
                    </ul>
6970
                </div>
6971
            </dd>
6972
        </dl>
6973
6974
        <script type="text/javascript">
6975
          jQuery(document).ready(function () {
6976
              $(\'.multiselectcheckbox'.$htmlname.' input[type="checkbox"]\').on(\'click\', function () {
6977
                  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
6978
6979
                  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\');	// Update field so we know we changed something on selected fields after POST
6980
6981
                  var title = $(this).val() + ",";
6982
                  if ($(this).is(\':checked\')) {
6983
                      $(\'.'.$htmlname.'\').val(title + $(\'.'.$htmlname.'\').val());
6984
                  }
6985
                  else {
6986
                      $(\'.'.$htmlname.'\').val( $(\'.'.$htmlname.'\').val().replace(title, \'\') )
6987
                  }
6988
                  // Now, we submit page
6989
                  //$(this).parents(\'form:first\').submit();
6990
              });
6991
6992
6993
           });
6994
        </script>
6995
6996
        ';
6997
		return $out;
6998
	}
6999
7000
	/**
7001
	 * 	Render list of categories linked to object with id $id and type $type
7002
	 *
7003
	 * 	@param		int		$id				Id of object
7004
	 * 	@param		string	$type			Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
7005
	 *  @param		int		$rendermode		0=Default, use multiselect. 1=Emulate multiselect (recommended)
7006
	 *  @param		int		$nolink			1=Do not add html links
7007
	 * 	@return		string					String with categories
7008
	 */
7009
	public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
7010
	{
7011
		global $db;
7012
7013
		include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7014
7015
		$cat = new Categorie($db);
7016
		$categories = $cat->containing($id, $type);
7017
7018
		if ($rendermode == 1)
7019
		{
7020
			$toprint = array();
7021
			foreach ($categories as $c)
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7022
			{
7023
				$ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text
7024
				foreach ($ways as $way)
7025
				{
7026
					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.$way.'</li>';
7027
				}
7028
			}
7029
			return '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7030
		}
7031
7032
		if ($rendermode == 0)
7033
		{
7034
			$arrayselected = array();
7035
			$cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 1);
7036
			foreach ($categories as $c) {
0 ignored issues
show
Bug introduced by
The expression $categories of type integer is not traversable.
Loading history...
7037
				$arrayselected[] = $c->id;
7038
			}
7039
7040
			return $this->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, '', 0, '100%', 'disabled', 'category');
7041
		}
7042
7043
		return 'ErrorBadValueForParameterRenderMode'; // Should not happened
7044
	}
7045
7046
	/**
7047
	 *  Show linked object block.
7048
	 *
7049
	 *  @param	CommonObject	$object		      Object we want to show links to
7050
	 *  @param  string          $morehtmlright    More html to show on right of title
7051
	 *  @param  array           $compatibleImportElementsList  Array of compatibles elements object for "import from" action
7052
	 *  @return	int							      <0 if KO, >=0 if OK
7053
	 */
7054
	public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = false)
7055
	{
7056
		global $conf, $langs, $hookmanager;
7057
		global $bc, $action;
7058
7059
		$object->fetchObjectLinked();
7060
7061
		// Bypass the default method
7062
		$hookmanager->initHooks(array('commonobject'));
7063
		$parameters = array(
7064
			'morehtmlright' => $morehtmlright,
7065
			'compatibleImportElementsList' => &$compatibleImportElementsList,
7066
		);
7067
		$reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
7068
7069
		if (empty($reshook))
7070
		{
7071
			$nbofdifferenttypes = count($object->linkedObjects);
7072
7073
			print '<!-- showLinkedObjectBlock -->';
7074
			print load_fiche_titre($langs->trans('RelatedObjects'), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
7075
7076
7077
			print '<div class="div-table-responsive-no-min">';
7078
			print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="'.$object->element.'"  data-elementid="'.$object->id.'"   >';
7079
7080
			print '<tr class="liste_titre">';
7081
			print '<td>'.$langs->trans("Type").'</td>';
7082
			print '<td>'.$langs->trans("Ref").'</td>';
7083
			print '<td class="center"></td>';
7084
			print '<td class="center">'.$langs->trans("Date").'</td>';
7085
			print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
7086
			print '<td class="right">'.$langs->trans("Status").'</td>';
7087
			print '<td></td>';
7088
			print '</tr>';
7089
7090
			$nboftypesoutput = 0;
7091
7092
			foreach ($object->linkedObjects as $objecttype => $objects)
7093
			{
7094
				$tplpath = $element = $subelement = $objecttype;
7095
7096
				// to display inport button on tpl
7097
				$showImportButton = false;
7098
				if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
7099
					$showImportButton = true;
7100
				}
7101
7102
				$regs = array();
7103
				if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
7104
				{
7105
					$element = $regs[1];
7106
					$subelement = $regs[2];
7107
					$tplpath = $element.'/'.$subelement;
7108
				}
7109
				$tplname = 'linkedobjectblock';
7110
7111
				// To work with non standard path
7112
				if ($objecttype == 'facture') {
7113
					$tplpath = 'compta/'.$element;
7114
					if (empty($conf->facture->enabled)) continue; // Do not show if module disabled
7115
				} elseif ($objecttype == 'facturerec') {
7116
					$tplpath = 'compta/facture';
7117
					$tplname = 'linkedobjectblockForRec';
7118
					if (empty($conf->facture->enabled)) continue; // Do not show if module disabled
7119
				} elseif ($objecttype == 'propal') {
7120
					$tplpath = 'comm/'.$element;
7121
					if (empty($conf->propal->enabled)) continue; // Do not show if module disabled
7122
				} elseif ($objecttype == 'supplier_proposal') {
7123
					if (empty($conf->supplier_proposal->enabled)) continue; // Do not show if module disabled
7124
				} elseif ($objecttype == 'shipping' || $objecttype == 'shipment') {
7125
					$tplpath = 'expedition';
7126
					if (empty($conf->expedition->enabled)) continue; // Do not show if module disabled
7127
				} elseif ($objecttype == 'reception') {
7128
					$tplpath = 'reception';
7129
					if (empty($conf->reception->enabled)) continue; // Do not show if module disabled
7130
				} elseif ($objecttype == 'delivery') {
7131
					$tplpath = 'delivery';
7132
					if (empty($conf->expedition->enabled)) continue; // Do not show if module disabled
7133
				} elseif ($objecttype == 'invoice_supplier') {
7134
					$tplpath = 'fourn/facture';
7135
				} elseif ($objecttype == 'order_supplier') {
7136
					$tplpath = 'fourn/commande';
7137
				} elseif ($objecttype == 'expensereport') {
7138
					$tplpath = 'expensereport';
7139
				} elseif ($objecttype == 'subscription') {
7140
					$tplpath = 'adherents';
7141
				}
7142
7143
				global $linkedObjectBlock;
7144
				$linkedObjectBlock = $objects;
7145
7146
7147
				// Output template part (modules that overwrite templates must declare this into descriptor)
7148
				$dirtpls = array_merge($conf->modules_parts['tpl'], array('/'.$tplpath.'/tpl'));
7149
				foreach ($dirtpls as $reldir)
7150
				{
7151
					if ($nboftypesoutput == ($nbofdifferenttypes - 1))    // No more type to show after
7152
					{
7153
						global $noMoreLinkedObjectBlockAfter;
7154
						$noMoreLinkedObjectBlockAfter = 1;
7155
					}
7156
7157
					$res = @include dol_buildpath($reldir.'/'.$tplname.'.tpl.php');
7158
					if ($res)
7159
					{
7160
						$nboftypesoutput++;
7161
						break;
7162
					}
7163
				}
7164
			}
7165
7166
			if (!$nboftypesoutput)
7167
			{
7168
				print '<tr><td class="impair opacitymedium" colspan="7">'.$langs->trans("None").'</td></tr>';
7169
			}
7170
7171
			print '</table>';
7172
7173
			if (!empty($compatibleImportElementsList))
7174
			{
7175
				$res = @include dol_buildpath('core/tpl/ajax/objectlinked_lineimport.tpl.php');
7176
			}
7177
7178
7179
			print '</div>';
7180
7181
			return $nbofdifferenttypes;
7182
		}
7183
	}
7184
7185
	/**
7186
	 *  Show block with links to link to other objects.
7187
	 *
7188
	 *  @param	CommonObject	$object				Object we want to show links to
7189
	 *  @param	array			$restrictlinksto	Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
7190
	 *  @param	array			$excludelinksto		Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
7191
	 *  @return	string								<0 if KO, >0 if OK
7192
	 */
7193
	public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
7194
	{
7195
		global $conf, $langs, $hookmanager;
7196
		global $bc, $action;
7197
7198
		$linktoelem = '';
7199
		$linktoelemlist = '';
7200
		$listofidcompanytoscan = '';
7201
7202
		if (!is_object($object->thirdparty)) $object->fetch_thirdparty();
7203
7204
		$possiblelinks = array();
7205
		if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0)
7206
		{
7207
			$listofidcompanytoscan = $object->thirdparty->id;
7208
			if (($object->thirdparty->parent > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PARENT_IN_LINKTO)) $listofidcompanytoscan .= ','.$object->thirdparty->parent;
7209
			if (($object->fk_project > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO))
7210
			{
7211
				include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
7212
				$tmpproject = new Project($this->db);
7213
				$tmpproject->fetch($object->fk_project);
7214
				if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) $listofidcompanytoscan .= ','.$tmpproject->socid;
7215
				unset($tmpproject);
7216
			}
7217
7218
			$possiblelinks = array(
7219
				'propal'=>array('enabled'=>$conf->propal->enabled, 'perms'=>1, 'label'=>'LinkToProposal', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('propal').')'),
7220
				'order'=>array('enabled'=>$conf->commande->enabled, 'perms'=>1, 'label'=>'LinkToOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('commande').')'),
7221
				'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 as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('invoice').')'),
7222
				'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 as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture_rec as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('invoice').')'),
7223
				'contrat'=>array('enabled'=>$conf->contrat->enabled, 'perms'=>1, 'label'=>'LinkToContract',
7224
								'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, '' as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."contrat as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('contract').')'),
7225
				'fichinter'=>array('enabled'=>$conf->ficheinter->enabled, 'perms'=>1, 'label'=>'LinkToIntervention', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."fichinter as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('intervention').')'),
7226
				'supplier_proposal'=>array('enabled'=>$conf->supplier_proposal->enabled, 'perms'=>1, 'label'=>'LinkToSupplierProposal', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, '' as ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('supplier_proposal').')'),
7227
				'order_supplier'=>array('enabled'=>$conf->supplier_order->enabled, 'perms'=>1, 'label'=>'LinkToSupplierOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('commande_fournisseur').')'),
7228
				'invoice_supplier'=>array('enabled'=>$conf->supplier_invoice->enabled, 'perms'=>1, 'label'=>'LinkToSupplierInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('facture_fourn').')'),
7229
				'ticket'=>array('enabled'=>$conf->ticket->enabled, 'perms'=>1, 'label'=>'LinkToTicket', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.track_id, '0' as total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."ticket as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (".$listofidcompanytoscan.') AND t.entity IN ('.getEntity('ticket').')')
7230
			);
7231
		}
7232
7233
		// Can complete the possiblelink array
7234
		$hookmanager->initHooks(array('commonobject'));
7235
		$parameters = array('listofidcompanytoscan' => $listofidcompanytoscan);
7236
7237
		if (!empty($listofidcompanytoscan))  // If empty, we don't have criteria to scan the object we can link to
7238
		{
7239
			$reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
7240
		}
7241
7242
		if (empty($reshook))
7243
		{
7244
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray))
7245
			{
7246
				$possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
7247
			}
7248
		} 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...
7249
		{
7250
			if (is_array($hookmanager->resArray) && count($hookmanager->resArray))
7251
			{
7252
				$possiblelinks = $hookmanager->resArray;
7253
			}
7254
		}
7255
7256
		foreach ($possiblelinks as $key => $possiblelink)
7257
		{
7258
			$num = 0;
7259
7260
			if (empty($possiblelink['enabled'])) continue;
7261
7262
			if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto)))
7263
			{
7264
				print '<div id="'.$key.'list"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display:none"').'>';
7265
				$sql = $possiblelink['sql'];
7266
7267
				$resqllist = $this->db->query($sql);
7268
				if ($resqllist)
7269
				{
7270
					$num = $this->db->num_rows($resqllist);
7271
					$i = 0;
7272
7273
					print '<br>';
7274
					print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
7275
					print '<input type="hidden" name="action" value="addlink">';
7276
					print '<input type="hidden" name="token" value="'.newToken().'">';
7277
					print '<input type="hidden" name="id" value="'.$object->id.'">';
7278
					print '<input type="hidden" name="addlink" value="'.$key.'">';
7279
					print '<table class="noborder">';
7280
					print '<tr class="liste_titre">';
7281
					print '<td class="nowrap"></td>';
7282
					print '<td class="center">'.$langs->trans("Ref").'</td>';
7283
					print '<td class="left">'.$langs->trans("RefCustomer").'</td>';
7284
					print '<td class="right">'.$langs->trans("AmountHTShort").'</td>';
7285
					print '<td class="left">'.$langs->trans("Company").'</td>';
7286
					print '</tr>';
7287
					while ($i < $num)
7288
					{
7289
						$objp = $this->db->fetch_object($resqllist);
7290
7291
						print '<tr class="oddeven">';
7292
						print '<td class="left">';
7293
						print '<input type="radio" name="idtolinkto" value='.$objp->rowid.'>';
7294
						print '</td>';
7295
						print '<td class="center">'.$objp->ref.'</td>';
7296
						print '<td>'.$objp->ref_client.'</td>';
7297
						print '<td class="right">'.price($objp->total_ht).'</td>';
7298
						print '<td>'.$objp->name.'</td>';
7299
						print '</tr>';
7300
						$i++;
7301
					}
7302
					print '</table>';
7303
					print '<div class="center"><input type="submit" class="button valignmiddle" value="'.$langs->trans('ToLink').'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="submit" class="button button-cancel" name="cancel" value="'.$langs->trans("Cancel").'"></div>';
7304
7305
					print '</form>';
7306
					$this->db->free($resqllist);
7307
				} else {
7308
					dol_print_error($this->db);
7309
				}
7310
				print '</div>';
7311
				if ($num > 0)
7312
				{
7313
				}
7314
7315
				//$linktoelem.=($linktoelem?' &nbsp; ':'');
7316
				if ($num > 0) $linktoelemlist .= '<li><a href="#linkto'.$key.'" class="linkto dropdowncloseonclick" rel="'.$key.'">'.$langs->trans($possiblelink['label']).' ('.$num.')</a></li>';
7317
				//else $linktoelem.=$langs->trans($possiblelink['label']);
7318
				else $linktoelemlist .= '<li><span class="linktodisabled">'.$langs->trans($possiblelink['label']).' (0)</span></li>';
7319
			}
7320
		}
7321
7322
		if ($linktoelemlist)
7323
		{
7324
			$linktoelem = '
7325
    		<dl class="dropdown" id="linktoobjectname">
7326
    		';
7327
			if (!empty($conf->use_javascript_ajax)) $linktoelem .= '<dt><a href="#linktoobjectname">'.$langs->trans("LinkTo").'...</a></dt>';
7328
			$linktoelem .= '<dd>
7329
    		<div class="multiselectlinkto">
7330
    		<ul class="ulselectedfields">'.$linktoelemlist.'
7331
    		</ul>
7332
    		</div>
7333
    		</dd>
7334
    		</dl>';
7335
		} else {
7336
			$linktoelem = '';
7337
		}
7338
7339
		if (!empty($conf->use_javascript_ajax))
7340
		{
7341
			print '<!-- Add js to show linkto box -->
7342
				<script>
7343
				jQuery(document).ready(function() {
7344
					jQuery(".linkto").click(function() {
7345
						console.log("We choose to show/hide link for rel="+jQuery(this).attr(\'rel\'));
7346
					    jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
7347
					});
7348
				});
7349
				</script>
7350
		    ';
7351
		}
7352
7353
		return $linktoelem;
7354
	}
7355
7356
	/**
7357
	 *	Return an html string with a select combo box to choose yes or no
7358
	 *
7359
	 *	@param	string		$htmlname		Name of html select field
7360
	 *	@param	string		$value			Pre-selected value
7361
	 *	@param	int			$option			0 return yes/no, 1 return 1/0
7362
	 *	@param	bool		$disabled		true or false
7363
	 *  @param	int      	$useempty		1=Add empty line
7364
	 *  @param	int			$addjscombo		1=Add js beautifier on combo box
7365
	 *	@return	string						See option
7366
	 */
7367
	public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0)
7368
	{
7369
		global $langs;
7370
7371
		$yes = "yes"; $no = "no";
7372
		if ($option)
7373
		{
7374
			$yes = "1";
7375
			$no = "0";
7376
		}
7377
7378
		$disabled = ($disabled ? ' disabled' : '');
7379
7380
		$resultyesno = '<select class="flat width75" id="'.$htmlname.'" name="'.$htmlname.'"'.$disabled.'>'."\n";
7381
		if ($useempty) $resultyesno .= '<option value="-1"'.(($value < 0) ? ' selected' : '').'>&nbsp;</option>'."\n";
7382
		if (("$value" == 'yes') || ($value == 1))
7383
		{
7384
			$resultyesno .= '<option value="'.$yes.'" selected>'.$langs->trans("Yes").'</option>'."\n";
7385
			$resultyesno .= '<option value="'.$no.'">'.$langs->trans("No").'</option>'."\n";
7386
		} else {
7387
	   		$selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
7388
			$resultyesno .= '<option value="'.$yes.'">'.$langs->trans("Yes").'</option>'."\n";
7389
			$resultyesno .= '<option value="'.$no.'"'.$selected.'>'.$langs->trans("No").'</option>'."\n";
7390
		}
7391
		$resultyesno .= '</select>'."\n";
7392
7393
		if ($addjscombo) {
7394
			$resultyesno .= ajax_combobox($htmlname);
7395
		}
7396
7397
		return $resultyesno;
7398
	}
7399
7400
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7401
	/**
7402
	 *  Return list of export templates
7403
	 *
7404
	 *  @param	string	$selected          Id modele pre-selectionne
7405
	 *  @param  string	$htmlname          Name of HTML select
7406
	 *  @param  string	$type              Type of searched templates
7407
	 *  @param  int		$useempty          Affiche valeur vide dans liste
7408
	 *  @return	void
7409
	 */
7410
	public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
7411
	{
7412
		// phpcs:enable
7413
		$sql = "SELECT rowid, label";
7414
		$sql .= " FROM ".MAIN_DB_PREFIX."export_model";
7415
		$sql .= " WHERE type = '".$this->db->escape($type)."'";
7416
		$sql .= " ORDER BY rowid";
7417
		$result = $this->db->query($sql);
7418
		if ($result)
7419
		{
7420
			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
7421
			if ($useempty)
7422
			{
7423
				print '<option value="-1">&nbsp;</option>';
7424
			}
7425
7426
			$num = $this->db->num_rows($result);
7427
			$i = 0;
7428
			while ($i < $num)
7429
			{
7430
				$obj = $this->db->fetch_object($result);
7431
				if ($selected == $obj->rowid)
7432
				{
7433
					print '<option value="'.$obj->rowid.'" selected>';
7434
				} else {
7435
					print '<option value="'.$obj->rowid.'">';
7436
				}
7437
				print $obj->label;
7438
				print '</option>';
7439
				$i++;
7440
			}
7441
			print "</select>";
7442
		} else {
7443
			dol_print_error($this->db);
7444
		}
7445
	}
7446
7447
	/**
7448
	 *    Return a HTML area with the reference of object and a navigation bar for a business object
7449
	 *    Note: To complete search with a particular filter on select, you can set $object->next_prev_filter set to define SQL criterias.
7450
	 *
7451
	 *    @param	object	$object			Object to show.
7452
	 *    @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link.
7453
	 *    @param	string	$morehtml  		More html content to output just before the nav bar.
7454
	 *    @param	int		$shownav	  	Show Condition (navigation is shown if value is 1).
7455
	 *    @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.
7456
	 *    @param	string	$fieldref   	Name of field ref of object (object->ref) to show or 'none' to not show ref.
7457
	 *    @param	string	$morehtmlref  	More html to show after ref.
7458
	 *    @param	string	$moreparam  	More param to add in nav link url. Must start with '&...'.
7459
	 *	  @param	int		$nodbprefix		Do not include DB prefix to forge table name.
7460
	 *	  @param	string	$morehtmlleft	More html code to show before ref.
7461
	 *	  @param	string	$morehtmlstatus	More html code to show under navigation arrows (status place).
7462
	 *	  @param	string	$morehtmlright	More html code to show after ref.
7463
	 * 	  @return	string    				Portion HTML with ref + navigation buttons
7464
	 */
7465
	public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
7466
	{
7467
		global $langs, $conf, $hookmanager, $extralanguages;
7468
7469
		$ret = '';
7470
		if (empty($fieldid))  $fieldid = 'rowid';
7471
		if (empty($fieldref)) $fieldref = 'ref';
7472
7473
		// Add where from hooks
7474
		if (is_object($hookmanager))
7475
		{
7476
			$parameters = array();
7477
			$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
7478
			$object->next_prev_filter .= $hookmanager->resPrint;
7479
		}
7480
		$previous_ref = $next_ref = '';
7481
		if ($shownav)
7482
		{
7483
			//print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
7484
			$object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
7485
7486
			$navurl = $_SERVER["PHP_SELF"];
7487
			// Special case for project/task page
7488
			if ($paramid == 'project_ref')
7489
			{
7490
				if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl))     // TODO Remove this when nav with project_ref on task pages are ok
7491
				{
7492
					$navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
7493
					$paramid = 'ref';
7494
				}
7495
			}
7496
7497
			// accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
7498
			// accesskey is for Mac:               CTRL + key for all browsers
7499
			$stringforfirstkey = $langs->trans("KeyboardShortcut");
7500
			if ($conf->browser->name == 'chrome')
7501
			{
7502
				$stringforfirstkey .= ' ALT +';
7503
			} elseif ($conf->browser->name == 'firefox')
7504
			{
7505
				$stringforfirstkey .= ' ALT + SHIFT +';
7506
			} else {
7507
				$stringforfirstkey .= ' CTL +';
7508
			}
7509
7510
			$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>';
7511
			$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>';
7512
		}
7513
7514
		//print "xx".$previous_ref."x".$next_ref;
7515
		$ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
7516
7517
		// Right part of banner
7518
		if ($morehtmlright) $ret .= '<div class="inline-block floatleft">'.$morehtmlright.'</div>';
7519
7520
		if ($previous_ref || $next_ref || $morehtml)
7521
		{
7522
			$ret .= '<div class="pagination paginationref"><ul class="right">';
7523
		}
7524
		if ($morehtml)
7525
		{
7526
			$ret .= '<li class="noborder litext'.(($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '').'">'.$morehtml.'</li>';
7527
		}
7528
		if ($shownav && ($previous_ref || $next_ref))
7529
		{
7530
			$ret .= '<li class="pagination">'.$previous_ref.'</li>';
7531
			$ret .= '<li class="pagination">'.$next_ref.'</li>';
7532
		}
7533
		if ($previous_ref || $next_ref || $morehtml)
7534
		{
7535
			$ret .= '</ul></div>';
7536
		}
7537
7538
		$parameters = array();
7539
		$reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
7540
		if (empty($reshook)) $morehtmlstatus .= $hookmanager->resPrint;
7541
		else $morehtmlstatus = $hookmanager->resPrint;
7542
		if ($morehtmlstatus) $ret .= '<div class="statusref">'.$morehtmlstatus.'</div>';
7543
7544
		$parameters = array();
7545
		$reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
7546
		if (empty($reshook)) $morehtmlref .= $hookmanager->resPrint;
7547
		elseif ($reshook > 0) $morehtmlref = $hookmanager->resPrint;
7548
7549
		// Left part of banner
7550
		if ($morehtmlleft)
7551
		{
7552
			if ($conf->browser->layout == 'phone') $ret .= '<!-- morehtmlleft --><div class="floatleft">'.$morehtmlleft.'</div>'; // class="center" to have photo in middle
7553
			else $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">'.$morehtmlleft.'</div>';
7554
		}
7555
7556
		//if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
7557
		$ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid'.(($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '').'">';
7558
7559
		// For thirdparty, contact, user, member, the ref is the id, so we show something else
7560
		if ($object->element == 'societe')
7561
		{
7562
			$ret .= dol_htmlentities($object->name);
7563
7564
			// List of extra languages
7565
			$arrayoflangcode = array();
7566
			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
7567
7568
			if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
7569
				if (!is_object($extralanguages)) {
7570
					include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
7571
					$extralanguages = new ExtraLanguages($this->db);
7572
				}
7573
				$extralanguages->fetch_name_extralanguages('societe');
7574
7575
				if (!empty($extralanguages->attributes['societe']['name']))
7576
				{
7577
					$object->fetchValuesForExtraLanguages();
7578
7579
					$htmltext = '';
7580
					// If there is extra languages
7581
					foreach ($arrayoflangcode as $extralangcode) {
7582
						$htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
7583
						if ($object->array_languages['name'][$extralangcode]) {
7584
							$htmltext .= $object->array_languages['name'][$extralangcode];
7585
						} else {
7586
							$htmltext .= '<span class="opacitymedium">'.$langs->trans("SwitchInEditModeToAddTranslation").'</span>';
7587
						}
7588
					}
7589
					$ret .= '<!-- Show translations of name -->'."\n";
7590
					$ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
7591
				}
7592
			}
7593
		} elseif ($object->element == 'member')
7594
		{
7595
			$ret .= $object->ref.'<br>';
7596
			$fullname = $object->getFullName($langs);
7597
			if ($object->morphy == 'mor' && $object->societe) {
7598
				$ret .= dol_htmlentities($object->societe).((!empty($fullname) && $object->societe != $fullname) ? ' ('.dol_htmlentities($fullname).')' : '');
7599
			} else {
7600
				$ret .= dol_htmlentities($fullname).((!empty($object->societe) && $object->societe != $fullname) ? ' ('.dol_htmlentities($object->societe).')' : '');
7601
			}
7602
		} elseif (in_array($object->element, array('contact', 'user', 'usergroup')))
7603
		{
7604
			$ret .= dol_htmlentities($object->getFullName($langs));
7605
		} elseif (in_array($object->element, array('action', 'agenda')))
7606
		{
7607
			$ret .= $object->ref.'<br>'.$object->label;
7608
		} elseif (in_array($object->element, array('adherent_type')))
7609
		{
7610
			$ret .= $object->label;
7611
		} elseif ($object->element == 'ecm_directories')
7612
		{
7613
			$ret .= '';
7614
		} elseif ($fieldref != 'none')
7615
		{
7616
			$ret .= dol_htmlentities($object->$fieldref);
7617
		}
7618
7619
		if ($morehtmlref)
7620
		{
7621
			// don't add a additional space, when "$morehtmlref" starts with a HTML div tag
7622
			if (substr($morehtmlref, 0, 4) != '<div')
7623
			{
7624
				$ret .= ' ';
7625
			}
7626
7627
			$ret .= $morehtmlref;
7628
		}
7629
7630
		$ret .= '</div>';
7631
7632
		$ret .= '</div><!-- End banner content -->';
7633
7634
		return $ret;
7635
	}
7636
7637
7638
	/**
7639
	 *    	Return HTML code to output a barcode
7640
	 *
7641
	 *     	@param	Object	$object		Object containing data to retrieve file name
7642
	 * 		@param	int		$width			Width of photo
7643
	 * 	  	@return string    				HTML code to output barcode
7644
	 */
7645
	public function showbarcode(&$object, $width = 100)
7646
	{
7647
		global $conf;
7648
7649
		//Check if barcode is filled in the card
7650
		if (empty($object->barcode)) return '';
7651
7652
		// Complete object if not complete
7653
		if (empty($object->barcode_type_code) || empty($object->barcode_type_coder))
7654
		{
7655
			$result = $object->fetch_barcode();
7656
			//Check if fetch_barcode() failed
7657
			if ($result < 1) return '<!-- ErrorFetchBarcode -->';
7658
		}
7659
7660
		// Barcode image
7661
		$url = DOL_URL_ROOT.'/viewimage.php?modulepart=barcode&generator='.urlencode($object->barcode_type_coder).'&code='.urlencode($object->barcode).'&encoding='.urlencode($object->barcode_type_code);
7662
		$out = '<!-- url barcode = '.$url.' -->';
7663
		$out .= '<img src="'.$url.'">';
7664
		return $out;
7665
	}
7666
7667
	/**
7668
	 *    	Return HTML code to output a photo
7669
	 *
7670
	 *    	@param	string		$modulepart			Key to define module concerned ('societe', 'userphoto', 'memberphoto')
7671
	 *     	@param  object		$object				Object containing data to retrieve file name
7672
	 * 		@param	int			$width				Width of photo
7673
	 * 		@param	int			$height				Height of photo (auto if 0)
7674
	 * 		@param	int			$caneditfield		Add edit fields
7675
	 * 		@param	string		$cssclass			CSS name to use on img for photo
7676
	 * 		@param	string		$imagesize		    'mini', 'small' or '' (original)
7677
	 *      @param  int         $addlinktofullsize  Add link to fullsize image
7678
	 *      @param  int         $cache              1=Accept to use image in cache
7679
	 *      @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 ''.
7680
	 *      @param	int			$noexternsourceoverwrite	No overwrite image with extern source (like 'gravatar' or other module)
7681
	 * 	  	@return string    						HTML code to output photo
7682
	 */
7683
	public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
7684
	{
7685
		global $conf, $langs;
7686
7687
		$entity = (!empty($object->entity) ? $object->entity : $conf->entity);
7688
		$id = (!empty($object->id) ? $object->id : $object->rowid);
7689
7690
		$ret = ''; $dir = ''; $file = ''; $originalfile = ''; $altfile = ''; $email = ''; $capture = '';
7691
		if ($modulepart == 'societe')
7692
		{
7693
			$dir = $conf->societe->multidir_output[$entity];
7694
			if (!empty($object->logo)) {
7695
				if ((string) $imagesize == 'mini') $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
7696
				elseif ((string) $imagesize == 'small') $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.getImageFileNameForSize($object->logo, '_small');
7697
				else $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
7698
				$originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty').'logos/'.$object->logo;
7699
			}
7700
			$email = $object->email;
7701
		} elseif ($modulepart == 'contact')	{
7702
			$dir = $conf->societe->multidir_output[$entity].'/contact';
7703
			if (!empty($object->photo))
7704
			{
7705
				if ((string) $imagesize == 'mini') $file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_mini');
7706
				elseif ((string) $imagesize == 'small') $file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.getImageFileNameForSize($object->photo, '_small');
7707
				else $file = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
7708
				$originalfile = get_exdir(0, 0, 0, 0, $object, 'contact').'photos/'.$object->photo;
7709
			}
7710
			$email = $object->email;
7711
			$capture = 'user';
7712
		} elseif ($modulepart == 'userphoto') {
7713
			$dir = $conf->user->dir_output;
7714
			if (!empty($object->photo))
7715
			{
7716
				if ((string) $imagesize == 'mini') $file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_mini');
7717
				elseif ((string) $imagesize == 'small') $file = get_exdir(0, 0, 0, 0, $object, 'user').getImageFileNameForSize($object->photo, '_small');
7718
				else $file = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
7719
				$originalfile = get_exdir(0, 0, 0, 0, $object, 'user').$object->photo;
7720
			}
7721
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) $altfile = $object->id.".jpg"; // For backward compatibility
7722
			$email = $object->email;
7723
			$capture = 'user';
7724
		} elseif ($modulepart == 'memberphoto')	{
7725
			$dir = $conf->adherent->dir_output;
7726
			if (!empty($object->photo))
7727
			{
7728
				if ((string) $imagesize == 'mini') $file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_mini');
7729
				elseif ((string) $imagesize == 'small') $file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.getImageFileNameForSize($object->photo, '_small');
7730
				else $file = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
7731
				$originalfile = get_exdir(0, 0, 0, 0, $object, 'member').'photos/'.$object->photo;
7732
			}
7733
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) $altfile = $object->id.".jpg"; // For backward compatibility
7734
			$email = $object->email;
7735
			$capture = 'user';
7736
		} else {
7737
			// Generic case to show photos
7738
			$dir = $conf->$modulepart->dir_output;
7739
			if (!empty($object->photo))
7740
			{
7741
				if ((string) $imagesize == 'mini') $file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_mini');
7742
				elseif ((string) $imagesize == 'small') $file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.getImageFileNameForSize($object->photo, '_small');
7743
				else $file = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
7744
				$originalfile = get_exdir($id, 2, 0, 0, $object, $modulepart).'photos/'.$object->photo;
7745
			}
7746
			if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) $altfile = $object->id.".jpg"; // For backward compatibility
7747
			$email = $object->email;
7748
		}
7749
7750
		if ($forcecapture) $capture = $forcecapture;
7751
7752
		if ($dir)
7753
		{
7754
			if ($file && file_exists($dir."/".$file))
7755
			{
7756
				if ($addlinktofullsize) {
7757
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
7758
					if ($urladvanced) $ret .= '<a href="'.$urladvanced.'">';
7759
					else $ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
7760
				}
7761
				$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.'">';
7762
				if ($addlinktofullsize) $ret .= '</a>';
7763
			} elseif ($altfile && file_exists($dir."/".$altfile)) {
7764
				if ($addlinktofullsize) {
7765
					$urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity='.$entity);
7766
					if ($urladvanced) $ret .= '<a href="'.$urladvanced.'">';
7767
					else $ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$entity.'&file='.urlencode($originalfile).'&cache='.$cache.'">';
7768
				}
7769
				$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.'">';
7770
				if ($addlinktofullsize) $ret .= '</a>';
7771
			} else {
7772
				$nophoto = '/public/theme/common/nophoto.png';
7773
				if (in_array($modulepart, array('userphoto', 'contact', 'memberphoto'))) {	// For module that are "physical" users
7774
					if ($modulepart == 'memberphoto' && strpos($object->morphy, 'mor') !== false) {
7775
						$nophoto = '/public/theme/common/company.png';
7776
					} else {
7777
						$nophoto = '/public/theme/common/user_anonymous.png';
7778
						if ($object->gender == 'man') $nophoto = '/public/theme/common/user_man.png';
7779
						if ($object->gender == 'woman') $nophoto = '/public/theme/common/user_woman.png';
7780
					}
7781
				}
7782
7783
				if (!empty($conf->gravatar->enabled) && $email && empty($noexternsourceoverwrite)) {
7784
					// see https://gravatar.com/site/implement/images/php/
7785
					global $dolibarr_main_url_root;
7786
					$ret .= '<!-- Put link to gravatar -->';
7787
					//$defaultimg=urlencode(dol_buildpath($nophoto,3));
7788
					$defaultimg = 'mm';
7789
					$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="Gravatar avatar" title="'.$email.' Gravatar avatar" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="https://www.gravatar.com/avatar/'.md5(strtolower(trim($email))).'?s='.$width.'&d='.$defaultimg.'">'; // gravatar need md5 hash
7790
				} else {
7791
					$ret .= '<img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" '.($width ? ' width="'.$width.'"' : '').($height ? ' height="'.$height.'"' : '').' src="'.DOL_URL_ROOT.$nophoto.'">';
7792
				}
7793
			}
7794
7795
			if ($caneditfield)
7796
			{
7797
				if ($object->photo) $ret .= "<br>\n";
7798
				$ret .= '<table class="nobordernopadding centpercent">';
7799
				if ($object->photo) $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> '.$langs->trans("Delete").'<br><br></td></tr>';
7800
				$ret .= '<tr><td class="tdoverflow"><input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"'.($capture ? ' capture="'.$capture.'"' : '').'></td></tr>';
7801
				$ret .= '</table>';
7802
			}
7803
		} else dol_print_error('', 'Call of showphoto with wrong parameters modulepart='.$modulepart);
7804
7805
		return $ret;
7806
	}
7807
7808
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7809
	/**
7810
	 *	Return select list of groups
7811
	 *
7812
	 *  @param	string	$selected       Id group preselected
7813
	 *  @param  string	$htmlname       Field name in form
7814
	 *  @param  int		$show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
7815
	 *  @param  string	$exclude        Array list of groups id to exclude
7816
	 * 	@param	int		$disabled		If select list must be disabled
7817
	 *  @param  string	$include        Array list of groups id to include
7818
	 * 	@param	int		$enableonly		Array list of groups id to be enabled. All other must be disabled
7819
	 * 	@param	string	$force_entity	'0' or Ids of environment to force
7820
	 * 	@param	bool	$multiple		add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter)
7821
	 *  @param  string	$morecss		More css to add to html component
7822
	 *  @return	string
7823
	 *  @see select_dolusers()
7824
	 */
7825
	public function select_dolgroups($selected = '', $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $multiple = false, $morecss = '')
7826
	{
7827
		// phpcs:enable
7828
		global $conf, $user, $langs;
7829
7830
		// Permettre l'exclusion de groupes
7831
		if (is_array($exclude))	$excludeGroups = implode("','", $exclude);
0 ignored issues
show
introduced by
The condition is_array($exclude) is always false.
Loading history...
7832
		// Permettre l'inclusion de groupes
7833
		if (is_array($include))	$includeGroups = implode("','", $include);
0 ignored issues
show
introduced by
The condition is_array($include) is always false.
Loading history...
7834
7835
		if (!is_array($selected)) $selected = array($selected);
0 ignored issues
show
introduced by
The condition is_array($selected) is always false.
Loading history...
7836
7837
		$out = '';
7838
7839
		// On recherche les groupes
7840
		$sql = "SELECT ug.rowid, ug.nom as name";
7841
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity)
7842
		{
7843
			$sql .= ", e.label";
7844
		}
7845
		$sql .= " FROM ".MAIN_DB_PREFIX."usergroup as ug ";
7846
		if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity)
7847
		{
7848
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=ug.entity";
7849
			if ($force_entity) $sql .= " WHERE ug.entity IN (0, ".$force_entity.")";
7850
			else $sql .= " WHERE ug.entity IS NOT NULL";
7851
		} else {
7852
			$sql .= " WHERE ug.entity IN (0, ".$conf->entity.")";
7853
		}
7854
		if (is_array($exclude) && $excludeGroups) $sql .= " AND ug.rowid NOT IN ('".$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...
7855
		if (is_array($include) && $includeGroups) $sql .= " AND ug.rowid IN ('".$includeGroups."')";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $includeGroups does not seem to be defined for all execution paths leading up to this point.
Loading history...
introduced by
The condition is_array($include) is always false.
Loading history...
7856
		$sql .= " ORDER BY ug.nom ASC";
7857
7858
		dol_syslog(get_class($this)."::select_dolgroups", LOG_DEBUG);
7859
		$resql = $this->db->query($sql);
7860
		if ($resql)
7861
		{
7862
			// Enhance with select2
7863
			include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7864
		   	$out .= ajax_combobox($htmlname);
7865
7866
			$out .= '<select class="flat minwidth200'.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
7867
7868
			$num = $this->db->num_rows($resql);
7869
			$i = 0;
7870
			if ($num)
7871
			{
7872
				if ($show_empty && !$multiple) $out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>&nbsp;</option>'."\n";
7873
7874
				while ($i < $num)
7875
				{
7876
					$obj = $this->db->fetch_object($resql);
7877
					$disableline = 0;
7878
					if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) $disableline = 1;
7879
7880
					$out .= '<option value="'.$obj->rowid.'"';
7881
					if ($disableline) $out .= ' disabled';
7882
					if ((is_object($selected[0]) && $selected[0]->id == $obj->rowid) || (!is_object($selected[0]) && in_array($obj->rowid, $selected)))
7883
					{
7884
						$out .= ' selected';
7885
					}
7886
					$out .= '>';
7887
7888
					$out .= $obj->name;
7889
					if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1)
7890
					{
7891
						$out .= " (".$obj->label.")";
7892
					}
7893
7894
					$out .= '</option>';
7895
					$i++;
7896
				}
7897
			} else {
7898
				if ($show_empty) $out .= '<option value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'></option>'."\n";
7899
				$out .= '<option value="" disabled>'.$langs->trans("NoUserGroupDefined").'</option>';
7900
			}
7901
			$out .= '</select>';
7902
		} else {
7903
			dol_print_error($this->db);
7904
		}
7905
7906
		return $out;
7907
	}
7908
7909
7910
	/**
7911
	 *	Return HTML to show the search and clear seach button
7912
	 *
7913
	 *  @return	string
7914
	 */
7915
	public function showFilterButtons()
7916
	{
7917
		$out = '<div class="nowrap">';
7918
		$out .= '<button type="submit" class="liste_titre button_search" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
7919
		$out .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
7920
		$out .= '</div>';
7921
7922
		return $out;
7923
	}
7924
7925
	/**
7926
	 *	Return HTML to show the search and clear search button
7927
	 *
7928
	 *  @param  string  $cssclass                  CSS class
7929
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
7930
	 *  @param  string  $massactionname            Mass action button name that will launch an action on the selected items
7931
	 *  @return	string
7932
	 */
7933
	public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
7934
	{
7935
		global $conf, $langs;
7936
7937
		$out = '';
7938
7939
		if (!empty($conf->use_javascript_ajax)) $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="'.$cssclass.'s" name="'.$cssclass.'s" class="checkallactions"></div>';
7940
		$out .= '<script>
7941
            $(document).ready(function() {
7942
                $("#' . $cssclass.'s").click(function() {
7943
                    if($(this).is(\':checked\')){
7944
                        console.log("We check all '.$cssclass.' and trigger the change method");
7945
                		$(".'.$cssclass.'").prop(\'checked\', true).trigger(\'change\');
7946
                    }
7947
                    else
7948
                    {
7949
                        console.log("We uncheck all");
7950
                		$(".'.$cssclass.'").prop(\'checked\', false).trigger(\'change\');
7951
                    }'."\n";
7952
				if ($calljsfunction) $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "'.$massactionname.'", "'.$cssclass.'"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
7953
		$out .= '         });
7954
        	        $(".' . $cssclass.'").change(function() {
7955
					$(this).closest("tr").toggleClass("highlight", this.checked);
7956
				});
7957
		 	});
7958
    	</script>';
7959
7960
		return $out;
7961
	}
7962
7963
	/**
7964
	 *	Return HTML to show the search and clear seach button
7965
	 *
7966
	 *  @param	int  	$addcheckuncheckall        Add the check all/uncheck all checkbox (use javascript) and code to manage this
7967
	 *  @param  string  $cssclass                  CSS class
7968
	 *  @param  int     $calljsfunction            0=default. 1=call function initCheckForSelect() after changing status of checkboxes
7969
	 *  @param  string  $massactionname            Mass action name
7970
	 *  @return	string
7971
	 */
7972
	public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
7973
	{
7974
		$out = $this->showFilterButtons();
7975
		if ($addcheckuncheckall)
7976
		{
7977
			$out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
7978
		}
7979
		return $out;
7980
	}
7981
7982
	/**
7983
	 * Return HTML to show the select of expense categories
7984
	 *
7985
	 * @param	string	$selected              preselected category
7986
	 * @param	string	$htmlname              name of HTML select list
7987
	 * @param	integer	$useempty              1=Add empty line
7988
	 * @param	array	$excludeid             id to exclude
7989
	 * @param	string	$target                htmlname of target select to bind event
7990
	 * @param	int		$default_selected      default category to select if fk_c_type_fees change = EX_KME
7991
	 * @param	array	$params                param to give
7992
	 * @param	int		$info_admin			   Show the tooltip help picto to setup list
7993
	 * @return	string
7994
	 */
7995
	public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
7996
	{
7997
		global $db, $langs, $user;
7998
7999
		$out = '';
8000
		$sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1';
8001
		$sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')';
8002
		if (!empty($excludeid)) $sql .= ' AND rowid NOT IN ('.implode(',', $excludeid).')';
8003
		$sql .= ' ORDER BY label';
8004
8005
		$resql = $db->query($sql);
8006
		if ($resql)
8007
		{
8008
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp maxwidth200">';
8009
			if ($useempty) $out .= '<option value="0">&nbsp;</option>';
8010
8011
			while ($obj = $db->fetch_object($resql))
8012
			{
8013
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.$langs->trans($obj->label).'</option>';
8014
			}
8015
			$out .= '</select>';
8016
			$out .= ajax_combobox('select_'.$htmlname);
8017
8018
			if (!empty($htmlname) && $user->admin && $info_admin) $out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
8019
8020
			if (!empty($target))
8021
			{
8022
				$sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
8023
				$resql = $db->query($sql);
8024
				if ($resql)
8025
				{
8026
					if ($db->num_rows($resql) > 0)
8027
					{
8028
						$obj = $db->fetch_object($resql);
8029
						$out .= '<script>
8030
							$(function() {
8031
								$("select[name='.$target.']").on("change", function() {
8032
									var current_val = $(this).val();
8033
									if (current_val == '.$obj->id.') {';
8034
						if (!empty($default_selected) || !empty($selected)) $out .= '$("select[name='.$htmlname.']").val("'.($default_selected > 0 ? $default_selected : $selected).'");';
8035
8036
						$out .= '
8037
										$("select[name='.$htmlname.']").change();
8038
									}
8039
								});
8040
8041
								$("select[name='.$htmlname.']").change(function() {
8042
8043
									if ($("select[name='.$target.']").val() == '.$obj->id.') {
8044
										// get price of kilometer to fill the unit price
8045
										var data = '.json_encode($params).';
8046
										data.fk_c_exp_tax_cat = $(this).val();
8047
8048
										$.ajax({
8049
											method: "POST",
8050
											dataType: "json",
8051
											data: data,
8052
											url: "'.(DOL_URL_ROOT.'/expensereport/ajax/ajaxik.php').'",
8053
										}).done(function( data, textStatus, jqXHR ) {
8054
											console.log(data);
8055
											if (typeof data.up != "undefined") {
8056
												$("input[name=value_unit]").val(data.up);
8057
												$("select[name='.$htmlname.']").attr("title", data.title);
8058
											} else {
8059
												$("input[name=value_unit]").val("");
8060
												$("select[name='.$htmlname.']").attr("title", "");
8061
											}
8062
										});
8063
									}
8064
								});
8065
							});
8066
						</script>';
8067
					}
8068
				}
8069
			}
8070
		} else {
8071
			dol_print_error($db);
8072
		}
8073
8074
		return $out;
8075
	}
8076
8077
	/**
8078
	 * Return HTML to show the select ranges of expense range
8079
	 *
8080
	 * @param	string	$selected    preselected category
8081
	 * @param	string	$htmlname    name of HTML select list
8082
	 * @param	integer	$useempty    1=Add empty line
8083
	 * @return	string
8084
	 */
8085
	public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
8086
	{
8087
		global $db, $conf, $langs;
8088
8089
		$out = '';
8090
		$sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range';
8091
		$sql .= ' WHERE entity = '.$conf->entity.' AND active = 1';
8092
8093
		$resql = $db->query($sql);
8094
		if ($resql)
8095
		{
8096
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
8097
			if ($useempty) $out .= '<option value="0"></option>';
8098
8099
			while ($obj = $db->fetch_object($resql))
8100
			{
8101
				$out .= '<option '.($selected == $obj->rowid ? 'selected="selected"' : '').' value="'.$obj->rowid.'">'.price($obj->range_ik, 0, $langs, 1, 0).'</option>';
8102
			}
8103
			$out .= '</select>';
8104
		} else {
8105
			dol_print_error($db);
8106
		}
8107
8108
		return $out;
8109
	}
8110
8111
	/**
8112
	 * Return HTML to show a select of expense
8113
	 *
8114
	 * @param	string	$selected    preselected category
8115
	 * @param	string	$htmlname    name of HTML select list
8116
	 * @param	integer	$useempty    1=Add empty choice
8117
	 * @param	integer	$allchoice   1=Add all choice
8118
	 * @param	integer	$useid       0=use 'code' as key, 1=use 'id' as key
8119
	 * @return	string
8120
	 */
8121
	public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
8122
	{
8123
		global $db, $langs;
8124
8125
		$out = '';
8126
		$sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees';
8127
		$sql .= ' WHERE active = 1';
8128
8129
		$resql = $db->query($sql);
8130
		if ($resql)
8131
		{
8132
			$out = '<select id="select_'.$htmlname.'" name="'.$htmlname.'" class="'.$htmlname.' flat minwidth75imp">';
8133
			if ($useempty) $out .= '<option value="0"></option>';
8134
			if ($allchoice) $out .= '<option value="-1">'.$langs->trans('AllExpenseReport').'</option>';
8135
8136
			$field = 'code';
8137
			if ($useid) $field = 'id';
8138
8139
			while ($obj = $db->fetch_object($resql))
8140
			{
8141
				$key = $langs->trans($obj->code);
8142
				$out .= '<option '.($selected == $obj->{$field} ? 'selected="selected"' : '').' value="'.$obj->{$field}.'">'.($key != $obj->code ? $key : $obj->label).'</option>';
8143
			}
8144
			$out .= '</select>';
8145
		} else {
8146
			dol_print_error($db);
8147
		}
8148
8149
		return $out;
8150
	}
8151
8152
	/**
8153
	 *  Output a combo list with invoices qualified for a third party
8154
	 *
8155
	 *  @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)
8156
	 *  @param  int		$selected   	Id invoice preselected
8157
	 *  @param  string	$htmlname   	Name of HTML select
8158
	 *	@param	int		$maxlength		Maximum length of label
8159
	 *	@param	int		$option_only	Return only html options lines without the select tag
8160
	 *	@param	string	$show_empty		Add an empty line ('1' or string to show for empty line)
8161
	 *  @param	int		$discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
8162
	 *  @param	int		$forcefocus		Force focus on field (works with javascript only)
8163
	 *  @param	int		$disabled		Disabled
8164
	 *  @param	string	$morecss        More css added to the select component
8165
	 *  @param	string	$projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
8166
	 *  @param	string	$showproject	'all' = Show project info, ''=Hide project info
8167
	 *  @param	User	$usertofilter	User object to use for filtering
8168
	 *	@return int         			Nbr of project if OK, <0 if KO
8169
	 */
8170
	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)
8171
	{
8172
		global $user, $conf, $langs;
8173
8174
		require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
8175
8176
		if (is_null($usertofilter))
8177
		{
8178
			$usertofilter = $user;
8179
		}
8180
8181
		$out = '';
8182
8183
		$hideunselectables = false;
8184
		if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true;
8185
8186
		if (empty($projectsListId))
8187
		{
8188
			if (empty($usertofilter->rights->projet->all->lire))
8189
			{
8190
				$projectstatic = new Project($this->db);
8191
				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
8192
			}
8193
		}
8194
8195
		// Search all projects
8196
		$sql = 'SELECT f.rowid, f.ref as fref, "nolabel" as flabel, p.rowid as pid, f.ref,
8197
            p.title, p.fk_soc, p.fk_statut, p.public,';
8198
		$sql .= ' s.nom as name';
8199
		$sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p';
8200
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,';
8201
		$sql .= ' '.MAIN_DB_PREFIX.'facture as f';
8202
		$sql .= " WHERE p.entity IN (".getEntity('project').")";
8203
		$sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
8204
		//if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")";
8205
		//if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
8206
		//if ($socid > 0)  $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)";
8207
		$sql .= " GROUP BY f.ref ORDER BY p.ref, f.ref ASC";
8208
8209
		$resql = $this->db->query($sql);
8210
		if ($resql)
8211
		{
8212
			// Use select2 selector
8213
			if (!empty($conf->use_javascript_ajax))
8214
			{
8215
				include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
8216
			   	$comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
8217
				$out .= $comboenhancement;
8218
				$morecss = 'minwidth200imp maxwidth500';
8219
			}
8220
8221
			if (empty($option_only)) {
8222
				$out .= '<select class="valignmiddle flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled="disabled"' : '').' id="'.$htmlname.'" name="'.$htmlname.'">';
8223
			}
8224
			if (!empty($show_empty)) {
8225
				$out .= '<option value="0" class="optiongrey">';
8226
				if (!is_numeric($show_empty)) $out .= $show_empty;
8227
				else $out .= '&nbsp;';
8228
				$out .= '</option>';
8229
			}
8230
			$num = $this->db->num_rows($resql);
8231
			$i = 0;
8232
			if ($num)
8233
			{
8234
				while ($i < $num)
8235
				{
8236
					$obj = $this->db->fetch_object($resql);
8237
					// 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.
8238
					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire))
8239
					{
8240
						// Do nothing
8241
					} else {
8242
						if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED)
8243
						{
8244
							$i++;
8245
							continue;
8246
						}
8247
8248
						$labeltoshow = '';
8249
8250
						if ($showproject == 'all')
8251
						{
8252
							$labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
8253
							if ($obj->name) $labeltoshow .= ' - '.$obj->name; // Soc name
8254
8255
							$disabled = 0;
8256
							if ($obj->fk_statut == Project::STATUS_DRAFT)
8257
							{
8258
								$disabled = 1;
8259
								$labeltoshow .= ' - '.$langs->trans("Draft");
8260
							} elseif ($obj->fk_statut == Project::STATUS_CLOSED)
8261
							{
8262
								if ($discard_closed == 2) $disabled = 1;
8263
								$labeltoshow .= ' - '.$langs->trans("Closed");
8264
							} elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid))
8265
							{
8266
								$disabled = 1;
8267
								$labeltoshow .= ' - '.$langs->trans("LinkedToAnotherCompany");
8268
							}
8269
						}
8270
8271
						if (!empty($selected) && $selected == $obj->rowid)
8272
						{
8273
							$out .= '<option value="'.$obj->rowid.'" selected';
8274
							//if ($disabled) $out.=' disabled';						// with select2, field can't be preselected if disabled
8275
							$out .= '>'.$labeltoshow.'</option>';
8276
						} else {
8277
							if ($hideunselectables && $disabled && ($selected != $obj->rowid))
8278
							{
8279
								$resultat = '';
8280
							} else {
8281
								$resultat = '<option value="'.$obj->rowid.'"';
8282
								if ($disabled) $resultat .= ' disabled';
8283
								//if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
8284
								//else $labeltoshow.=' ('.$langs->trans("Private").')';
8285
								$resultat .= '>';
8286
								$resultat .= $labeltoshow;
8287
								$resultat .= '</option>';
8288
							}
8289
							$out .= $resultat;
8290
						}
8291
					}
8292
					$i++;
8293
				}
8294
			}
8295
			if (empty($option_only)) {
8296
				$out .= '</select>';
8297
			}
8298
8299
			print $out;
8300
8301
			$this->db->free($resql);
8302
			return $num;
8303
		} else {
8304
			dol_print_error($this->db);
8305
			return -1;
8306
		}
8307
	}
8308
8309
	/**
8310
	 * Output the component to make advanced search criteries
8311
	 *
8312
	 * @param	array		$arrayofcriterias			          Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...)
8313
	 * @param	array		$search_component_params	          Array of selected search criterias
8314
	 * @param   array       $arrayofinputfieldsalreadyoutput      Array of input fields already inform. The component will not generate a hidden input field if it is in this list.
8315
	 * @return	string									          HTML component for advanced search
8316
	 */
8317
	public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array())
8318
	{
8319
		global $langs;
8320
8321
		$ret = '';
8322
8323
		$ret .= '<div class="nowrap centpercent">';
8324
		//$ret .= '<button type="submit" class="liste_titre button_removefilter" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
8325
		$ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor paddingright">';
8326
		$ret .= '<span class="fas fa-filter linkobject boxfilter" title="Filter" id="idsubimgproductdistribution"></span>';
8327
		$ret .= $langs->trans("Filters");
8328
		$ret .= '</a>';
8329
		//$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
8330
		$ret .= '<div name="search_component_params" class="search_component_params inline-block minwidth500 maxwidth300onsmartphone valignmiddle">';
8331
		$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
8332
8333
		$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
8334
		$ret .= '</div>';
8335
		$ret .= '<input type="hidden" name="search_component_params_hidden" class="search_component_params_hidden" value="'.GETPOST("search_component_params_hidden").'">';
8336
		// For compatibility with forms that show themself the search criteria in addition of this component, we output the fields
8337
		foreach ($arrayofcriterias as $criterias) {
8338
			foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {
8339
				if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) continue;
8340
				if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) continue;
8341
				if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
8342
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_start">';
8343
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startyear">';
8344
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startmonth">';
8345
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_startday">';
8346
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_end">';
8347
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endyear">';
8348
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endmonth">';
8349
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'_endday">';
8350
				} else {
8351
					$ret .= '<input type="hidden" name="search_'.$criteriafamilykey.'">';
8352
				}
8353
			}
8354
		}
8355
		$ret .= '</div>';
8356
8357
8358
		return $ret;
8359
	}
8360
8361
	/**
8362
	 * selectModelMail
8363
	 *
8364
	 * @param   string   $prefix     	Prefix
8365
	 * @param   string   $modelType  	Model type
8366
	 * @param	int		 $default	 	1=Show also Default mail template
8367
	 * @param	int		 $addjscombo	Add js combobox
8368
	 * @return  string               	HTML select string
8369
	 */
8370
	public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
8371
	{
8372
		global $langs, $db, $user;
8373
8374
		$retstring = '';
8375
8376
		$TModels = array();
8377
8378
		include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
8379
		$formmail = new FormMail($db);
8380
		$result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
8381
8382
		if ($default) $TModels[0] = $langs->trans('DefaultMailModel');
8383
		if ($result > 0) {
8384
			foreach ($formmail->lines_model as $model) {
8385
				$TModels[$model->id] = $model->label;
8386
			}
8387
		}
8388
8389
		$retstring .= '<select class="flat" id="select_'.$prefix.'model_mail" name="'.$prefix.'model_mail">';
8390
8391
		foreach ($TModels as $id_model=>$label_model) {
8392
			$retstring .= '<option value="'.$id_model.'"';
8393
			$retstring .= ">".$label_model."</option>";
8394
		}
8395
8396
		$retstring .= "</select>";
8397
8398
		if ($addjscombo) $retstring .= ajax_combobox('select_'.$prefix.'model_mail');
8399
8400
		return $retstring;
8401
	}
8402
}
8403