Passed
Push — dev ( f7d146...05f415 )
by Rafael
60:50
created

Form::selectMembersList()   D

Complexity

Conditions 21

Size

Total Lines 107
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 65
nop 10
dl 0
loc 107
rs 4.1666
c 0
b 0
f 0

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
3
/* Copyright (c) 2002-2007  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2004-2012  Laurent Destailleur         <[email protected]>
5
 * Copyright (C) 2004       Benoit Mortier              <[email protected]>
6
 * Copyright (C) 2004       Sebastien Di Cintio         <[email protected]>
7
 * Copyright (C) 2004       Eric Seigne                 <[email protected]>
8
 * Copyright (C) 2005-2017  Regis Houssin               <[email protected]>
9
 * Copyright (C) 2006       Andre Cianfarani            <[email protected]>
10
 * Copyright (C) 2006       Marc Barilley/Ocebo         <[email protected]>
11
 * Copyright (C) 2007       Franky Van Liedekerke       <[email protected]>
12
 * Copyright (C) 2007       Patrick Raguin              <[email protected]>
13
 * Copyright (C) 2010       Juanjo Menent               <[email protected]>
14
 * Copyright (C) 2010-2021  Philippe Grand              <[email protected]>
15
 * Copyright (C) 2011       Herve Prot                  <[email protected]>
16
 * Copyright (C) 2012-2016  Marcos García               <[email protected]>
17
 * Copyright (C) 2012       Cedric Salvador             <[email protected]>
18
 * Copyright (C) 2012-2015  Raphaël Doursenaud          <[email protected]>
19
 * Copyright (C) 2014-2023  Alexandre Spangaro          <[email protected]>
20
 * Copyright (C) 2018-2022  Ferran Marcet               <[email protected]>
21
 * Copyright (C) 2018-2024  Frédéric France             <[email protected]>
22
 * Copyright (C) 2018       Nicolas ZABOURI	            <[email protected]>
23
 * Copyright (C) 2018       Christophe Battarel         <[email protected]>
24
 * Copyright (C) 2018       Josep Lluis Amador          <[email protected]>
25
 * Copyright (C) 2023		Joachim Kueter			    <[email protected]>
26
 * Copyright (C) 2023		Nick Fragoulis
27
 * Copyright (C) 2024		MDW							<[email protected]>
28
 * Copyright (C) 2024       Rafael San José             <[email protected]>
29
 *
30
 * This program is free software; you can redistribute it and/or modify
31
 * it under the terms of the GNU General Public License as published by
32
 * the Free Software Foundation; either version 3 of the License, or
33
 * (at your option) any later version.
34
 *
35
 * This program is distributed in the hope that it will be useful,
36
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38
 * GNU General Public License for more details.
39
 *
40
 * You should have received a copy of the GNU General Public License
41
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
42
 */
43
44
namespace Dolibarr\Code\Core\Classes;
45
46
use Dolibarr\Code\Adherents\Classes\Adherent;
47
use Dolibarr\Code\Categories\Classes\Categorie;
48
use Dolibarr\Code\Compta\Classes\Facture;
49
use Dolibarr\Code\Contact\Classes\Contact;
50
use Dolibarr\Code\Fourn\Classes\ProductFournisseur;
51
use Dolibarr\Code\Product\Classes\Entrepot;
52
use Dolibarr\Code\Product\Classes\PriceParser;
53
use Dolibarr\Code\Product\Classes\Product;
54
use Dolibarr\Code\Projet\Classes\Project;
55
use Dolibarr\Code\Resource\Classes\Dolresource;
56
use Dolibarr\Code\Resource\Classes\FormResource;
57
use Dolibarr\Code\Societe\Classes\Societe;
58
use Dolibarr\Code\Ticket\Classes\Ticket;
59
use Dolibarr\Code\User\Classes\User;
60
use Dolibarr\Core\Base\CommonObject;
61
use Dolibarr\Lib\Misc;
62
use DoliDB;
63
use stdClass;
64
65
/**
66
 * \file       htdocs/core/class/html.form.class.php
67
 * \ingroup    core
68
 * \brief      File of class with all html predefined components
69
 */
70
71
72
/**
73
 * Class to manage generation of HTML components
74
 * Only common components must be here.
75
 *
76
 * TODO Merge all function load_cache_* and loadCache* (except load_cache_vatrates) into one generic function loadCacheTable
77
 */
78
class Form
79
{
80
    /**
81
     * @var DoliDB Database handler.
82
     */
83
    public $db;
84
85
    /**
86
     * @var string Error code (or message)
87
     */
88
    public $error = '';
89
90
    /**
91
     * @var string[]    Array of error strings
92
     */
93
    public $errors = array();
94
95
    // Some properties used to return data by some methods
96
    /** @var array<string,int> */
97
    public $result;
98
    /** @var int */
99
    public $num;
100
101
    // Cache arrays
102
    public $cache_types_paiements = array();
103
    public $cache_conditions_paiements = array();
104
    public $cache_transport_mode = array();
105
    public $cache_availability = array();
106
    public $cache_demand_reason = array();
107
    public $cache_types_fees = array();
108
    public $cache_vatrates = array();
109
    public $cache_invoice_subtype = array();
110
111
112
    /**
113
     * Constructor
114
     *
115
     * @param DoliDB $db Database handler
116
     */
117
    public function __construct(DoliDB $db)
118
    {
119
        $this->db = $db;
120
    }
121
122
    /**
123
     * Output key field for an editable field
124
     *
125
     * @param   string  $text           Text of label or key to translate
126
     * @param   string  $htmlname       Name of select field ('edit' prefix will be added)
127
     * @param   string  $preselected    Value to show/edit (not used in this function)
128
     * @param   object  $object         Object (on the page we show)
129
     * @param   boolean $perm           Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
130
     * @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 'datehourpicker' 'checkbox:ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
131
     * @param   string  $moreparam      More param to add on a href URL.
132
     * @param   int     $fieldrequired  1 if we want to show field as mandatory using the "fieldrequired" CSS.
133
     * @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 ' '
134
     * @param   string  $paramid        Key of parameter for id ('id', 'socid')
135
     * @param   string  $help           Tooltip help
136
     * @return  string                  HTML edit field
137
     */
138
    public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
139
    {
140
        global $langs;
141
142
        $ret = '';
143
144
        // TODO change for compatibility
145
        if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !preg_match('/^select;/', $typeofdata)) {
146
            if (!empty($perm)) {
147
                $tmp = explode(':', $typeofdata);
148
                $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
149
                if ($fieldrequired) {
150
                    $ret .= '<span class="fieldrequired">';
151
                }
152
                if ($help) {
153
                    $ret .= $this->textwithpicto($langs->trans($text), $help);
154
                } else {
155
                    $ret .= $langs->trans($text);
156
                }
157
                if ($fieldrequired) {
158
                    $ret .= '</span>';
159
                }
160
                $ret .= '</div>' . "\n";
161
            } else {
162
                if ($fieldrequired) {
163
                    $ret .= '<span class="fieldrequired">';
164
                }
165
                if ($help) {
166
                    $ret .= $this->textwithpicto($langs->trans($text), $help);
167
                } else {
168
                    $ret .= $langs->trans($text);
169
                }
170
                if ($fieldrequired) {
171
                    $ret .= '</span>';
172
                }
173
            }
174
        } else {
175
            if (empty($notabletag) && $perm) {
176
                $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
177
            }
178
            if ($fieldrequired) {
179
                $ret .= '<span class="fieldrequired">';
180
            }
181
            if ($help) {
182
                $ret .= $this->textwithpicto($langs->trans($text), $help);
183
            } else {
184
                $ret .= $langs->trans($text);
185
            }
186
            if ($fieldrequired) {
187
                $ret .= '</span>';
188
            }
189
            if (!empty($notabletag)) {
190
                $ret .= ' ';
191
            }
192
            if (empty($notabletag) && $perm) {
193
                $ret .= '</td>';
194
            }
195
            if (empty($notabletag) && $perm) {
196
                $ret .= '<td class="right">';
197
            }
198
            if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm) {
199
                $ret .= '<a class="editfielda reposition" href="' . $_SERVER["PHP_SELF"] . '?action=edit' . $htmlname . '&token=' . newToken() . '&' . $paramid . '=' . $object->id . $moreparam . '">' . img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)) . '</a>';
200
            }
201
            if (!empty($notabletag) && $notabletag == 1) {
202
                if ($text) {
203
                    $ret .= ' : ';
204
                } else {
205
                    $ret .= ' ';
206
                }
207
            }
208
            if (!empty($notabletag) && $notabletag == 3) {
209
                $ret .= ' ';
210
            }
211
            if (empty($notabletag) && $perm) {
212
                $ret .= '</td>';
213
            }
214
            if (empty($notabletag) && $perm) {
215
                $ret .= '</tr></table>';
216
            }
217
        }
218
219
        return $ret;
220
    }
221
222
    /**
223
     * Output value of a field for an editable field
224
     *
225
     * @param string    $text           Text of label (not used in this function)
226
     * @param string    $htmlname       Name of select field
227
     * @param string    $value          Value to show/edit
228
     * @param CommonObject  $object         Object (that we want to show)
229
     * @param boolean   $perm           Permission to allow button to edit parameter
230
     * @param string    $typeofdata     Type of data ('string' by default, 'checkbox', 'email', 'phone', 'amount:99', 'numeric:99',
231
     *                                  'text' or 'textarea:rows:cols%', 'safehtmlstring', 'restricthtml',
232
     *                                  'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datehourpicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...')
233
     * @param string    $editvalue      When in edit mode, use this value as $value instead of value (for example, you can provide here a formatted price instead of numeric value, or a select combo). Use '' to use same than $value
234
     * @param ?CommonObject $extObject  External object ???
235
     * @param mixed     $custommsg      String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
236
     * @param string    $moreparam      More param to add on the form on action href URL parameter
237
     * @param int       $notabletag     Do no output table tags
238
     * @param string    $formatfunc     Call a specific method of $object->$formatfunc to output field in view mode (For example: 'dol_print_email')
239
     * @param string    $paramid        Key of parameter for id ('id', 'socid')
240
     * @param string    $gm             'auto' or 'tzuser' or 'tzuserrel' or 'tzserver' (when $typeofdata is a date)
241
     * @param array<string,int>     $moreoptions    Array with more options. For example array('addnowlink'=>1), array('valuealreadyhtmlescaped'=>1)
242
     * @param string    $editaction     [=''] use GETPOST default action or set action to edit mode
243
     * @return string                   HTML edit field
244
     */
245
    public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '')
246
    {
247
        global $conf, $langs;
248
249
        $ret = '';
250
251
        // Check parameters
252
        if (empty($typeofdata)) {
253
            return 'ErrorBadParameter typeofdata is empty';
254
        }
255
        // Clean parameter $typeofdata
256
        if ($typeofdata == 'datetime') {
257
            $typeofdata = 'dayhour';
258
        }
259
        $reg = array();
260
        if (preg_match('/^(\w+)\((\d+)\)$/', $typeofdata, $reg)) {
261
            if ($reg[1] == 'varchar') {
262
                $typeofdata = 'string';
263
            } elseif ($reg[1] == 'int') {
264
                $typeofdata = 'numeric';
265
            } else {
266
                return 'ErrorBadParameter ' . $typeofdata;
267
            }
268
        }
269
270
        // When option to edit inline is activated
271
        if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
272
            $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
273
        } else {
274
            if ($editaction == '') {
275
                $editaction = GETPOST('action', 'aZ09');
276
            }
277
            $editmode = ($editaction == 'edit' . $htmlname);
278
            if ($editmode) {    // edit mode
279
                $ret .= "\n";
280
                $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
281
                $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
282
                $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
283
                $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
284
                if (empty($notabletag)) {
285
                    $ret .= '<table class="nobordernopadding centpercent">';
286
                }
287
                if (empty($notabletag)) {
288
                    $ret .= '<tr><td>';
289
                }
290
                if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
291
                    $tmp = explode(':', $typeofdata);
292
                    $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
293
                } elseif (preg_match('/^(integer)/', $typeofdata)) {
294
                    $tmp = explode(':', $typeofdata);
295
                    $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
296
                    $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
297
                } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
298
                    $tmp = explode(':', $typeofdata);
299
                    $valuetoshow = price2num($editvalue ? $editvalue : $value);
300
                    $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
301
                } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
302
                    $tmp = explode(':', $typeofdata);
303
                    $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
304
                } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {    // if wysiwyg is enabled $typeofdata = 'ckeditor'
305
                    $tmp = explode(':', $typeofdata);
306
                    $cols = (empty($tmp[2]) ? '' : $tmp[2]);
307
                    $morealt = '';
308
                    if (preg_match('/%/', $cols)) {
309
                        $morealt = ' style="width: ' . $cols . '"';
310
                        $cols = '';
311
                    }
312
                    $valuetoshow = ($editvalue ? $editvalue : $value);
313
                    $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
314
                    // textarea convert automatically entities chars into simple chars.
315
                    // 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 wysiwyg is off.
316
                    $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
317
                    $ret .= dol_htmlwithnojs(dol_string_neverthesehtmltags($valuetoshow, array('textarea')));
318
                    $ret .= '</textarea>';
319
                } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
320
                    $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
321
                    $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
322
                    $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
323
                    $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
324
                } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
325
                    $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
326
                    $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
327
                    $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
328
                    $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
329
                } elseif (preg_match('/^select;/', $typeofdata)) {
330
                    $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
331
                    $arraylist = array();
332
                    foreach ($arraydata as $val) {
333
                        $tmp = explode(':', $val);
334
                        $tmpkey = str_replace('|', ':', $tmp[0]);
335
                        $arraylist[$tmpkey] = $tmp[1];
336
                    }
337
                    $ret .= $this->selectarray($htmlname, $arraylist, $value);
338
                } elseif (preg_match('/^link/', $typeofdata)) {
339
                    // TODO Not yet implemented. See code for extrafields
340
                } elseif (preg_match('/^ckeditor/', $typeofdata)) {
341
                    $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
342
                                    $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), (empty($tmp[2]) ? '' : $tmp[2]), (empty($tmp[3]) ? '100' : $tmp[3]), (empty($tmp[1]) ? 'dolibarr_notes' : $tmp[1]), 'In', (empty($tmp[5]) ? 0 : $tmp[5]), (isset($tmp[8]) ? ($tmp[8] ? true : false) : true), true, (empty($tmp[6]) ? '20' : $tmp[6]), (empty($tmp[7]) ? '100' : $tmp[7]));
343
                    $ret .= $doleditor->Create(1);
344
                } elseif ($typeofdata == 'asis') {
345
                    $ret .= ($editvalue ? $editvalue : $value);
346
                }
347
                if (empty($notabletag)) {
348
                    $ret .= '</td>';
349
                }
350
351
                // Button save-cancel
352
                if (empty($notabletag)) {
353
                    $ret .= '<td>';
354
                }
355
                //else $ret.='<div class="clearboth"></div>';
356
                $ret .= '<input type="submit" class="smallpaddingimp button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Modify") . '">';
357
                if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
358
                    $ret .= '<br>' . "\n";
359
                }
360
                $ret .= '<input type="submit" class="smallpaddingimp button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
361
                if (empty($notabletag)) {
362
                    $ret .= '</td>';
363
                }
364
365
                if (empty($notabletag)) {
366
                    $ret .= '</tr></table>' . "\n";
367
                }
368
                $ret .= '</form>' . "\n";
369
            } else {        // view mode
370
                if (preg_match('/^email/', $typeofdata)) {
371
                    $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
372
                } elseif (preg_match('/^phone/', $typeofdata)) {
373
                    $ret .= dol_print_phone($value, '_blank', 32, 1);
374
                } elseif (preg_match('/^url/', $typeofdata)) {
375
                    $ret .= dol_print_url($value, '_blank', 32, 1);
376
                } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
377
                    $ret .= ($value != '' ? price($value, 0, $langs, 0, -1, -1, $conf->currency) : '');
378
                } elseif (preg_match('/^checkbox/', $typeofdata)) {
379
                    $tmp = explode(':', $typeofdata);
380
                    $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
381
                } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
382
                    $ret .= dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($value), 1, 1, 1));
383
                } elseif (preg_match('/^(safehtmlstring|restricthtml)/', $typeofdata)) {    // 'restricthtml' is not an allowed type for editfieldval. Value is 'safehtmlstring'
384
                    $ret .= dol_htmlwithnojs(dol_string_onlythesehtmltags($value));
385
                } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
386
                    $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
387
                } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
388
                    $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
389
                } elseif (preg_match('/^select;/', $typeofdata)) {
390
                    $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
391
                    $arraylist = array();
392
                    foreach ($arraydata as $val) {
393
                        $tmp = explode(':', $val);
394
                        $arraylist[$tmp[0]] = $tmp[1];
395
                    }
396
                    $ret .= $arraylist[$value];
397
                    if ($htmlname == 'fk_product_type') {
398
                        if ($value == 0) {
399
                            $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
400
                        } else {
401
                            $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
402
                        }
403
                    }
404
                } elseif (preg_match('/^ckeditor/', $typeofdata)) {
405
                    $tmpcontent = dol_htmlentitiesbr($value);
406
                    if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
407
                        $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
408
                        $firstline = preg_replace('/[\n\r].*/', '', $firstline);
409
                        $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
410
                    }
411
                    // We don't use dol_escape_htmltag to get the html formatting active, but this need we must also
412
                    // clean data from some dangerous html
413
                    $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
414
                } else {
415
                    if (empty($moreoptions['valuealreadyhtmlescaped'])) {
416
                        $ret .= dol_escape_htmltag($value);
417
                    } else {
418
                        $ret .= $value;        // $value must be already html escaped.
419
                    }
420
                }
421
422
                // Custom format if parameter $formatfunc has been provided
423
                if ($formatfunc && method_exists($object, $formatfunc)) {
424
                    $ret = $object->$formatfunc($ret);
425
                }
426
            }
427
        }
428
        return $ret;
429
    }
430
431
    /**
432
     * Output edit in place form
433
     *
434
     * @param   string  $fieldname  Name of the field
435
     * @param   CommonObject    $object Object
436
     * @param   boolean $perm       Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
437
     * @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]'...)
438
     * @param   string  $check      Same coe than $check parameter of GETPOST()
439
     * @param   string  $morecss    More CSS
440
     * @return  string              HTML code for the edit of alternative language
441
     */
442
    public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
443
    {
444
        global $conf, $langs, $extralanguages;
445
446
        $result = '';
447
448
        // List of extra languages
449
        $arrayoflangcode = array();
450
        if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
451
            $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
452
        }
453
454
        if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
455
            if (!is_object($extralanguages)) {
456
                include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
457
                $extralanguages = new ExtraLanguages($this->db);
458
            }
459
            $extralanguages->fetch_name_extralanguages('societe');
460
461
            if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
462
                return ''; // No extralang field to show
463
            }
464
465
            $result .= '<!-- Widget for translation -->' . "\n";
466
            $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
467
            $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
468
            $result .= $s;
469
            $result .= '</div>';
470
471
            $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
472
473
            $resultforextrlang = '';
474
            foreach ($arrayoflangcode as $langcode) {
475
                $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
476
                if (empty($valuetoshow)) {
477
                    $object->fetchValuesForExtraLanguages();
478
                    //var_dump($object->array_languages);
479
                    $valuetoshow = $object->array_languages[$fieldname][$langcode];
480
                }
481
482
                $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
483
                $resultforextrlang .= $s;
484
485
                // TODO Use the showInputField() method of ExtraLanguages object
486
                if ($typeofdata == 'textarea') {
487
                    $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
488
                    $resultforextrlang .= $valuetoshow;
489
                    $resultforextrlang .= '</textarea>';
490
                } else {
491
                    $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
492
                }
493
            }
494
            $result .= $resultforextrlang;
495
496
            $result .= '</div>';
497
            $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
498
        }
499
500
        return $result;
501
    }
502
503
    /**
504
     * Output edit in place form
505
     *
506
     * @param   CommonObject    $object     Object
507
     * @param   string  $value      Value to show/edit
508
     * @param   string  $htmlname   DIV ID (field name)
509
     * @param   int     $condition  Condition to edit
510
     * @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')
511
     * @param   string  $editvalue  When in edit mode, use this value as $value instead of value
512
     * @param   ?CommonObject   $extObject  External object
513
     * @param   mixed   $custommsg  String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
514
     * @return  string              HTML edit in place
515
     */
516
    protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
517
    {
518
        $out = '';
519
520
        // Check parameters
521
        if (preg_match('/^text/', $inputType)) {
522
            $value = dol_nl2br($value);
523
        } elseif (preg_match('/^numeric/', $inputType)) {
524
            $value = price($value);
525
        } elseif ($inputType == 'day' || $inputType == 'datepicker') {
526
            $value = dol_print_date($value, 'day');
527
        }
528
529
        if ($condition) {
530
            $element = false;
531
            $table_element = false;
532
            $fk_element = false;
533
            $loadmethod = false;
534
            $savemethod = false;
535
            $ext_element = false;
536
            $button_only = false;
537
            $inputOption = '';
538
            $rows = '';
539
            $cols = '';
540
541
            if (is_object($object)) {
542
                $element = $object->element;
543
                $table_element = $object->table_element;
544
                $fk_element = $object->id;
545
            }
546
547
            if (is_object($extObject)) {
548
                $ext_element = $extObject->element;
549
            }
550
551
            if (preg_match('/^(string|email|numeric)/', $inputType)) {
552
                $tmp = explode(':', $inputType);
553
                $inputType = $tmp[0];
554
                if (!empty($tmp[1])) {
555
                    $inputOption = $tmp[1];
556
                }
557
                if (!empty($tmp[2])) {
558
                    $savemethod = $tmp[2];
559
                }
560
                $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
561
            } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
562
                $tmp = explode(':', $inputType);
563
                $inputType = $tmp[0];
564
                if (!empty($tmp[1])) {
565
                    $inputOption = $tmp[1];
566
                }
567
                if (!empty($tmp[2])) {
568
                    $savemethod = $tmp[2];
569
                }
570
571
                $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
572
            } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
573
                $tmp = explode(':', $inputType);
574
                $inputType = $tmp[0];
575
                $loadmethod = $tmp[1];
576
                if (!empty($tmp[2])) {
577
                    $savemethod = $tmp[2];
578
                }
579
                if (!empty($tmp[3])) {
580
                    $button_only = true;
581
                }
582
            } elseif (preg_match('/^textarea/', $inputType)) {
583
                $tmp = explode(':', $inputType);
584
                $inputType = $tmp[0];
585
                $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
586
                $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
587
            } elseif (preg_match('/^ckeditor/', $inputType)) {
588
                $tmp = explode(':', $inputType);
589
                $inputType = $tmp[0];
590
                $toolbar = $tmp[1];
591
                if (!empty($tmp[2])) {
592
                    $width = $tmp[2];
593
                }
594
                if (!empty($tmp[3])) {
595
                    $height = $tmp[3];
596
                }
597
                if (!empty($tmp[4])) {
598
                    $savemethod = $tmp[4];
599
                }
600
601
                if (isModEnabled('fckeditor')) {
602
                    $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
603
                } else {
604
                    $inputType = 'textarea';
605
                }
606
            }
607
608
            $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
609
            $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
610
            $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
611
            $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
612
            if (!empty($savemethod)) {
613
                $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
614
            }
615
            if (!empty($ext_element)) {
616
                $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
617
            }
618
            if (!empty($custommsg)) {
619
                if (is_array($custommsg)) {
620
                    if (!empty($custommsg['success'])) {
621
                        $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
622
                    }
623
                    if (!empty($custommsg['error'])) {
624
                        $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
625
                    }
626
                } else {
627
                    $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
628
                }
629
            }
630
            if ($inputType == 'textarea') {
631
                $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
632
                $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
633
            }
634
            $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
635
            $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
636
        } else {
637
            $out = $value;
638
        }
639
640
        return $out;
641
    }
642
643
    /**
644
     *  Show a text and picto with tooltip on text or picto.
645
     *  Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip
646
     *
647
     *  @param  string  $text               Text to show
648
     *  @param  string  $htmltext           HTML content of tooltip. Must be HTML/UTF8 encoded.
649
     *  @param  int     $tooltipon          1=tooltip on text, 2=tooltip on image, 3=tooltip on both
650
     *  @param  int     $direction          -1=image is before, 0=no image, 1=image is after
651
     *  @param  string  $img                Html code for image (use img_xxx() function to get it)
652
     *  @param  string  $extracss           Add a CSS style to td tags
653
     *  @param  int     $notabs             0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
654
     *  @param  string  $incbefore          Include code before the text
655
     *  @param  int     $noencodehtmltext   Do not encode into html entity the htmltext
656
     *  @param  string  $tooltiptrigger     ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key)
657
     *  @param  int     $forcenowrap        Force no wrap between text and picto (works with notabs=2 only)
658
     *  @return string                      Code html du tooltip (texte+picto)
659
     *  @see    textwithpicto()             Use textwithpicto() instead of textwithtooltip if you can.
660
     */
661
    public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
662
    {
663
        if ($incbefore) {
664
            $text = $incbefore . $text;
665
        }
666
        if (!$htmltext) {
667
            return $text;
668
        }
669
        $direction = (int) $direction;    // For backward compatibility when $direction was set to '' instead of 0
670
671
        $tag = 'td';
672
        if ($notabs == 2) {
673
            $tag = 'div';
674
        }
675
        if ($notabs == 3) {
676
            $tag = 'span';
677
        }
678
        // Sanitize tooltip
679
        $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
680
681
        $extrastyle = '';
682
        if ($direction < 0) {
683
            $extracss = ($extracss ? $extracss . ' ' : '') . ($notabs != 3 ? 'inline-block' : '');
684
            $extrastyle = 'padding: 0px; padding-left: 3px;';
685
        }
686
        if ($direction > 0) {
687
            $extracss = ($extracss ? $extracss . ' ' : '') . ($notabs != 3 ? 'inline-block' : '');
688
            $extrastyle = 'padding: 0px; padding-right: 3px;';
689
        }
690
691
        $classfortooltip = 'classfortooltip';
692
693
        $s = '';
694
        $textfordialog = '';
695
696
        if ($tooltiptrigger == '') {
697
            $htmltext = str_replace('"', '&quot;', $htmltext);
698
        } else {
699
            $classfortooltip = 'classfortooltiponclick';
700
            $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext">' . $htmltext . '</div>';
701
        }
702
        if ($tooltipon == 2 || $tooltipon == 3) {
703
            $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
704
            if ($tooltiptrigger == '') {
705
                $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)) . '"'; // Attribute to put on img tag to store tooltip
706
            } else {
707
                $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
708
            }
709
        } else {
710
            $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
711
        }
712
        if ($tooltipon == 1 || $tooltipon == 3) {
713
            $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ' inline-block' . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
714
            if ($tooltiptrigger == '') {
715
                $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)) . '"'; // Attribute to put on td tag to store tooltip
716
            } else {
717
                $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
718
            }
719
        } else {
720
            $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
721
        }
722
        if (empty($notabs)) {
723
            $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
724
        } elseif ($notabs == 2) {
725
            $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
726
        }
727
        // Define value if value is before
728
        if ($direction < 0) {
729
            $s .= '<' . $tag . $paramfortooltipimg;
730
            if ($tag == 'td') {
731
                $s .= ' class="valigntop" width="14"';
732
            }
733
            $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
734
        }
735
        // Use another method to help avoid having a space in value in order to use this value with jquery
736
        // Define label
737
        if ((string) $text != '') {
738
            $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
739
        }
740
        // Define value if value is after
741
        if ($direction > 0) {
742
            $s .= '<' . $tag . $paramfortooltipimg;
743
            if ($tag == 'td') {
744
                $s .= ' class="valignmiddle" width="14"';
745
            }
746
            $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
747
        }
748
        if (empty($notabs)) {
749
            $s .= '</tr></table>';
750
        } elseif ($notabs == 2) {
751
            $s .= '</div>';
752
        }
753
754
        return $s;
755
    }
756
757
    /**
758
     * Show a text with a picto and a tooltip on picto
759
     *
760
     * @param   string      $text               Text to show
761
     * @param   string      $htmltext           Content of tooltip
762
     * @param   int         $direction          1=Icon is after text, -1=Icon is before text, 0=no icon
763
     * @param   string      $type               Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none'
764
     * @param   string      $extracss           Add a CSS style to td, div or span tag
765
     * @param   int         $noencodehtmltext   Do not encode into html entity the htmltext
766
     * @param   int         $notabs             0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
767
     * @param   string      $tooltiptrigger     ''=Tooltip on hover and hidden on smartphone, 'abconsmartphone'=Tooltip on hover and on click on smartphone, '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')
768
     * @param   int         $forcenowrap        Force no wrap between text and picto (works with notabs=2 only)
769
     * @return  string                          HTML code of text, picto, tooltip
770
     */
771
    public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
772
    {
773
        global $conf, $langs;
774
775
        //For backwards compatibility
776
        if ($type == '0') {
777
            $type = 'info';
778
        } elseif ($type == '1') {
779
            $type = 'help';
780
        }
781
        // Clean parameters
782
        $tooltiptrigger = preg_replace('/[^a-z0-9]/i', '', $tooltiptrigger);
783
784
        if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
785
            $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
786
        }
787
        $alt = '';
788
        if ($tooltiptrigger) {
789
            $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
790
        }
791
792
        // If info or help with no javascript, show only text
793
        if (empty($conf->use_javascript_ajax)) {
794
            if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
795
                return $text;
796
            } else {
797
                $alt = $htmltext;
798
                $htmltext = '';
799
            }
800
        }
801
802
        // If info or help with smartphone, show only text (tooltip hover can't works)
803
        if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
804
            if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
805
                return $text;
806
            }
807
        }
808
        // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
809
        //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
810
        //{
811
        //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
812
        //}
813
814
        $img = '';
815
        if ($type == 'info') {
816
            $img = img_help(0, $alt);
817
        } elseif ($type == 'help') {
818
            $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
819
        } elseif ($type == 'helpclickable') {
820
            $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
821
        } elseif ($type == 'superadmin') {
822
            // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
823
            $img = img_picto($alt, 'redstar');
824
        } elseif ($type == 'admin') {
825
            // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
826
            $img = img_picto($alt, 'star');
827
        } elseif ($type == 'warning') {
828
            $img = img_warning($alt);
829
        } elseif ($type != 'none') {
830
            // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
831
            $img = img_picto($alt, $type); // $type can be an image path
832
        }
833
834
        return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
835
    }
836
837
    /**
838
     * Generate select HTML to choose massaction
839
     *
840
     * @param string    $selected       Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default.
841
     * @param array<string,string>  $arrayofaction  array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action.
842
     * @param int       $alwaysvisible  1=select button always visible
843
     * @param string    $name           Name for massaction
844
     * @param string    $cssclass       CSS class used to check for select
845
     * @return string|void              Select list
846
     */
847
    public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
848
    {
849
        global $conf, $langs, $hookmanager;
850
851
        $disabled = 0;
852
        $ret = '<div class="centpercent center">';
853
        $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
854
855
        // 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.
856
        $parameters = array();
857
        $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
858
        // check if there is a mass action
859
860
        if (is_array($arrayofaction) && count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
861
            return;
862
        }
863
        if (empty($reshook)) {
864
            $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
865
            if (is_array($arrayofaction)) {
866
                foreach ($arrayofaction as $code => $label) {
867
                    $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
868
                }
869
            }
870
        }
871
        $ret .= $hookmanager->resPrint;
872
873
        $ret .= '</select>';
874
875
        if (empty($conf->dol_optimize_smallscreen)) {
876
            $ret .= ajax_combobox('.' . $name . 'select');
877
        }
878
879
        // 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
880
        $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.
881
        $ret .= '<input type="submit" disabled name="confirmmassaction"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display: none"') . ' class="reposition button smallpaddingimp' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'confirmed" value="' . dol_escape_htmltag($langs->trans("Confirm")) . '">';
882
        $ret .= '</div>';
883
884
        if (!empty($conf->use_javascript_ajax)) {
885
            $ret .= '<!-- JS CODE TO ENABLE mass action select -->
886
    		<script nonce="' . getNonce() . '">
887
                        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 */
888
        		{
889
        			atleastoneselected=0;
890
                                jQuery("."+cssclass).each(function( index ) {
891
    	  				/* console.log( index + ": " + $( this ).text() ); */
892
    	  				if ($(this).is(\':checked\')) atleastoneselected++;
893
    	  			});
894
895
					console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
896
897
    	  			if (atleastoneselected || ' . $alwaysvisible . ')
898
    	  			{
899
                                    jQuery("."+name).show();
900
        			    ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
901
        			    ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
902
    	  			}
903
    	  			else
904
    	  			{
905
                                    jQuery("."+name).hide();
906
                                    jQuery("."+name+"other").hide();
907
    	            }
908
        		}
909
910
        	jQuery(document).ready(function () {
911
                    initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
912
                    jQuery(".' . $cssclass . '").click(function() {
913
                        initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
914
                    });
915
                        jQuery(".' . $name . 'select").change(function() {
916
        			var massaction = $( this ).val();
917
        			var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
918
        			if (massaction == "builddoc")
919
                    {
920
                        urlform = urlform + "#show_files";
921
    	            }
922
        			$( this ).closest("form").attr("action", urlform);
923
                    console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
924
        	        /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
925
        			if ($(this).val() != \'0\')
926
    	  			{
927
                                        jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
928
										jQuery(".' . $name . 'other").hide();	/* To disable if another div was open */
929
                                        jQuery(".' . $name . '"+massaction).show();
930
    	  			}
931
    	  			else
932
    	  			{
933
                                        jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
934
										jQuery(".' . $name . 'other").hide();	/* To disable any div open */
935
    	  			}
936
    	        });
937
        	});
938
    		</script>
939
        	';
940
        }
941
942
        return $ret;
943
    }
944
945
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
946
947
    /**
948
     *  Return combo list of activated countries, into language of user
949
     *
950
     * @param string        $selected               Id or Code or Label of preselected country
951
     * @param string        $htmlname               Name of html select object
952
     * @param string        $htmloption             More html options on select object
953
     * @param integer       $maxlength              Max length for labels (0=no limit)
954
     * @param string        $morecss                More css class
955
     * @param string        $usecodeaskey           ''=Use id as key (default), 'code3'=Use code on 3 alpha as key, 'code2"=Use code on 2 alpha as key
956
     * @param int<0,1>|string   $showempty              Show empty choice
957
     * @param int<0,1>      $disablefavorites       1=Disable favorites,
958
     * @param int<0,1>      $addspecialentries      1=Add dedicated entries for group of countries (like 'European Economic Community', ...)
959
     * @param string[]      $exclude_country_code   Array of country code (iso2) to exclude
960
     * @param int<0,1>      $hideflags              Hide flags
961
     * @return string                               HTML string with select
962
     */
963
    public function select_country($selected = '', $htmlname = 'country_id', $htmloption = '', $maxlength = 0, $morecss = 'minwidth300', $usecodeaskey = '', $showempty = 1, $disablefavorites = 0, $addspecialentries = 0, $exclude_country_code = array(), $hideflags = 0)
964
    {
965
		// phpcs:enable
966
        global $conf, $langs, $mysoc;
967
968
        $langs->load("dict");
969
970
        $out = '';
971
        $countryArray = array();
972
        $favorite = array();
973
        $label = array();
974
        $atleastonefavorite = 0;
975
976
        $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
977
        $sql .= " FROM " . $this->db->prefix() . "c_country";
978
        $sql .= " WHERE active > 0";
979
        //$sql.= " ORDER BY code ASC";
980
981
        dol_syslog(get_only_class($this) . "::select_country", LOG_DEBUG);
982
        $resql = $this->db->query($sql);
983
        if ($resql) {
984
            $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
985
            $num = $this->db->num_rows($resql);
986
            $i = 0;
987
            if ($num) {
988
                while ($i < $num) {
989
                    $obj = $this->db->fetch_object($resql);
990
991
                    $countryArray[$i]['rowid'] = $obj->rowid;
992
                    $countryArray[$i]['code_iso'] = $obj->code_iso;
993
                    $countryArray[$i]['code_iso3'] = $obj->code_iso3;
994
                    $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 : ''));
995
                    $countryArray[$i]['favorite'] = $obj->favorite;
996
                    $countryArray[$i]['eec'] = $obj->eec;
997
                    $favorite[$i] = $obj->favorite;
998
                    $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
999
                    $i++;
1000
                }
1001
1002
                if (empty($disablefavorites)) {
1003
                    $array1_sort_order = SORT_DESC;
1004
                    $array2_sort_order = SORT_ASC;
1005
                    array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
1006
                } else {
1007
                    $countryArray = dol_sort_array($countryArray, 'label');
1008
                }
1009
1010
                if ($showempty) {
1011
                    if (is_numeric($showempty)) {
1012
                        $out .= '<option value="">&nbsp;</option>' . "\n";
1013
                    } else {
1014
                        $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
1015
                    }
1016
                }
1017
1018
                if ($addspecialentries) {    // Add dedicated entries for groups of countries
1019
                    //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
1020
                    $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1021
                    $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
1022
                    if ($mysoc->isInEEC()) {
1023
                        $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1024
                    }
1025
                    $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
1026
                    $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1027
                }
1028
1029
                foreach ($countryArray as $row) {
1030
                    //if (empty($showempty) && empty($row['rowid'])) continue;
1031
                    if (empty($row['rowid'])) {
1032
                        continue;
1033
                    }
1034
                    if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1035
                        continue; // exclude some countries
1036
                    }
1037
1038
                    if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1039
                        $atleastonefavorite++;
1040
                    }
1041
                    if (empty($row['favorite']) && $atleastonefavorite) {
1042
                        $atleastonefavorite = 0;
1043
                        $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1044
                    }
1045
1046
                    $labeltoshow = '';
1047
                    if ($row['label']) {
1048
                        $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1049
                    } else {
1050
                        $labeltoshow .= '&nbsp;';
1051
                    }
1052
                    if ($row['code_iso']) {
1053
                        $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1054
                        if (empty($hideflags)) {
1055
                            $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1056
                            $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1057
                        }
1058
                    }
1059
1060
                    if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1061
                        $out .= '<option value="' . ($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']) . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '" data-eec="' . ((int) $row['eec']) . '">';
1062
                    } else {
1063
                        $out .= '<option value="' . ($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']) . '" data-html="' . dol_escape_htmltag($labeltoshow) . '" data-eec="' . ((int) $row['eec']) . '">';
1064
                    }
1065
                    $out .= $labeltoshow;
1066
                    $out .= '</option>' . "\n";
1067
                }
1068
            }
1069
            $out .= '</select>';
1070
        } else {
1071
            dol_print_error($this->db);
1072
        }
1073
1074
        // Make select dynamic
1075
        include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1076
        $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1077
1078
        return $out;
1079
    }
1080
1081
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1082
1083
    /**
1084
     *  Return select list of incoterms
1085
     *
1086
     * @param   string  $selected               Id or Code of preselected incoterm
1087
     * @param   string  $location_incoterms     Value of input location
1088
     * @param   string  $page                   Defined the form action
1089
     * @param   string  $htmlname               Name of html select object
1090
     * @param   string  $htmloption             Options html on select object
1091
     * @param   int<0,1>    $forcecombo             Force to load all values and output a standard combobox (with no beautification)
1092
     * @param   array<array{method:string,url:string,htmlname:string,params:array<string,string>}>  $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')))
1093
     * @param   int<0,1>    $disableautocomplete    Disable autocomplete
1094
     * @return  string                          HTML string with select and input
1095
     */
1096
    public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1097
    {
1098
		// phpcs:enable
1099
        global $conf, $langs;
1100
1101
        $langs->load("dict");
1102
1103
        $out = '';
1104
        $moreattrib = '';
1105
        $incotermArray = array();
1106
1107
        $sql = "SELECT rowid, code";
1108
        $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1109
        $sql .= " WHERE active > 0";
1110
        $sql .= " ORDER BY code ASC";
1111
1112
        dol_syslog(get_only_class($this) . "::select_incoterm", LOG_DEBUG);
1113
        $resql = $this->db->query($sql);
1114
        if ($resql) {
1115
            if ($conf->use_javascript_ajax && !$forcecombo) {
1116
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1117
                $out .= ajax_combobox($htmlname, $events);
1118
            }
1119
1120
            if (!empty($page)) {
1121
                $out .= '<form method="post" action="' . $page . '">';
1122
                $out .= '<input type="hidden" name="action" value="set_incoterms">';
1123
                $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1124
            }
1125
1126
            $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1127
            $out .= '<option value="0">&nbsp;</option>';
1128
            $num = $this->db->num_rows($resql);
1129
            $i = 0;
1130
            if ($num) {
1131
                while ($i < $num) {
1132
                    $obj = $this->db->fetch_object($resql);
1133
                    $incotermArray[$i]['rowid'] = $obj->rowid;
1134
                    $incotermArray[$i]['code'] = $obj->code;
1135
                    $i++;
1136
                }
1137
1138
                foreach ($incotermArray as $row) {
1139
                    if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1140
                        $out .= '<option value="' . $row['rowid'] . '" selected>';
1141
                    } else {
1142
                        $out .= '<option value="' . $row['rowid'] . '">';
1143
                    }
1144
1145
                    if ($row['code']) {
1146
                        $out .= $row['code'];
1147
                    }
1148
1149
                    $out .= '</option>';
1150
                }
1151
            }
1152
            $out .= '</select>';
1153
1154
            if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1155
                $out .= ajax_multiautocompleter('location_incoterms', array(), constant('BASE_URL') . '/core/ajax/locationincoterms.php') . "\n";
1156
                $moreattrib .= ' autocomplete="off"';
1157
            }
1158
            $out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1159
1160
            if (!empty($page)) {
1161
                $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1162
            }
1163
        } else {
1164
            dol_print_error($this->db);
1165
        }
1166
1167
        return $out;
1168
    }
1169
1170
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1171
1172
    /**
1173
     * Return list of types of lines (product or service)
1174
     * Example: 0=product, 1=service, 9=other (for external module)
1175
     *
1176
     * @param   string              $selected   Preselected type
1177
     * @param   string              $htmlname   Name of field in html form
1178
     * @param   int<0,1>|string     $showempty  Add an empty field
1179
     * @param   int                 $hidetext   Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
1180
     * @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')
1181
     * @param   string              $morecss    More css
1182
     * @return  void
1183
     */
1184
    public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "")
1185
    {
1186
		// phpcs:enable
1187
        global $langs;
1188
1189
        // If product & services are enabled or both disabled.
1190
        if (
1191
            $forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1192
            || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))
1193
        ) {
1194
            if (empty($hidetext)) {
1195
                print $langs->trans("Type") . ': ';
1196
            }
1197
            print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1198
            if ($showempty) {
1199
                print '<option value="-1"';
1200
                if ($selected == -1) {
1201
                    print ' selected';
1202
                }
1203
                print '>';
1204
                if (is_numeric($showempty)) {
1205
                    print '&nbsp;';
1206
                } else {
1207
                    print $showempty;
1208
                }
1209
                print '</option>';
1210
            }
1211
1212
            print '<option value="0"';
1213
            if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1214
                print ' selected';
1215
            }
1216
            print '>' . $langs->trans("Product");
1217
1218
            print '<option value="1"';
1219
            if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1220
                print ' selected';
1221
            }
1222
            print '>' . $langs->trans("Service");
1223
1224
            print '</select>';
1225
            print ajax_combobox('select_' . $htmlname);
1226
            //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1227
        }
1228
        if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1229
            print $langs->trans("Service");
1230
            print '<input type="hidden" name="' . $htmlname . '" value="1">';
1231
        }
1232
        if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1233
            print $langs->trans("Product");
1234
            print '<input type="hidden" name="' . $htmlname . '" value="0">';
1235
        }
1236
        if ($forceall < 0) {    // This should happened only for contracts when both predefined product and service are disabled.
1237
            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
1238
        }
1239
    }
1240
1241
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1242
1243
    /**
1244
     *    Load into cache cache_types_fees, array of types of fees
1245
     *
1246
     * @return     int             Nb of lines loaded, <0 if KO
1247
     */
1248
    public function load_cache_types_fees()
1249
    {
1250
		// phpcs:enable
1251
        global $langs;
1252
1253
        $num = count($this->cache_types_fees);
1254
        if ($num > 0) {
1255
            return 0; // Cache already loaded
1256
        }
1257
1258
        dol_syslog(__METHOD__, LOG_DEBUG);
1259
1260
        $langs->load("trips");
1261
1262
        $sql = "SELECT c.code, c.label";
1263
        $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1264
        $sql .= " WHERE active > 0";
1265
1266
        $resql = $this->db->query($sql);
1267
        if ($resql) {
1268
            $num = $this->db->num_rows($resql);
1269
            $i = 0;
1270
1271
            while ($i < $num) {
1272
                $obj = $this->db->fetch_object($resql);
1273
1274
                // Si traduction existe, on l'utilise, sinon on prend le libelle par default
1275
                $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1276
                $this->cache_types_fees[$obj->code] = $label;
1277
                $i++;
1278
            }
1279
1280
            asort($this->cache_types_fees);
1281
1282
            return $num;
1283
        } else {
1284
            dol_print_error($this->db);
1285
            return -1;
1286
        }
1287
    }
1288
1289
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1290
1291
    /**
1292
     *    Return list of types of notes
1293
     *
1294
     * @param string $selected Preselected type
1295
     * @param string $htmlname Name of field in form
1296
     * @param int $showempty Add an empty field
1297
     * @return    void
1298
     */
1299
    public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1300
    {
1301
		// phpcs:enable
1302
        global $user, $langs;
1303
1304
        dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1305
1306
        $this->load_cache_types_fees();
1307
1308
        print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1309
        if ($showempty) {
1310
            print '<option value="-1"';
1311
            if ($selected == -1) {
1312
                print ' selected';
1313
            }
1314
            print '>&nbsp;</option>';
1315
        }
1316
1317
        foreach ($this->cache_types_fees as $key => $value) {
1318
            print '<option value="' . $key . '"';
1319
            if ($key == $selected) {
1320
                print ' selected';
1321
            }
1322
            print '>';
1323
            print $value;
1324
            print '</option>';
1325
        }
1326
1327
        print '</select>';
1328
        if ($user->admin) {
1329
            print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1330
        }
1331
    }
1332
1333
1334
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1335
1336
    /**
1337
     *  Output html form to select a third party
1338
     *  This call select_thirdparty_list() or ajax depending on setup. This component is not able to support multiple select.
1339
     *
1340
     * @param int|string    $selected               Preselected ID
1341
     * @param string        $htmlname               Name of field in form
1342
     * @param string        $filter                 Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>()] are allowed here. Example: ((s.client:IN:1,3) AND (s.status:=:1)). Do not use a filter coming from input of users.
1343
     * @param string|int<1,1>   $showempty          Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
1344
     * @param int           $showtype               Show third party type in combolist (customer, prospect or supplier)
1345
     * @param int           $forcecombo             Force to load all values and output a standard combobox (with no beautification)
1346
     * @param array<array{method:string,url:string,htmlname:string,params:array<string,string>}>    $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')))
1347
     * @param int           $limit                  Maximum number of elements
1348
     * @param string        $morecss                Add more css styles to the SELECT component
1349
     * @param string        $moreparam              Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1350
     * @param string        $selected_input_value   Value of preselected input text (for use with ajax)
1351
     * @param int<0,3>      $hidelabel              Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
1352
     * @param array<string,string|string[]> $ajaxoptions        Options for ajax_autocompleter
1353
     * @param bool          $multiple               add [] in the name of element and add 'multiple' attribute (not working with ajax_autocompleter)
1354
     * @param string[]      $excludeids             Exclude IDs from the select combo
1355
     * @param int<0,1>      $showcode               Show code
1356
     * @return string                               HTML string with select box for thirdparty.
1357
     */
1358
    public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false, $excludeids = array(), $showcode = 0)
1359
    {
1360
		// phpcs:enable
1361
        global $conf, $langs;
1362
1363
        $out = '';
1364
1365
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1366
            if (is_null($ajaxoptions)) {
1367
                $ajaxoptions = array();
1368
            }
1369
1370
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/ajax.lib.php';
1371
1372
            // No immediate load of all database
1373
            $placeholder = '';
1374
            if ($selected && empty($selected_input_value)) {
1375
                $societetmp = new Societe($this->db);
1376
                $societetmp->fetch($selected);
1377
                $selected_input_value = $societetmp->name;
1378
                unset($societetmp);
1379
            }
1380
1381
            // mode 1
1382
            $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($excludeids) ? '' : '&excludeids=' . implode(',', $excludeids)) . ($showtype ? '&showtype=' . urlencode((string) ($showtype)) : '') . ($showcode ? '&showcode=' . urlencode((string) ($showcode)) : '');
1383
1384
            $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1385
            if (empty($hidelabel)) {
1386
                print $langs->trans("RefOrLabel") . ' : ';
1387
            } elseif ($hidelabel > 1) {
1388
                $placeholder = $langs->trans("RefOrLabel");
1389
                if ($hidelabel == 2) {
1390
                    $out .= img_picto($langs->trans("Search"), 'search');
1391
                }
1392
            }
1393
            $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('THIRDPARTY_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
1394
            if ($hidelabel == 3) {
1395
                $out .= img_picto($langs->trans("Search"), 'search');
1396
            }
1397
1398
            $out .= ajax_event($htmlname, $events);
1399
1400
            $out .= ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/societe/ajax/company.php', $urloption, getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1401
        } else {
1402
            // Immediate load of all database
1403
            $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1404
        }
1405
1406
        return $out;
1407
    }
1408
1409
1410
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1411
1412
    /**
1413
     * Output html form to select a contact
1414
     * This call select_contacts() or ajax depending on setup. This component is not able to support multiple select.
1415
     *
1416
     * Return HTML code of the SELECT of list of all contacts (for a third party or all).
1417
     * This also set the number of contacts found into $this->num
1418
     *
1419
     * @param   int             $socid              Id of third party or 0 for all or -1 for empty list
1420
     * @param   int|string      $selected           ID of preselected contact id
1421
     * @param   string          $htmlname           Name of HTML field ('none' for a not editable field)
1422
     * @param   int<0,3>|string $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
1423
     * @param   string          $exclude            List of contacts id to exclude
1424
     * @param   string          $limitto            Not used
1425
     * @param   integer         $showfunction       Add function into label
1426
     * @param   string          $morecss            Add more class to class style
1427
     * @param   bool            $nokeyifsocid       When 1, we force the option "Press a key to show list" to 0 if there is a value for $socid
1428
     * @param   integer         $showsoc            Add company into label
1429
     * @param   int             $forcecombo         1=Force to use combo box (so no ajax beautify effect)
1430
     * @param   array<array{method:string,url:string,htmlname:string,params:array<string,string>}>  $events     Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1431
     * @param   string          $moreparam          Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1432
     * @param   string          $htmlid             Html id to use instead of htmlname
1433
     * @param   string          $selected_input_value   Value of preselected input text (for use with ajax)
1434
     * @param   string          $filter             Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>()] are allowed here. Example: ((s.client:IN:1,3) AND (s.status:=:1)). Do not use a filter coming from input of users.
1435
     * @return  int|string                          Return integer <0 if KO, HTML with select string if OK.
1436
     */
1437
    public function select_contact($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $nokeyifsocid = true, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $selected_input_value = '', $filter = '')
1438
    {
1439
		// phpcs:enable
1440
1441
        global $conf, $langs;
1442
1443
        $out = '';
1444
1445
        $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1446
        if ($nokeyifsocid && $socid > 0) {
1447
            $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1448
        }
1449
1450
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1451
            if (is_null($events)) {
1452
                $events = array();
1453
            }
1454
1455
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/ajax.lib.php';
1456
1457
            // No immediate load of all database
1458
            $placeholder = '';
1459
            if ($selected && empty($selected_input_value)) {
1460
                $contacttmp = new Contact($this->db);
1461
                $contacttmp->fetch($selected);
1462
                $selected_input_value = $contacttmp->getFullName($langs);
1463
                unset($contacttmp);
1464
            }
1465
            if (!is_numeric($showempty)) {
1466
                $placeholder = $showempty;
1467
            }
1468
1469
            // mode 1
1470
            $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1471
1472
            $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1473
1474
            $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('CONTACT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
1475
1476
            $out .= ajax_event($htmlname, $events);
1477
1478
            $out .= ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/contact/ajax/contact.php', $urloption, getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT'), 0, $events);
1479
        } else {
1480
            // Immediate load of all database
1481
            $multiple = false;
1482
            $disableifempty = 0;
1483
            $options_only = false;
1484
            $limitto = '';
1485
1486
            $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty);
1487
        }
1488
1489
        $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1490
1491
        return $out;
1492
    }
1493
1494
1495
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1496
1497
    /**
1498
     *  Output html form to select a third party.
1499
     *  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.
1500
     *
1501
     * @param string            $selected       Preselected type
1502
     * @param string            $htmlname       Name of field in form
1503
     * @param string            $filter         Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here, example: 's.rowid <> x'
1504
     *                                          If you need parenthesis, use the Universal Filter Syntax, example: '(s.client:in:1,3)'
1505
     *                                          Do not use a filter coming from input of users.
1506
     * @param string|int<0,1>   $showempty      Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty')
1507
     * @param int<0,1>          $showtype       Show third party type in combolist (customer, prospect or supplier)
1508
     * @param int               $forcecombo     Force to use standard HTML select component without beautification
1509
     * @param array<array{method:string,url:string,htmlname:string,params:array<string,string>}>    $events     Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1510
     * @param string            $filterkey      Filter on key value
1511
     * @param int<0,1>          $outputmode     0=HTML select string, 1=Array
1512
     * @param int               $limit          Limit number of answers
1513
     * @param string            $morecss        Add more css styles to the SELECT component
1514
     * @param string            $moreparam      Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1515
     * @param bool              $multiple       add [] in the name of element and add 'multiple' attribute
1516
     * @param string[]          $excludeids     Exclude IDs from the select combo
1517
     * @param int<0,1>          $showcode       Show code in list
1518
     * @return array<int,array{key:int,value:string,label:string,labelhtml:string}>|string              HTML string with
1519
     * @see select_company()
1520
     */
1521
    public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false, $excludeids = array(), $showcode = 0)
1522
    {
1523
		// phpcs:enable
1524
        global $user, $langs;
1525
        global $hookmanager;
1526
1527
        $out = '';
1528
        $num = 0;
1529
        $outarray = array();
1530
1531
        if ($selected === '') {
1532
            $selected = array();
1533
        } elseif (!is_array($selected)) {
1534
            $selected = array($selected);
1535
        }
1536
1537
        // Clean $filter that may contains sql conditions so sql code
1538
        if (function_exists('testSqlAndScriptInject')) {
1539
            if (testSqlAndScriptInject($filter, 3) > 0) {
1540
                $filter = '';
1541
                return 'SQLInjectionTryDetected';
1542
            }
1543
        }
1544
1545
        if ($filter != '') {    // If a filter was provided
1546
            if (preg_match('/[\(\)]/', $filter)) {
1547
                // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
1548
                $errormsg = '';
1549
                $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
1550
1551
                // Redo clean $filter that may contains sql conditions so sql code
1552
                if (function_exists('testSqlAndScriptInject')) {
1553
                    if (testSqlAndScriptInject($filter, 3) > 0) {
1554
                        $filter = '';
1555
                        return 'SQLInjectionTryDetected';
1556
                    }
1557
                }
1558
            } else {
1559
                // If not, we do nothing. We already know that there is no parenthesis
1560
                // TODO Disallow this case in a future.
1561
                dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Syntax.", LOG_WARNING);
1562
            }
1563
        }
1564
1565
        // We search companies
1566
        $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1567
        if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
1568
            $sql .= ", s.address, s.zip, s.town";
1569
            $sql .= ", dictp.code as country_code";
1570
        }
1571
        $sql .= " FROM " . $this->db->prefix() . "societe as s";
1572
        if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
1573
            $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
1574
        }
1575
        if (!$user->hasRight('societe', 'client', 'voir')) {
1576
            $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
1577
        }
1578
        $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
1579
        if (!empty($user->socid)) {
1580
            $sql .= " AND s.rowid = " . ((int) $user->socid);
1581
        }
1582
        if ($filter) {
1583
            // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
1584
            // if not, by testSqlAndScriptInject() only.
1585
            $sql .= " AND (" . $filter . ")";
1586
        }
1587
        if (!$user->hasRight('societe', 'client', 'voir')) {
1588
            $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
1589
        }
1590
        if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
1591
            $sql .= " AND s.status <> 0";
1592
        }
1593
        if (!empty($excludeids)) {
1594
            $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
1595
        }
1596
        // Add where from hooks
1597
        $parameters = array();
1598
        $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1599
        $sql .= $hookmanager->resPrint;
1600
        // Add criteria
1601
        if ($filterkey && $filterkey != '') {
1602
            $sql .= " AND (";
1603
            $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1604
            // For natural search
1605
            $search_crit = explode(' ', $filterkey);
1606
            $i = 0;
1607
            if (count($search_crit) > 1) {
1608
                $sql .= "(";
1609
            }
1610
            foreach ($search_crit as $crit) {
1611
                if ($i > 0) {
1612
                    $sql .= " AND ";
1613
                }
1614
                $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
1615
                $i++;
1616
            }
1617
            if (count($search_crit) > 1) {
1618
                $sql .= ")";
1619
            }
1620
            if (isModEnabled('barcode')) {
1621
                $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1622
            }
1623
            $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1624
            $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1625
            $sql .= ")";
1626
        }
1627
        $sql .= $this->db->order("nom", "ASC");
1628
        $sql .= $this->db->plimit($limit, 0);
1629
1630
        // Build output string
1631
        dol_syslog(get_only_class($this) . "::select_thirdparty_list", LOG_DEBUG);
1632
        $resql = $this->db->query($sql);
1633
        if ($resql) {
1634
            // Construct $out and $outarray
1635
            $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . '>' . "\n";
1636
1637
            $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1638
            if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
1639
                // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1640
                //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1641
                if ($showempty && !is_numeric($showempty)) {
1642
                    $textifempty = $langs->trans($showempty);
1643
                } else {
1644
                    $textifempty .= $langs->trans("All");
1645
                }
1646
            }
1647
            if ($showempty) {
1648
                $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
1649
            }
1650
1651
            $companytemp = new Societe($this->db);
1652
1653
            $num = $this->db->num_rows($resql);
1654
            $i = 0;
1655
            if ($num) {
1656
                while ($i < $num) {
1657
                    $obj = $this->db->fetch_object($resql);
1658
                    $label = '';
1659
                    if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
1660
                        if (($obj->client) && (!empty($obj->code_client))) {
1661
                            $label = $obj->code_client . ' - ';
1662
                        }
1663
                        if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1664
                            $label .= $obj->code_fournisseur . ' - ';
1665
                        }
1666
                        $label .= ' ' . $obj->name;
1667
                    } else {
1668
                        $label = $obj->name;
1669
                    }
1670
1671
                    if (!empty($obj->name_alias)) {
1672
                        $label .= ' (' . $obj->name_alias . ')';
1673
                    }
1674
1675
                    if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
1676
                        $label .= ' - ' . $obj->tva_intra;
1677
                    }
1678
1679
                    $labelhtml = $label;
1680
1681
                    if ($showtype) {
1682
                        $companytemp->id = $obj->rowid;
1683
                        $companytemp->client = $obj->client;
1684
                        $companytemp->fournisseur = $obj->fournisseur;
1685
                        $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
1686
                        if ($tmptype) {
1687
                            $labelhtml .= ' ' . $tmptype;
1688
                        }
1689
1690
                        if ($obj->client || $obj->fournisseur) {
1691
                            $label .= ' (';
1692
                        }
1693
                        if ($obj->client == 1 || $obj->client == 3) {
1694
                            $label .= $langs->trans("Customer");
1695
                        }
1696
                        if ($obj->client == 2 || $obj->client == 3) {
1697
                            $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
1698
                        }
1699
                        if ($obj->fournisseur) {
1700
                            $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
1701
                        }
1702
                        if ($obj->client || $obj->fournisseur) {
1703
                            $label .= ')';
1704
                        }
1705
                    }
1706
1707
                    if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
1708
                        $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
1709
                        if (!empty($obj->country_code)) {
1710
                            $s .= ', ' . $langs->trans('Country' . $obj->country_code);
1711
                        }
1712
                        $label .= $s;
1713
                        $labelhtml .= $s;
1714
                    }
1715
1716
                    if (empty($outputmode)) {
1717
                        if (in_array($obj->rowid, $selected)) {
1718
                            $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
1719
                        } else {
1720
                            $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
1721
                        }
1722
                    } else {
1723
                        array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
1724
                    }
1725
1726
                    $i++;
1727
                    if (($i % 10) == 0) {
1728
                        $out .= "\n";
1729
                    }
1730
                }
1731
            }
1732
            $out .= '</select>' . "\n";
1733
            if (!$forcecombo) {
1734
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1735
                $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
1736
            }
1737
        } else {
1738
            dol_print_error($this->db);
1739
        }
1740
1741
        $this->result = array('nbofthirdparties' => $num);
1742
1743
        if ($outputmode) {
1744
            return $outarray;
1745
        }
1746
        return $out;
1747
    }
1748
1749
1750
    /**
1751
     * Return HTML code of the SELECT of list of all contacts (for a third party or all).
1752
     * This also set the number of contacts found into $this->num
1753
     * Note: you must use the select_contact() to get the component to select a contact. This function must only be called by select_contact.
1754
     *
1755
     * @param   int                 $socid              Id of third party or 0 for all or -1 for empty list
1756
     * @param   array|int|string    $selected           Array of ID of preselected contact id
1757
     * @param   string              $htmlname           Name of HTML field ('none' for a not editable field)
1758
     * @param   int<0,3>|string     $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
1759
     * @param   string              $exclude            List of contacts id to exclude
1760
     * @param   string              $limitto            Disable answers that are not id in this array list
1761
     * @param   integer             $showfunction       Add function into label
1762
     * @param   string              $morecss            Add more class to class style
1763
     * @param   int                 $options_only       1=Return options only (for ajax treatment), 2=Return array
1764
     * @param   integer             $showsoc            Add company into label
1765
     * @param   int                 $forcecombo         Force to use combo box (so no ajax beautify effect)
1766
     * @param   array<array{method:string,url:string,htmlname:string,params:array<string,string>}>  $events     Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
1767
     * @param   string              $moreparam          Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
1768
     * @param   string              $htmlid             Html id to use instead of htmlname
1769
     * @param   bool                $multiple           add [] in the name of element and add 'multiple' attribute
1770
     * @param   integer             $disableifempty     Set tag 'disabled' on select if there is no choice
1771
     * @param   string              $filter             Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here, example: 's.rowid <> x'
1772
     *                                                  If you need parenthesis, use the Universal Filter Syntax, example: '(s.client:in:1,3)'
1773
     *                                                  Do not use a filter coming from input of users.
1774
     * @return  int|string|array<int,array{key:int,value:string,label:string,labelhtml:string}>     Return integer <0 if KO, HTML with select string if OK.
1775
     */
1776
    public function selectcontacts($socid, $selected = array(), $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = 0, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0, $filter = '')
1777
    {
1778
        global $conf, $langs, $hookmanager, $action;
1779
1780
        $langs->load('companies');
1781
1782
        if (empty($htmlid)) {
1783
            $htmlid = $htmlname;
1784
        }
1785
        $num = 0;
1786
        $out = '';
1787
        $outarray = array();
1788
1789
        if ($selected === '') {
1790
            $selected = array();
1791
        } elseif (!is_array($selected)) {
1792
            $selected = array((int) $selected);
1793
        }
1794
1795
        // Clean $filter that may contains sql conditions so sql code
1796
        if (function_exists('testSqlAndScriptInject')) {
1797
            if (testSqlAndScriptInject($filter, 3) > 0) {
1798
                $filter = '';
1799
                return 'SQLInjectionTryDetected';
1800
            }
1801
        }
1802
1803
        if ($filter != '') {    // If a filter was provided
1804
            if (preg_match('/[\(\)]/', $filter)) {
1805
                // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
1806
                $errormsg = '';
1807
                $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
1808
1809
                // Redo clean $filter that may contains sql conditions so sql code
1810
                if (function_exists('testSqlAndScriptInject')) {
1811
                    if (testSqlAndScriptInject($filter, 3) > 0) {
1812
                        $filter = '';
1813
                        return 'SQLInjectionTryDetected';
1814
                    }
1815
                }
1816
            } else {
1817
                // If not, we do nothing. We already know that there is no parenthesis
1818
                // TODO Disallow this case in a future.
1819
                dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Syntax.", LOG_WARNING);
1820
            }
1821
        }
1822
1823
        if (!is_object($hookmanager)) {
1824
            $hookmanager = new HookManager($this->db);
1825
        }
1826
1827
        // We search third parties
1828
        $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";
1829
        if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
1830
            $sql .= ", s.nom as company, s.town AS company_town";
1831
        }
1832
        $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
1833
        if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
1834
            $sql .= " LEFT OUTER JOIN  " . $this->db->prefix() . "societe as s ON s.rowid=sp.fk_soc";
1835
        }
1836
        $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
1837
        if ($socid > 0 || $socid == -1) {
1838
            $sql .= " AND sp.fk_soc = " . ((int) $socid);
1839
        }
1840
        if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
1841
            $sql .= " AND sp.statut <> 0";
1842
        }
1843
        if ($filter) {
1844
            // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
1845
            // if not, by testSqlAndScriptInject() only.
1846
            $sql .= " AND (" . $filter . ")";
1847
        }
1848
        // Add where from hooks
1849
        $parameters = array();
1850
        $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1851
        $sql .= $hookmanager->resPrint;
1852
        $sql .= " ORDER BY sp.lastname ASC";
1853
1854
        dol_syslog(get_only_class($this) . "::selectcontacts", LOG_DEBUG);
1855
        $resql = $this->db->query($sql);
1856
        if ($resql) {
1857
            $num = $this->db->num_rows($resql);
1858
1859
            if ($htmlname != 'none' && !$options_only) {
1860
                $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
1861
            }
1862
1863
            if ($showempty && !is_numeric($showempty)) {
1864
                $textforempty = $showempty;
1865
                $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
1866
            } else {
1867
                if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($showempty == 1 || $sho...num > 1) && ! $multiple, Probably Intended Meaning: $showempty == 1 || ($sho...num > 1 && ! $multiple)
Loading history...
1868
                    $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
1869
                }
1870
                if ($showempty == 2) {
1871
                    $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
1872
                }
1873
            }
1874
1875
            $i = 0;
1876
            if ($num) {
1877
                    $contactstatic = new Contact($this->db);
1878
1879
                while ($i < $num) {
1880
                    $obj = $this->db->fetch_object($resql);
1881
1882
                    // Set email (or phones) and town extended infos
1883
                    $extendedInfos = '';
1884
                    if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
1885
                        $extendedInfos = array();
1886
                        $email = trim($obj->email);
1887
                        if (!empty($email)) {
1888
                            $extendedInfos[] = $email;
1889
                        } else {
1890
                            $phone = trim($obj->phone);
1891
                            $phone_perso = trim($obj->phone_perso);
1892
                            $phone_mobile = trim($obj->phone_mobile);
1893
                            if (!empty($phone)) {
1894
                                $extendedInfos[] = $phone;
1895
                            }
1896
                            if (!empty($phone_perso)) {
1897
                                $extendedInfos[] = $phone_perso;
1898
                            }
1899
                            if (!empty($phone_mobile)) {
1900
                                $extendedInfos[] = $phone_mobile;
1901
                            }
1902
                        }
1903
                        $contact_town = trim($obj->contact_town);
1904
                        $company_town = trim($obj->company_town);
1905
                        if (!empty($contact_town)) {
1906
                            $extendedInfos[] = $contact_town;
1907
                        } elseif (!empty($company_town)) {
1908
                            $extendedInfos[] = $company_town;
1909
                        }
1910
                        $extendedInfos = implode(' - ', $extendedInfos);
1911
                        if (!empty($extendedInfos)) {
1912
                            $extendedInfos = ' - ' . $extendedInfos;
1913
                        }
1914
                    }
1915
1916
                    $contactstatic->id = $obj->rowid;
1917
                    $contactstatic->lastname = $obj->lastname;
1918
                    $contactstatic->firstname = $obj->firstname;
1919
                    if ($obj->statut == 1) {
1920
                        $tmplabel = '';
1921
                        if ($htmlname != 'none') {
1922
                            $disabled = 0;
1923
                            if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1924
                                $disabled = 1;
1925
                            }
1926
                            if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1927
                                $disabled = 1;
1928
                            }
1929
                            if (!empty($selected) && in_array($obj->rowid, $selected)) {
1930
                                $out .= '<option value="' . $obj->rowid . '"';
1931
                                if ($disabled) {
1932
                                    $out .= ' disabled';
1933
                                }
1934
                                $out .= ' selected>';
1935
1936
                                $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
1937
                                if ($showfunction && $obj->poste) {
1938
                                    $tmplabel .= ' (' . $obj->poste . ')';
1939
                                }
1940
                                if (($showsoc > 0) && $obj->company) {
1941
                                    $tmplabel .= ' - (' . $obj->company . ')';
1942
                                }
1943
1944
                                $out .= $tmplabel;
1945
                                $out .= '</option>';
1946
                            } else {
1947
                                $out .= '<option value="' . $obj->rowid . '"';
1948
                                if ($disabled) {
1949
                                    $out .= ' disabled';
1950
                                }
1951
                                $out .= '>';
1952
1953
                                $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
1954
                                if ($showfunction && $obj->poste) {
1955
                                    $tmplabel .= ' (' . $obj->poste . ')';
1956
                                }
1957
                                if (($showsoc > 0) && $obj->company) {
1958
                                    $tmplabel .= ' - (' . $obj->company . ')';
1959
                                }
1960
1961
                                $out .= $tmplabel;
1962
                                $out .= '</option>';
1963
                            }
1964
                        } else {
1965
                            if (in_array($obj->rowid, $selected)) {
1966
                                $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
1967
                                if ($showfunction && $obj->poste) {
1968
                                    $tmplabel .= ' (' . $obj->poste . ')';
1969
                                }
1970
                                if (($showsoc > 0) && $obj->company) {
1971
                                    $tmplabel .= ' - (' . $obj->company . ')';
1972
                                }
1973
1974
                                $out .= $tmplabel;
1975
                            }
1976
                        }
1977
1978
                        if ($tmplabel != '') {
1979
                            array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
1980
                        }
1981
                    }
1982
                    $i++;
1983
                }
1984
            } else {
1985
                $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1986
                $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
1987
                $out .= $labeltoshow;
1988
                $out .= '</option>';
1989
            }
1990
1991
            $parameters = array(
1992
                'socid' => $socid,
1993
                'htmlname' => $htmlname,
1994
                'resql' => $resql,
1995
                'out' => &$out,
1996
                'showfunction' => $showfunction,
1997
                'showsoc' => $showsoc,
1998
            );
1999
2000
            $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2001
2002
            if ($htmlname != 'none' && !$options_only) {
2003
                $out .= '</select>';
2004
            }
2005
2006
            if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
2007
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2008
                $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
2009
            }
2010
2011
            $this->num = $num;
2012
2013
            if ($options_only === 2) {
2014
                // Return array of options
2015
                return $outarray;
2016
            } else {
2017
                return $out;
2018
            }
2019
        } else {
2020
            dol_print_error($this->db);
2021
            return -1;
2022
        }
2023
    }
2024
2025
2026
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2027
2028
    /**
2029
     *  Return HTML combo list of absolute discounts
2030
     *
2031
     * @param   string  $selected   Id Fixed reduction preselected
2032
     * @param   string  $htmlname   Name of the form field
2033
     * @param   string  $filter     Optional filter critreria
2034
     * @param   int     $socid      Id of thirdparty
2035
     * @param   int     $maxvalue   Max value for lines that can be selected
2036
     * @return  int                 Return number of qualifed lines in list
2037
     */
2038
    public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2039
    {
2040
		// phpcs:enable
2041
        global $langs, $conf;
2042
2043
        // On recherche les remises
2044
        $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2045
        $sql .= " re.description, re.fk_facture_source";
2046
        $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2047
        $sql .= " WHERE re.fk_soc = " . (int) $socid;
2048
        $sql .= " AND re.entity = " . $conf->entity;
2049
        if ($filter) {
2050
            $sql .= " AND " . $filter;
2051
        }
2052
        $sql .= " ORDER BY re.description ASC";
2053
2054
        dol_syslog(get_only_class($this) . "::select_remises", LOG_DEBUG);
2055
        $resql = $this->db->query($sql);
2056
        if ($resql) {
2057
            print '<select id="select_' . $htmlname . '" class="flat maxwidthonsmartphone" name="' . $htmlname . '">';
2058
            $num = $this->db->num_rows($resql);
2059
2060
            $qualifiedlines = $num;
2061
2062
            $i = 0;
2063
            if ($num) {
2064
                print '<option value="0">&nbsp;</option>';
2065
                while ($i < $num) {
2066
                    $obj = $this->db->fetch_object($resql);
2067
                    $desc = dol_trunc($obj->description, 40);
2068
                    if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
2069
                        $desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
2070
                    }
2071
                    if (preg_match('/\(DEPOSIT\)/', $desc)) {
2072
                        $desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
2073
                    }
2074
                    if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
2075
                        $desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
2076
                    }
2077
                    if (preg_match('/\(EXCESS PAID\)/', $desc)) {
2078
                        $desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
2079
                    }
2080
2081
                    $selectstring = '';
2082
                    if ($selected > 0 && $selected == $obj->rowid) {
2083
                        $selectstring = ' selected';
2084
                    }
2085
2086
                    $disabled = '';
2087
                    if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2088
                        $qualifiedlines--;
2089
                        $disabled = ' disabled';
2090
                    }
2091
2092
                    if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2093
                        $tmpfac = new Facture($this->db);
2094
                        if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2095
                            $desc = $desc . ' - ' . $tmpfac->ref;
2096
                        }
2097
                    }
2098
2099
                    print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2100
                    $i++;
2101
                }
2102
            }
2103
            print '</select>';
2104
            print ajax_combobox('select_' . $htmlname);
2105
2106
            return $qualifiedlines;
2107
        } else {
2108
            dol_print_error($this->db);
2109
            return -1;
2110
        }
2111
    }
2112
2113
2114
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2115
2116
    /**
2117
     * Return the HTML select list of users
2118
     *
2119
     * @param string $selected Id user preselected
2120
     * @param string $htmlname Field name in form
2121
     * @param int<0,1> $show_empty 0=liste sans valeur nulle, 1=ajoute valeur inconnue
2122
     * @param int[] $exclude Array list of users id to exclude
2123
     * @param int<0,1> $disabled If select list must be disabled
2124
     * @param int[]|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
2125
     * @param int[]|int $enableonly Array list of users id to be enabled. All other must be disabled
2126
     * @param string $force_entity '0' or Ids of environment to force
2127
     * @return    void
2128
     * @deprecated        Use select_dolusers instead
2129
     * @see select_dolusers()
2130
     */
2131
    public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2132
    {
2133
		// phpcs:enable
2134
        print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2135
    }
2136
2137
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2138
2139
    /**
2140
     * Return select list of users
2141
     *
2142
     * @param string|int|User   $selected       User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1 or '', keep unselected (if empty is allowed)
2143
     * @param string            $htmlname       Field name in form
2144
     * @param int<0,1>|string   $show_empty     0=list with no empty value, 1=add also an empty value into list
2145
     * @param int[]|null        $exclude        Array list of users id to exclude
2146
     * @param int               $disabled       If select list must be disabled
2147
     * @param int[]|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
2148
     * @param array|string      $enableonly     Array list of users id to be enabled. If defined, it means that others will be disabled
2149
     * @param string            $force_entity   '0' or list of Ids of environment to force, separated by a coma, or 'default' = do no extend to all entities allowed to superadmin.
2150
     * @param int               $maxlength      Maximum length of string into list (0=no limit)
2151
     * @param int<-1,1>         $showstatus     0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2152
     * @param string            $morefilter     Add more filters into sql request (Example: 'employee = 1'). This value must not come from user input.
2153
     * @param integer           $show_every     0=default list, 1=add also a value "Everybody" at beginning of list
2154
     * @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.
2155
     * @param string            $morecss        More css
2156
     * @param int<0,1>          $notdisabled    Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
2157
     * @param int<0,2>          $outputmode     0=HTML select string, 1=Array, 2=Detailed array
2158
     * @param bool              $multiple       add [] in the name of element and add 'multiple' attribute
2159
     * @param int<0,1>          $forcecombo     Force the component to be a simple combo box without ajax
2160
     * @return string|array<int,string|array{id:int,label:string,labelhtml:string,color:string,picto:string}>   HTML select string
2161
     * @see select_dolgroups()
2162
     */
2163
    public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
2164
    {
2165
		// phpcs:enable
2166
        global $conf, $user, $langs, $hookmanager;
2167
        global $action;
2168
2169
        // If no preselected user defined, we take current user
2170
        if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2171
            $selected = $user->id;
2172
        }
2173
2174
        if ($selected === '') {
2175
            $selected = array();
2176
        } elseif (!is_array($selected)) {
2177
            $selected = array($selected);
2178
        }
2179
2180
        $excludeUsers = null;
2181
        $includeUsers = null;
2182
2183
        // Exclude some users
2184
        if (is_array($exclude)) {
2185
            $excludeUsers = implode(",", $exclude);
2186
        }
2187
        // Include some uses
2188
        if (is_array($include)) {
2189
            $includeUsers = implode(",", $include);
2190
        } elseif ($include == 'hierarchy') {
2191
            // Build list includeUsers to have only hierarchy
2192
            $includeUsers = implode(",", $user->getAllChildIds(0));
2193
        } elseif ($include == 'hierarchyme') {
2194
            // Build list includeUsers to have only hierarchy and current user
2195
            $includeUsers = implode(",", $user->getAllChildIds(1));
2196
        }
2197
2198
        $num = 0;
2199
2200
        $out = '';
2201
        $outarray = array();
2202
        $outarray2 = array();
2203
2204
        // Do we want to show the label of entity into the combo list ?
2205
        $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity);
2206
        $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2207
2208
        // Forge request to select users
2209
        $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.gender, u.photo";
2210
        if ($showlabelofentity) {
2211
            $sql .= ", e.label";
2212
        }
2213
        $sql .= " FROM " . $this->db->prefix() . "user as u";
2214
        if ($showlabelofentity) {
2215
            $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2216
        }
2217
        // Condition here should be the same than into societe->getSalesRepresentatives().
2218
        if ($userissuperadminentityone && $force_entity != 'default') {
2219
            if (!empty($force_entity)) {
2220
                $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2221
            } else {
2222
                $sql .= " WHERE u.entity IS NOT NULL";
2223
            }
2224
        } else {
2225
            if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2226
                $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM " . $this->db->prefix() . "usergroup_user as ug WHERE ug.entity IN (" . getEntity('usergroup') . "))";
2227
            } else {
2228
                $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2229
            }
2230
        }
2231
2232
        if (!empty($user->socid)) {
2233
            $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2234
        }
2235
        if (is_array($exclude) && $excludeUsers) {
2236
            $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2237
        }
2238
        if ($includeUsers) {
2239
            $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2240
        }
2241
        if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2242
            $sql .= " AND u.statut <> 0";
2243
        }
2244
        if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX') || $notdisabled) {
2245
            $sql .= " AND u.employee <> 0";
2246
        }
2247
        if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX') || $notdisabled) {
2248
            $sql .= " AND u.fk_soc IS NULL";
2249
        }
2250
        if (!empty($morefilter)) {
2251
            $sql .= " " . $morefilter;
2252
        }
2253
2254
        //Add hook to filter on user (for example on usergroup define in custom modules)
2255
        $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2256
        if (!empty($reshook)) {
2257
            $sql .= $hookmanager->resPrint;
2258
        }
2259
2260
        if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {    // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2261
            $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2262
        } else {
2263
            $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2264
        }
2265
2266
        dol_syslog(get_only_class($this) . "::select_dolusers", LOG_DEBUG);
2267
2268
        $resql = $this->db->query($sql);
2269
        if ($resql) {
2270
            $num = $this->db->num_rows($resql);
2271
            $i = 0;
2272
            if ($num) {
2273
                // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2274
                $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2275
                if ($show_empty && !$multiple) {
2276
                    $textforempty = ' ';
2277
                    if (!empty($conf->use_javascript_ajax)) {
2278
                        $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2279
                    }
2280
                    if (!is_numeric($show_empty)) {
2281
                        $textforempty = $show_empty;
2282
                    }
2283
                    $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2284
                }
2285
                if ($show_every) {
2286
                    $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2287
                }
2288
2289
                $userstatic = new User($this->db);
2290
2291
                while ($i < $num) {
2292
                    $obj = $this->db->fetch_object($resql);
2293
2294
                    $userstatic->id = $obj->rowid;
2295
                    $userstatic->lastname = $obj->lastname;
2296
                    $userstatic->firstname = $obj->firstname;
2297
                    $userstatic->photo = $obj->photo;
2298
                    $userstatic->status = $obj->status;
2299
                    $userstatic->entity = $obj->entity;
2300
                    $userstatic->admin = $obj->admin;
2301
                    $userstatic->gender = $obj->gender;
2302
2303
                    $disableline = '';
2304
                    if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2305
                        $disableline = ($enableonlytext ? $enableonlytext : '1');
2306
                    }
2307
2308
                    $labeltoshow = '';
2309
                    $labeltoshowhtml = '';
2310
2311
                    // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2312
                    $fullNameMode = 0;
2313
                    if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2314
                        $fullNameMode = 1; //Firstname+lastname
2315
                    }
2316
                    $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2317
                    $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2318
                    if (empty($obj->firstname) && empty($obj->lastname)) {
2319
                        $labeltoshow .= $obj->login;
2320
                        $labeltoshowhtml .= $obj->login;
2321
                    }
2322
2323
                    // Complete name with a more info string like: ' (info1 - info2 - ...)'
2324
                    $moreinfo = '';
2325
                    $moreinfohtml = '';
2326
                    if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2327
                        $moreinfo .= ($moreinfo ? ' - ' : ' (');
2328
                        $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2329
                        $moreinfo .= $obj->login;
2330
                        $moreinfohtml .= $obj->login;
2331
                    }
2332
                    if ($showstatus >= 0) {
2333
                        if ($obj->status == 1 && $showstatus == 1) {
2334
                            $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2335
                            $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2336
                        }
2337
                        if ($obj->status == 0 && $showstatus == 1) {
2338
                            $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2339
                            $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2340
                        }
2341
                    }
2342
                    if ($showlabelofentity) {
2343
                        if (empty($obj->entity)) {
2344
                            $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2345
                            $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2346
                        } else {
2347
                            if ($obj->entity != $conf->entity) {
2348
                                $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2349
                                $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2350
                            }
2351
                        }
2352
                    }
2353
                    $moreinfo .= (!empty($moreinfo) ? ')' : '');
2354
                    $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2355
                    if (!empty($disableline) && $disableline != '1') {
2356
                        // Add text from $enableonlytext parameter
2357
                        $moreinfo .= ' - ' . $disableline;
2358
                        $moreinfohtml .= ' - ' . $disableline;
2359
                    }
2360
                    $labeltoshow .= $moreinfo;
2361
                    $labeltoshowhtml .= $moreinfohtml;
2362
2363
                    $out .= '<option value="' . $obj->rowid . '"';
2364
                    if (!empty($disableline)) {
2365
                        $out .= ' disabled';
2366
                    }
2367
                    if ((!empty($selected[0]) && is_object($selected[0])) ? $selected[0]->id == $obj->rowid : in_array($obj->rowid, $selected)) {
2368
                        $out .= ' selected';
2369
                    }
2370
                    $out .= ' data-html="';
2371
2372
                    $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2373
                    if ($showstatus >= 0 && $obj->status == 0) {
2374
                        $outhtml .= '<strike class="opacitymediumxxx">';
2375
                    }
2376
                    $outhtml .= $labeltoshowhtml;
2377
                    if ($showstatus >= 0 && $obj->status == 0) {
2378
                        $outhtml .= '</strike>';
2379
                    }
2380
                    $labeltoshowhtml = $outhtml;
2381
2382
                    $out .= dol_escape_htmltag($outhtml);
2383
                    $out .= '">';
2384
                    $out .= $labeltoshow;
2385
                    $out .= '</option>';
2386
2387
                    $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2388
                    $outarray2[$userstatic->id] = array(
2389
                        'id' => $userstatic->id,
2390
                        'label' => $labeltoshow,
2391
                        'labelhtml' => $labeltoshowhtml,
2392
                        'color' => '',
2393
                        'picto' => ''
2394
                    );
2395
2396
                    $i++;
2397
                }
2398
            } else {
2399
                $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2400
                $out .= '<option value="">' . $langs->trans("None") . '</option>';
2401
            }
2402
            $out .= '</select>';
2403
2404
            if ($num && !$forcecombo) {
2405
                // Enhance with select2
2406
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2407
                $out .= ajax_combobox($htmlname);
2408
            }
2409
        } else {
2410
            dol_print_error($this->db);
2411
        }
2412
2413
        $this->num = $num;
2414
2415
        if ($outputmode == 2) {
2416
            return $outarray2;
2417
        } elseif ($outputmode) {
2418
            return $outarray;
2419
        }
2420
2421
        return $out;
2422
    }
2423
2424
2425
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2426
    /**
2427
     * Return select list of users. Selected users are stored into session.
2428
     * List of users are provided into $_SESSION['assignedtouser'].
2429
     *
2430
     * @param string    $action             Value for $action
2431
     * @param string    $htmlname           Field name in form
2432
     * @param int<0,1>  $show_empty         0=list without the empty value, 1=add empty value
2433
     * @param int[]     $exclude            Array list of users id to exclude
2434
     * @param int<0,1>  $disabled           If select list must be disabled
2435
     * @param int[]|string  $include            Array list of users id to include or 'hierarchy' to have only supervised users
2436
     * @param int[]|int $enableonly         Array list of users id to be enabled. All other must be disabled
2437
     * @param string    $force_entity       '0' or Ids of environment to force
2438
     * @param int       $maxlength          Maximum length of string into list (0=no limit)
2439
     * @param int<0,1>  $showstatus         0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2440
     * @param string    $morefilter         Add more filters into sql request
2441
     * @param int       $showproperties     Show properties of each attendees
2442
     * @param int[]     $listofuserid       Array with properties of each user
2443
     * @param int[]     $listofcontactid    Array with properties of each contact
2444
     * @param int[]     $listofotherid      Array with properties of each other contact
2445
     * @return    string                    HTML select string
2446
     * @see select_dolgroups()
2447
     */
2448
    public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
2449
    {
2450
		// phpcs:enable
2451
        global $langs;
2452
2453
        $userstatic = new User($this->db);
2454
        $out = '';
2455
2456
        if (!empty($_SESSION['assignedtouser'])) {
2457
            $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2458
            if (!is_array($assignedtouser)) {
2459
                $assignedtouser = array();
2460
            }
2461
        } else {
2462
            $assignedtouser = array();
2463
        }
2464
        $nbassignetouser = count($assignedtouser);
2465
2466
        //if ($nbassignetouser && $action != 'view') $out .= '<br>';
2467
        if ($nbassignetouser) {
2468
            $out .= '<ul class="attendees">';
2469
        }
2470
        $i = 0;
2471
        $ownerid = 0;
2472
        foreach ($assignedtouser as $key => $value) {
2473
            if ($value['id'] == $ownerid) {
2474
                continue;
2475
            }
2476
2477
            $out .= '<li>';
2478
            $userstatic->fetch($value['id']);
2479
            $out .= $userstatic->getNomUrl(-1);
2480
            if ($i == 0) {
2481
                $ownerid = $value['id'];
2482
                $out .= ' (' . $langs->trans("Owner") . ')';
2483
            }
2484
            if ($nbassignetouser > 1 && $action != 'view') {
2485
                $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
2486
            }
2487
            // Show my availability
2488
            if ($showproperties) {
2489
                if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2490
                    $out .= '<div class="myavailability inline-block">';
2491
                    $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>';
2492
                    $out .= '</div>';
2493
                }
2494
            }
2495
            //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2496
            //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2497
2498
            $out .= '</li>';
2499
            $i++;
2500
        }
2501
        if ($nbassignetouser) {
2502
            $out .= '</ul>';
2503
        }
2504
2505
        // Method with no ajax
2506
        if ($action != 'view') {
2507
            $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2508
            $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
2509
            $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2510
            $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2511
            $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
2512
            $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
2513
            $out .= '});';
2514
            $out .= '})</script>';
2515
            $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2516
            $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
2517
            $out .= '<br>';
2518
        }
2519
2520
        return $out;
2521
    }
2522
2523
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2524
    /**
2525
     * Return select list of resources. Selected resources are stored into session.
2526
     * List of resources are provided into $_SESSION['assignedtoresource'].
2527
     *
2528
     * @param string    $action             Value for $action
2529
     * @param string    $htmlname           Field name in form
2530
     * @param int       $show_empty         0=list without the empty value, 1=add empty value
2531
     * @param int[]     $exclude            Array list of users id to exclude
2532
     * @param int<0,1>  $disabled           If select list must be disabled
2533
     * @param int[]|string  $include        Array list of users id to include or 'hierarchy' to have only supervised users
2534
     * @param int[]     $enableonly         Array list of users id to be enabled. All other must be disabled
2535
     * @param string    $force_entity       '0' or Ids of environment to force
2536
     * @param int       $maxlength          Maximum length of string into list (0=no limit)
2537
     * @param int<-1,1> $showstatus         0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
2538
     * @param string    $morefilter         Add more filters into sql request
2539
     * @param int       $showproperties     Show properties of each attendees
2540
     * @param array<int,array{transparency:bool|int<0,1>}> $listofresourceid    Array with properties of each resource
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int,array{transparency:bool|int<0,1>}> at position 10 could not be parsed: Expected '}' at position 10, but found 'int'.
Loading history...
2541
     * @return    string                    HTML select string
2542
     */
2543
    public function select_dolresources_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofresourceid = array())
2544
    {
2545
		// phpcs:enable
2546
        global $langs;
2547
2548
        $formresources = new FormResource($this->db);
2549
        $resourcestatic = new Dolresource($this->db);
2550
2551
        $out = '';
2552
        if (!empty($_SESSION['assignedtoresource'])) {
2553
            $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
2554
            if (!is_array($assignedtoresource)) {
2555
                $assignedtoresource = array();
2556
            }
2557
        } else {
2558
            $assignedtoresource = array();
2559
        }
2560
        $nbassignetoresource = count($assignedtoresource);
2561
2562
        //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
2563
        if ($nbassignetoresource) {
2564
            $out .= '<ul class="attendees">';
2565
        }
2566
        $i = 0;
2567
2568
        foreach ($assignedtoresource as $key => $value) {
2569
            $out .= '<li>';
2570
            $resourcestatic->fetch($value['id']);
2571
            $out .= $resourcestatic->getNomUrl(-1);
2572
            if ($nbassignetoresource > 1 && $action != 'view') {
2573
                $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $resourcestatic->id . '" class="removedassigned reposition" id="removedassignedresource_' . $resourcestatic->id . '" name="removedassignedresource_' . $resourcestatic->id . '">';
2574
            }
2575
            // Show my availability
2576
            if ($showproperties) {
2577
                if (is_array($listofresourceid) && count($listofresourceid)) {
2578
                    $out .= '<div class="myavailability inline-block">';
2579
                    $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;<span class="opacitymedium">' . $langs->trans("Availability") . ':</span>  </span><input id="transparencyresource" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofresourceid[$value['id']]['transparency'] ? ' checked' : '') . '><label for="transparency">' . $langs->trans("Busy") . '</label>';
2580
                    $out .= '</div>';
2581
                }
2582
            }
2583
            //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2584
            //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2585
2586
            $out .= '</li>';
2587
            $i++;
2588
        }
2589
        if ($nbassignetoresource) {
2590
            $out .= '</ul>';
2591
        }
2592
2593
        // Method with no ajax
2594
        if ($action != 'view') {
2595
            $out .= '<input type="hidden" class="removedassignedhidden" name="removedassignedresource" value="">';
2596
            $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
2597
            $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
2598
            $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
2599
            $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
2600
            $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
2601
            $out .= '});';
2602
            $out .= '})</script>';
2603
2604
            $events = array();
2605
            $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
2606
            $out .= $formresources->select_resource_list(0, $htmlname, [], 1, 1, 0, $events, array(), 2, 0);
2607
            //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2608
            $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
2609
            $out .= '<br>';
2610
        }
2611
2612
        return $out;
2613
    }
2614
2615
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2616
2617
    /**
2618
     *  Return list of products for customer.
2619
     *  Use Ajax if Ajax activated or go to select_produits_list
2620
     *
2621
     *  @param      int         $selected               Preselected products
2622
     *  @param      string      $htmlname               Name of HTML select field (must be unique in page).
2623
     *  @param      int|string  $filtertype             Filter on product type (''=nofilter, 0=product, 1=service)
2624
     *  @param      int         $limit                  Limit on number of returned lines
2625
     *  @param      int         $price_level            Level of price to show
2626
     *  @param      int         $status                 Sell status: -1=No filter on sell status, 0=Products not on sell, 1=Products on sell
2627
     *  @param      int         $finished               2=all, 1=finished, 0=raw material
2628
     *  @param      string      $selected_input_value   Value of preselected input text (for use with ajax)
2629
     *  @param      int         $hidelabel              Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
2630
     *  @param      array<string,string|string[]>   $ajaxoptions            Options for ajax_autocompleter
2631
     *  @param      int         $socid                  Thirdparty Id (to get also price dedicated to this customer)
2632
     *  @param      string|int<0,1> $showempty          '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2633
     *  @param      int         $forcecombo             Force to use combo box
2634
     *  @param      string      $morecss                Add more css on select
2635
     *  @param      int<0,1>    $hidepriceinlabel       1=Hide prices in label
2636
     *  @param      string      $warehouseStatus        Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used
2637
     *                                                  'warehouseopen' = count products from open warehouses,
2638
     *                                                  'warehouseclosed' = count products from closed warehouses,
2639
     *                                                  'warehouseinternal' = count products from warehouses for internal correct/transfer only
2640
     *  @param      ?mixed[]    $selected_combinations  Selected combinations. Format: array([attrid] => attrval, [...])
2641
     *  @param      int<0,1>    $nooutput               No print if 1, return the output into a string
2642
     *  @param      int<-1,1>   $status_purchase        Purchase status: -1=No filter on purchase status, 0=Products not on purchase, 1=Products on purchase
2643
     *  @return     void|string
2644
     */
2645
    public function select_produits($selected = 0, $htmlname = 'productid', $filtertype = '', $limit = 0, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = null, $nooutput = 0, $status_purchase = -1)
2646
    {
2647
		// phpcs:enable
2648
        global $langs, $conf;
2649
2650
        $out = '';
2651
2652
        // check parameters
2653
        $price_level = (!empty($price_level) ? $price_level : 0);
2654
        if (is_null($ajaxoptions)) {
2655
            $ajaxoptions = array();
2656
        }
2657
2658
        if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
2659
            if (isModEnabled("product") && !isModEnabled('service')) {
2660
                $filtertype = '0';
2661
            } elseif (!isModEnabled('product') && isModEnabled("service")) {
2662
                $filtertype = '1';
2663
            }
2664
        }
2665
2666
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
2667
            $placeholder = '';
2668
2669
            if ($selected && empty($selected_input_value)) {
2670
                $producttmpselect = new Product($this->db);
2671
                $producttmpselect->fetch($selected);
2672
                $selected_input_value = $producttmpselect->ref;
2673
                unset($producttmpselect);
2674
            }
2675
            // handle case where product or service module is disabled + no filter specified
2676
            if ($filtertype == '') {
2677
                if (!isModEnabled('product')) { // when product module is disabled, show services only
2678
                    $filtertype = 1;
2679
                } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2680
                    $filtertype = 0;
2681
                }
2682
            }
2683
            // mode=1 means customers products
2684
            $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=1&status=' . $status . '&status_purchase=' . $status_purchase . '&finished=' . $finished . '&hidepriceinlabel=' . $hidepriceinlabel . '&warehousestatus=' . $warehouseStatus;
2685
            $out .= ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2686
2687
            if (isModEnabled('variants') && is_array($selected_combinations)) {
2688
                // Code to automatically insert with javascript the select of attributes under the select of product
2689
                // when a parent of variant has been selected.
2690
                $out .= '
2691
				<!-- script to auto show attributes select tags if a variant was selected -->
2692
				<script nonce="' . getNonce() . '">
2693
					// auto show attributes fields
2694
					selected = ' . json_encode($selected_combinations) . ';
2695
					combvalues = {};
2696
2697
					jQuery(document).ready(function () {
2698
2699
						jQuery("input[name=\'prod_entry_mode\']").change(function () {
2700
							if (jQuery(this).val() == \'free\') {
2701
								jQuery(\'div#attributes_box\').empty();
2702
							}
2703
						});
2704
2705
						jQuery("input#' . $htmlname . '").change(function () {
2706
2707
							if (!jQuery(this).val()) {
2708
								jQuery(\'div#attributes_box\').empty();
2709
								return;
2710
							}
2711
2712
							console.log("A change has started. We get variants fields to inject html select");
2713
2714
							jQuery.getJSON("' . constant('BASE_URL') . '/variants/ajax/getCombinations.php", {
2715
								id: jQuery(this).val()
2716
							}, function (data) {
2717
								jQuery(\'div#attributes_box\').empty();
2718
2719
								jQuery.each(data, function (key, val) {
2720
2721
									combvalues[val.id] = val.values;
2722
2723
									var span = jQuery(document.createElement(\'div\')).css({
2724
										\'display\': \'table-row\'
2725
									});
2726
2727
									span.append(
2728
										jQuery(document.createElement(\'div\')).text(val.label).css({
2729
											\'font-weight\': \'bold\',
2730
											\'display\': \'table-cell\'
2731
										})
2732
									);
2733
2734
									var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2735
										\'margin-left\': \'15px\',
2736
										\'white-space\': \'pre\'
2737
									}).append(
2738
										jQuery(document.createElement(\'option\')).val(\'\')
2739
									);
2740
2741
									jQuery.each(combvalues[val.id], function (key, val) {
2742
										var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2743
2744
										if (selected[val.fk_product_attribute] == val.id) {
2745
											tag.attr(\'selected\', \'selected\');
2746
										}
2747
2748
										html.append(tag);
2749
									});
2750
2751
									span.append(html);
2752
									jQuery(\'div#attributes_box\').append(span);
2753
								});
2754
							})
2755
						});
2756
2757
						' . ($selected ? 'jQuery("input#' . $htmlname . '").change();' : '') . '
2758
					});
2759
				</script>
2760
                ';
2761
            }
2762
2763
            if (empty($hidelabel)) {
2764
                $out .= $langs->trans("RefOrLabel") . ' : ';
2765
            } elseif ($hidelabel > 1) {
2766
                $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
2767
                if ($hidelabel == 2) {
2768
                    $out .= img_picto($langs->trans("Search"), 'search');
2769
                }
2770
            }
2771
            $out .= '<input type="text" class="minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
2772
            if ($hidelabel == 3) {
2773
                $out .= img_picto($langs->trans("Search"), 'search');
2774
            }
2775
        } else {
2776
            $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2777
        }
2778
2779
        if (empty($nooutput)) {
2780
            print $out;
2781
        } else {
2782
            return $out;
2783
        }
2784
    }
2785
2786
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2787
2788
    /**
2789
     *  Return list of BOM for customer in Ajax if Ajax activated or go to select_produits_list
2790
     *
2791
     * @param string    $selected Preselected BOM id
2792
     * @param string    $htmlname Name of HTML select field (must be unique in page).
2793
     * @param int       $limit Limit on number of returned lines
2794
     * @param int       $status Sell status -1=Return all bom, 0=Draft BOM, 1=Validated BOM
2795
     * @param int       $type type of the BOM (-1=Return all BOM, 0=Return disassemble BOM, 1=Return manufacturing BOM)
2796
     * @param string|int<0,1>   $showempty  '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2797
     * @param string    $morecss Add more css on select
2798
     * @param string    $nooutput No print, return the output into a string
2799
     * @param int       $forcecombo Force to use combo box
2800
     * @param string[]  $TProducts Add filter on a defined product
2801
     * @return void|string
2802
     */
2803
    public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
2804
    {
2805
		// phpcs:enable
2806
        global $db;
2807
2808
2809
        $error = 0;
2810
        $out = '';
2811
2812
        if (!$forcecombo) {
2813
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2814
            $events = array();
2815
            $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2816
        }
2817
2818
        $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
2819
2820
        $sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2821
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'bom_bom as b';
2822
        $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
2823
        if (!empty($status)) {
2824
            $sql .= ' AND status = ' . (int) $status;
2825
        }
2826
        if (!empty($type)) {
2827
            $sql .= ' AND bomtype = ' . (int) $type;
2828
        }
2829
        if (!empty($TProducts)) {
2830
            $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
2831
        }
2832
        if (!empty($limit)) {
2833
            $sql .= ' LIMIT ' . (int) $limit;
2834
        }
2835
        $resql = $db->query($sql);
2836
        if ($resql) {
2837
            if ($showempty) {
2838
                $out .= '<option value="-1"';
2839
                if (empty($selected)) {
2840
                    $out .= ' selected';
2841
                }
2842
                $out .= '>&nbsp;</option>';
2843
            }
2844
            while ($obj = $db->fetch_object($resql)) {
2845
                $product = new Product($db);
2846
                $res = $product->fetch($obj->fk_product);
2847
                $out .= '<option value="' . $obj->rowid . '"';
2848
                if ($obj->rowid == $selected) {
2849
                    $out .= 'selected';
2850
                }
2851
                $out .= '>' . $obj->ref . ' - ' . $product->label . ' - ' . $obj->label . '</option>';
2852
            }
2853
        } else {
2854
            $error++;
2855
            dol_print_error($db);
2856
        }
2857
        $out .= '</select>';
2858
        if (empty($nooutput)) {
2859
            print $out;
2860
        } else {
2861
            return $out;
2862
        }
2863
    }
2864
2865
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2866
2867
    /**
2868
     * Return list of products for a customer.
2869
     * Called by select_produits.
2870
     *
2871
     * @param   int         $selected               Preselected product
2872
     * @param   string      $htmlname               Name of select html
2873
     * @param   string      $filtertype             Filter on product type (''=nofilter, 0=product, 1=service)
2874
     * @param   int         $limit                  Limit on number of returned lines
2875
     * @param   int         $price_level            Level of price to show
2876
     * @param   string      $filterkey              Filter on product
2877
     * @param   int         $status                 -1=Return all products, 0=Products not on sell, 1=Products on sell
2878
     * @param   int         $finished               Filter on finished field: 2=No filter
2879
     * @param   int         $outputmode             0=HTML select string, 1=Array
2880
     * @param   int         $socid                  Thirdparty Id (to get also price dedicated to this customer)
2881
     * @param   string|int<0,1> $showempty          '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
2882
     * @param   int         $forcecombo             Force to use combo box
2883
     * @param   string      $morecss                Add more css on select
2884
     * @param   int         $hidepriceinlabel       1=Hide prices in label
2885
     * @param   string      $warehouseStatus        Warehouse status filter to group/count stock. Following comma separated filter options can be used.
2886
     *                                              'warehouseopen' = count products from open warehouses,
2887
     *                                              'warehouseclosed' = count products from closed warehouses,
2888
     *                                              'warehouseinternal' = count products from warehouses for internal correct/transfer only
2889
     * @param   int         $status_purchase        Purchase status -1=Return all products, 0=Products not on purchase, 1=Products on purchase
2890
     * @return  array|string                        Array of keys for json
2891
     */
2892
    public function select_produits_list($selected = 0, $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = 'maxwidth500', $hidepriceinlabel = 0, $warehouseStatus = '', $status_purchase = -1)
2893
    {
2894
		// phpcs:enable
2895
        global $langs;
2896
        global $hookmanager;
2897
2898
        $out = '';
2899
        $outarray = array();
2900
2901
        // Units
2902
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2903
            $langs->load('other');
2904
        }
2905
2906
        $warehouseStatusArray = array();
2907
        if (!empty($warehouseStatus)) {
2908
            if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2909
                $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2910
            }
2911
            if (preg_match('/warehouseopen/', $warehouseStatus)) {
2912
                $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2913
            }
2914
            if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2915
                $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2916
            }
2917
        }
2918
2919
        $selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.default_vat_code, p.duration, p.fk_price_expression";
2920
        if (count($warehouseStatusArray)) {
2921
            $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
2922
        } else {
2923
            $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", 0, "p.stock") . " AS stock";
2924
        }
2925
2926
        $sql = "SELECT ";
2927
2928
        // Add select from hooks
2929
        $parameters = array();
2930
        $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
2931
        if (empty($reshook)) {
2932
            $sql .= $selectFields . $selectFieldsGrouped . $hookmanager->resPrint;
2933
        } else {
2934
            $sql .= $hookmanager->resPrint;
2935
        }
2936
2937
        if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
2938
            //Product category
2939
            $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
2940
						FROM " . $this->db->prefix() . "categorie_product
2941
						WHERE " . $this->db->prefix() . "categorie_product.fk_product=p.rowid
2942
						LIMIT 1
2943
				) AS categorie_product_id ";
2944
        }
2945
2946
        //Price by customer
2947
        if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') && !empty($socid)) {
2948
            $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2949
            $sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.default_vat_code as custdefault_vat_code, pcp.ref_customer as custref';
2950
            $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2951
        }
2952
        // Units
2953
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2954
            $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";
2955
            $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';
2956
        }
2957
2958
        // Multilang : we add translation
2959
        if (getDolGlobalInt('MAIN_MULTILANGS')) {
2960
            $sql .= ", pl.label as label_translated";
2961
            $sql .= ", pl.description as description_translated";
2962
            $selectFields .= ", label_translated";
2963
            $selectFields .= ", description_translated";
2964
        }
2965
        // Price by quantity
2966
        if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2967
            $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
2968
            if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2969
                $sql .= " AND price_level = " . ((int) $price_level);
2970
            }
2971
            $sql .= " ORDER BY date_price";
2972
            $sql .= " DESC LIMIT 1) as price_rowid";
2973
            $sql .= ", (SELECT pp.price_by_qty FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable
2974
            if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2975
                $sql .= " AND price_level = " . ((int) $price_level);
2976
            }
2977
            $sql .= " ORDER BY date_price";
2978
            $sql .= " DESC LIMIT 1) as price_by_qty";
2979
            $selectFields .= ", price_rowid, price_by_qty";
2980
        }
2981
2982
        $sql .= " FROM " . $this->db->prefix() . "product as p";
2983
2984
        if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
2985
            $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
2986
        }
2987
2988
        // Add from (left join) from hooks
2989
        $parameters = array();
2990
        $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
2991
        $sql .= $hookmanager->resPrint;
2992
2993
        if (count($warehouseStatusArray)) {
2994
            $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.fk_product = p.rowid";
2995
            $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
2996
            $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.
2997
        }
2998
2999
        // include search in supplier ref
3000
        if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3001
            $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3002
        }
3003
3004
        //Price by customer
3005
        if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') && !empty($socid)) {
3006
            $sql .= " LEFT JOIN  " . $this->db->prefix() . "product_customer_price as pcp ON pcp.fk_soc=" . ((int) $socid) . " AND pcp.fk_product=p.rowid";
3007
        }
3008
        // Units
3009
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3010
            $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
3011
        }
3012
        // Multilang : we add translation
3013
        if (getDolGlobalInt('MAIN_MULTILANGS')) {
3014
            $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid ";
3015
            if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3016
                $soc = new Societe($this->db);
3017
                $result = $soc->fetch($socid);
3018
                if ($result > 0 && !empty($soc->default_lang)) {
3019
                    $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3020
                } else {
3021
                    $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3022
                }
3023
            } else {
3024
                $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3025
            }
3026
        }
3027
3028
        if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3029
            $sql .= " LEFT JOIN " . $this->db->prefix() . "product_attribute_combination pac ON pac.fk_product_child = p.rowid";
3030
        }
3031
3032
        $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3033
3034
        if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3035
            $sql .= " AND pac.rowid IS NULL";
3036
        }
3037
3038
        if ($finished == 0) {
3039
            $sql .= " AND p.finished = " . ((int) $finished);
3040
        } elseif ($finished == 1) {
3041
            $sql .= " AND p.finished = " . ((int) $finished);
3042
        }
3043
        if ($status >= 0) {
3044
            $sql .= " AND p.tosell = " . ((int) $status);
3045
        }
3046
        if ($status_purchase >= 0) {
3047
            $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3048
        }
3049
        // Filter by product type
3050
        if (strval($filtertype) != '') {
3051
            $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3052
        } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3053
            $sql .= " AND p.fk_product_type = 1";
3054
        } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3055
            $sql .= " AND p.fk_product_type = 0";
3056
        }
3057
        // Add where from hooks
3058
        $parameters = array();
3059
        $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3060
        $sql .= $hookmanager->resPrint;
3061
        // Add criteria on ref/label
3062
        if ($filterkey != '') {
3063
            $sql .= ' AND (';
3064
            $prefix = !getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3065
            // For natural search
3066
            $search_crit = explode(' ', $filterkey);
3067
            $i = 0;
3068
            if (count($search_crit) > 1) {
3069
                $sql .= "(";
3070
            }
3071
            foreach ($search_crit as $crit) {
3072
                if ($i > 0) {
3073
                    $sql .= " AND ";
3074
                }
3075
                $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3076
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
3077
                    $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3078
                }
3079
                if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') && !empty($socid)) {
3080
                    $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3081
                }
3082
                if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3083
                    $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3084
                    if (getDolGlobalInt('MAIN_MULTILANGS')) {
3085
                        $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3086
                    }
3087
                }
3088
                if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3089
                    $sql .= " OR pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3090
                }
3091
                $sql .= ")";
3092
                $i++;
3093
            }
3094
            if (count($search_crit) > 1) {
3095
                $sql .= ")";
3096
            }
3097
            if (isModEnabled('barcode')) {
3098
                $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3099
            }
3100
            $sql .= ')';
3101
        }
3102
        if (count($warehouseStatusArray)) {
3103
            $sql .= " GROUP BY " . $selectFields;
3104
        }
3105
3106
        //Sort by category
3107
        if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3108
            $sql .= " ORDER BY categorie_product_id ";
3109
            //ASC OR DESC order
3110
            (getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1) ? $sql .= "ASC" : $sql .= "DESC";
3111
        } else {
3112
            $sql .= $this->db->order("p.ref");
3113
        }
3114
3115
        $sql .= $this->db->plimit($limit, 0);
3116
3117
        // Build output string
3118
        dol_syslog(get_only_class($this) . "::select_produits_list search products", LOG_DEBUG);
3119
        $result = $this->db->query($sql);
3120
        if ($result) {
3121
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/product.lib.php';
3122
3123
            $num = $this->db->num_rows($result);
3124
3125
            $events = array();
3126
3127
            if (!$forcecombo) {
3128
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3129
                $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3130
            }
3131
3132
            $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3133
3134
            $textifempty = '';
3135
            // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3136
            //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3137
            if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3138
                if ($showempty && !is_numeric($showempty)) {
3139
                    $textifempty = $langs->trans($showempty);
3140
                } else {
3141
                    $textifempty .= $langs->trans("All");
3142
                }
3143
            } else {
3144
                if ($showempty && !is_numeric($showempty)) {
3145
                    $textifempty = $langs->trans($showempty);
3146
                }
3147
            }
3148
            if ($showempty) {
3149
                $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3150
            }
3151
3152
            $i = 0;
3153
            while ($num && $i < $num) {
3154
                $opt = '';
3155
                $optJson = array();
3156
                $objp = $this->db->fetch_object($result);
3157
3158
                if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($objp->price_by_qty) && $objp->price_by_qty == 1) { // Price by quantity will return many prices for the same product
3159
                    $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3160
                    $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3161
                    $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3162
                    $sql .= " ORDER BY quantity ASC";
3163
3164
                    dol_syslog(get_only_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3165
                    $result2 = $this->db->query($sql);
3166
                    if ($result2) {
3167
                        $nb_prices = $this->db->num_rows($result2);
3168
                        $j = 0;
3169
                        while ($nb_prices && $j < $nb_prices) {
3170
                            $objp2 = $this->db->fetch_object($result2);
3171
3172
                            $objp->price_by_qty_rowid = $objp2->rowid;
3173
                            $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3174
                            $objp->price_by_qty_quantity = $objp2->quantity;
3175
                            $objp->price_by_qty_unitprice = $objp2->unitprice;
3176
                            $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3177
                            // For backward compatibility
3178
                            $objp->quantity = $objp2->quantity;
3179
                            $objp->price = $objp2->price;
3180
                            $objp->unitprice = $objp2->unitprice;
3181
                            $objp->remise_percent = $objp2->remise_percent;
3182
3183
                            //$objp->tva_tx is not overwritten by $objp2 value
3184
                            //$objp->default_vat_code is not overwritten by $objp2 value
3185
3186
                            $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3187
3188
                            $j++;
3189
3190
                            // Add new entry
3191
                            // "key" value of json key array is used by jQuery automatically as selected value
3192
                            // "label" value of json key array is used by jQuery automatically as text for combo box
3193
                            $out .= $opt;
3194
                            array_push($outarray, $optJson);
3195
                        }
3196
                    }
3197
                } else {
3198
                    if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3199
                        $price_product = new Product($this->db);
3200
                        $price_product->fetch($objp->rowid, '', '', 1);
3201
3202
                        $priceparser = new PriceParser($this->db);
3203
                        $price_result = $priceparser->parseProduct($price_product);
3204
                        if ($price_result >= 0) {
3205
                            $objp->price = $price_result;
3206
                            $objp->unitprice = $price_result;
3207
                            //Calculate the VAT
3208
                            $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3209
                            $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3210
                        }
3211
                    }
3212
3213
                    $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
3214
                    // Add new entry
3215
                    // "key" value of json key array is used by jQuery automatically as selected value
3216
                    // "label" value of json key array is used by jQuery automatically as text for combo box
3217
                    $out .= $opt;
3218
                    array_push($outarray, $optJson);
3219
                }
3220
3221
                $i++;
3222
            }
3223
3224
            $out .= '</select>';
3225
3226
            $this->db->free($result);
3227
3228
            if (empty($outputmode)) {
3229
                return $out;
3230
            }
3231
3232
            return $outarray;
3233
        } else {
3234
            dol_print_error($this->db);
3235
        }
3236
3237
        return '';
3238
    }
3239
3240
    /**
3241
     * Function to forge the string with OPTIONs of SELECT.
3242
     * This define value for &$opt and &$optJson.
3243
     * This function is called by select_produits_list().
3244
     *
3245
     * @param stdClass  $objp           Resultset of fetch
3246
     * @param string    $opt            Option (var used for returned value in string option format)
3247
     * @param array{key:string,value:string,label:string,label2:string,desc:string,type:string,price_ht:string,price_ttc:string,price_ht_locale:string,price_ttc_locale:string,pricebasetype:string,tva_tx:string,default_vat_code:string,qty:string,discount:string,duration_value:string,duration_unit:string,pbq:string,labeltrans:string,desctrans:string,ref_customer:string}  $optJson        Option (var used for returned value in json format)
3248
     * @param int       $price_level    Price level
3249
     * @param int       $selected       Preselected value
3250
     * @param int<0,1>  $hidepriceinlabel Hide price in label
3251
     * @param string    $filterkey      Filter key to highlight
3252
     * @param int<0,1>  $novirtualstock Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on.
3253
     * @return    void
3254
     */
3255
    protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
3256
    {
3257
        global $langs, $conf, $user;
3258
        global $hookmanager;
3259
3260
        $outkey = '';
3261
        $outval = '';
3262
        $outref = '';
3263
        $outlabel = '';
3264
        $outlabel_translated = '';
3265
        $outdesc = '';
3266
        $outdesc_translated = '';
3267
        $outbarcode = '';
3268
        $outorigin = '';
3269
        $outtype = '';
3270
        $outprice_ht = '';
3271
        $outprice_ttc = '';
3272
        $outpricebasetype = '';
3273
        $outtva_tx = '';
3274
        $outdefault_vat_code = '';
3275
        $outqty = 1;
3276
        $outdiscount = 0;
3277
3278
        $maxlengtharticle = (!getDolGlobalString('PRODUCT_MAX_LENGTH_COMBO') ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3279
3280
        $label = $objp->label;
3281
        if (!empty($objp->label_translated)) {
3282
            $label = $objp->label_translated;
3283
        }
3284
        if (!empty($filterkey) && $filterkey != '') {
3285
            $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
3286
        }
3287
3288
        $outkey = $objp->rowid;
3289
        $outref = $objp->ref;
3290
        $outrefcust = empty($objp->custref) ? '' : $objp->custref;
3291
        $outlabel = $objp->label;
3292
        $outdesc = $objp->description;
3293
        if (getDolGlobalInt('MAIN_MULTILANGS')) {
3294
            $outlabel_translated = $objp->label_translated;
3295
            $outdesc_translated = $objp->description_translated;
3296
        }
3297
        $outbarcode = $objp->barcode;
3298
        $outorigin = $objp->fk_country;
3299
        $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
3300
3301
        $outtype = $objp->fk_product_type;
3302
        $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3303
        $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
3304
3305
        if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
3306
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/company.lib.php';
3307
        }
3308
3309
        // Units
3310
        $outvalUnits = '';
3311
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3312
            if (!empty($objp->unit_short)) {
3313
                $outvalUnits .= ' - ' . $objp->unit_short;
3314
            }
3315
        }
3316
        if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
3317
            if (!empty($objp->weight) && $objp->weight_units !== null) {
3318
                $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3319
                $outvalUnits .= ' - ' . $unitToShow;
3320
            }
3321
            if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3322
                $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
3323
                $outvalUnits .= ' - ' . $unitToShow;
3324
            }
3325
            if (!empty($objp->surface) && $objp->surface_units !== null) {
3326
                $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3327
                $outvalUnits .= ' - ' . $unitToShow;
3328
            }
3329
            if (!empty($objp->volume) && $objp->volume_units !== null) {
3330
                $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3331
                $outvalUnits .= ' - ' . $unitToShow;
3332
            }
3333
        }
3334
        if ($outdurationvalue && $outdurationunit) {
3335
            $da = array(
3336
                'h' => $langs->trans('Hour'),
3337
                'd' => $langs->trans('Day'),
3338
                'w' => $langs->trans('Week'),
3339
                'm' => $langs->trans('Month'),
3340
                'y' => $langs->trans('Year')
3341
            );
3342
            if (isset($da[$outdurationunit])) {
3343
                $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
3344
            }
3345
        }
3346
3347
        $opt = '<option value="' . $objp->rowid . '"';
3348
        $opt .= ($objp->rowid == $selected) ? ' selected' : '';
3349
        if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
3350
            $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 . '"';
3351
        }
3352
        if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
3353
            if ($user->hasRight('stock', 'lire')) {
3354
                if ($objp->stock > 0) {
3355
                    $opt .= ' class="product_line_stock_ok"';
3356
                } elseif ($objp->stock <= 0) {
3357
                    $opt .= ' class="product_line_stock_too_low"';
3358
                }
3359
            }
3360
        }
3361
        if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
3362
            $opt .= ' data-labeltrans="' . $outlabel_translated . '"';
3363
            $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
3364
        }
3365
        $opt .= '>';
3366
        $opt .= $objp->ref;
3367
        if (!empty($objp->custref)) {
3368
            $opt .= ' (' . $objp->custref . ')';
3369
        }
3370
        if ($outbarcode) {
3371
            $opt .= ' (' . $outbarcode . ')';
3372
        }
3373
        $opt .= ' - ' . dol_trunc($label, $maxlengtharticle);
3374
        if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
3375
            $opt .= ' (' . getCountry($outorigin, 1) . ')';
3376
        }
3377
3378
        $objRef = $objp->ref;
3379
        if (!empty($objp->custref)) {
3380
            $objRef .= ' (' . $objp->custref . ')';
3381
        }
3382
        if (!empty($filterkey) && $filterkey != '') {
3383
            $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
3384
        }
3385
        $outval .= $objRef;
3386
        if ($outbarcode) {
3387
            $outval .= ' (' . $outbarcode . ')';
3388
        }
3389
        $outval .= ' - ' . dol_trunc($label, $maxlengtharticle);
3390
        if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
3391
            $outval .= ' (' . getCountry($outorigin, 1) . ')';
3392
        }
3393
3394
        // Units
3395
        $opt .= $outvalUnits;
3396
        $outval .= $outvalUnits;
3397
3398
        $found = 0;
3399
3400
        // Multiprice
3401
        // If we need a particular price level (from 1 to n)
3402
        if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
3403
            $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
3404
            $sql .= " FROM " . $this->db->prefix() . "product_price";
3405
            $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
3406
            $sql .= " AND entity IN (" . getEntity('productprice') . ")";
3407
            $sql .= " AND price_level = " . ((int) $price_level);
3408
            $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
3409
            $sql .= " LIMIT 1";
3410
3411
            dol_syslog(get_only_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
3412
            $result2 = $this->db->query($sql);
3413
            if ($result2) {
3414
                $objp2 = $this->db->fetch_object($result2);
3415
                if ($objp2) {
3416
                    $found = 1;
3417
                    if ($objp2->price_base_type == 'HT') {
3418
                        $opt .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3419
                        $outval .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3420
                    } else {
3421
                        $opt .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3422
                        $outval .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3423
                    }
3424
                    $outprice_ht = price($objp2->price);
3425
                    $outprice_ttc = price($objp2->price_ttc);
3426
                    $outpricebasetype = $objp2->price_base_type;
3427
                    if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {  // using this option is a bug. kept for backward compatibility
3428
                        $outtva_tx = $objp2->tva_tx;                        // We use the vat rate on line of multiprice
3429
                        $outdefault_vat_code = $objp2->default_vat_code;    // We use the vat code on line of multiprice
3430
                    } else {
3431
                        $outtva_tx = $objp->tva_tx;                            // We use the vat rate of product, not the one on line of multiprice
3432
                        $outdefault_vat_code = $objp->default_vat_code;        // We use the vat code or product, not the one on line of multiprice
3433
                    }
3434
                }
3435
            } else {
3436
                dol_print_error($this->db);
3437
            }
3438
        }
3439
3440
        // Price by quantity
3441
        if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
3442
            $found = 1;
3443
            $outqty = $objp->quantity;
3444
            $outdiscount = $objp->remise_percent;
3445
            if ($objp->quantity == 1) {
3446
                $opt .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
3447
                $outval .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
3448
                $opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3449
                $outval .= $langs->transnoentities("Unit");
3450
            } else {
3451
                $opt .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3452
                $outval .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3453
                $opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3454
                $outval .= $langs->transnoentities("Units");
3455
            }
3456
3457
            $outprice_ht = price($objp->unitprice);
3458
            $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3459
            $outpricebasetype = $objp->price_base_type;
3460
            $outtva_tx = $objp->tva_tx;                            // This value is the value on product when constructProductListOption is called by select_produits_list even if other field $objp-> are from table price_by_qty
3461
            $outdefault_vat_code = $objp->default_vat_code;        // This value is the value on product when constructProductListOption is called by select_produits_list even if other field $objp-> are from table price_by_qty
3462
        }
3463
        if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3464
            $opt .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3465
            $outval .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3466
        }
3467
        if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3468
            $opt .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3469
            $outval .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3470
        }
3471
3472
        // Price by customer
3473
        if (empty($hidepriceinlabel) && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
3474
            if (!empty($objp->idprodcustprice)) {
3475
                $found = 1;
3476
3477
                if ($objp->custprice_base_type == 'HT') {
3478
                    $opt .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3479
                    $outval .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3480
                } else {
3481
                    $opt .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3482
                    $outval .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3483
                }
3484
3485
                $outprice_ht = price($objp->custprice);
3486
                $outprice_ttc = price($objp->custprice_ttc);
3487
                $outpricebasetype = $objp->custprice_base_type;
3488
                $outtva_tx = $objp->custtva_tx;
3489
                $outdefault_vat_code = $objp->custdefault_vat_code;
3490
            }
3491
        }
3492
3493
        // If level no defined or multiprice not found, we used the default price
3494
        if (empty($hidepriceinlabel) && !$found) {
3495
            if ($objp->price_base_type == 'HT') {
3496
                $opt .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3497
                $outval .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3498
            } else {
3499
                $opt .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3500
                $outval .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3501
            }
3502
            $outprice_ht = price($objp->price);
3503
            $outprice_ttc = price($objp->price_ttc);
3504
            $outpricebasetype = $objp->price_base_type;
3505
            $outtva_tx = $objp->tva_tx;
3506
            $outdefault_vat_code = $objp->default_vat_code;
3507
        }
3508
3509
        if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
3510
            if ($user->hasRight('stock', 'lire')) {
3511
                $opt .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3512
3513
                if ($objp->stock > 0) {
3514
                    $outval .= ' - <span class="product_line_stock_ok">';
3515
                } elseif ($objp->stock <= 0) {
3516
                    $outval .= ' - <span class="product_line_stock_too_low">';
3517
                }
3518
                $outval .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3519
                $outval .= '</span>';
3520
                if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) {  // Warning, this option may slow down combo list generation
3521
                    $langs->load("stocks");
3522
3523
                    $tmpproduct = new Product($this->db);
3524
                    $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3525
                    $tmpproduct->load_virtual_stock();
3526
                    $virtualstock = $tmpproduct->stock_theorique;
3527
3528
                    $opt .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
3529
3530
                    $outval .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
3531
                    if ($virtualstock > 0) {
3532
                        $outval .= '<span class="product_line_stock_ok">';
3533
                    } elseif ($virtualstock <= 0) {
3534
                        $outval .= '<span class="product_line_stock_too_low">';
3535
                    }
3536
                    $outval .= $virtualstock;
3537
                    $outval .= '</span>';
3538
3539
                    unset($tmpproduct);
3540
                }
3541
            }
3542
        }
3543
3544
        $parameters = array('objp' => $objp);
3545
        $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
3546
        if (empty($reshook)) {
3547
            $opt .= $hookmanager->resPrint;
3548
        } else {
3549
            $opt = $hookmanager->resPrint;
3550
        }
3551
3552
        $opt .= "</option>\n";
3553
        $optJson = array(
3554
            'key' => $outkey,
3555
            'value' => $outref,
3556
            'label' => $outval,
3557
            'label2' => $outlabel,
3558
            'desc' => $outdesc,
3559
            'type' => $outtype,
3560
            'price_ht' => price2num($outprice_ht),
3561
            'price_ttc' => price2num($outprice_ttc),
3562
            'price_ht_locale' => price(price2num($outprice_ht)),
3563
            'price_ttc_locale' => price(price2num($outprice_ttc)),
3564
            'pricebasetype' => $outpricebasetype,
3565
            'tva_tx' => $outtva_tx,
3566
            'default_vat_code' => $outdefault_vat_code,
3567
            'qty' => $outqty,
3568
            'discount' => $outdiscount,
3569
            'duration_value' => $outdurationvalue,
3570
            'duration_unit' => $outdurationunit,
3571
            'pbq' => $outpbq,
3572
            'labeltrans' => $outlabel_translated,
3573
            'desctrans' => $outdesc_translated,
3574
            'ref_customer' => $outrefcust
3575
        );
3576
    }
3577
3578
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3579
3580
    /**
3581
     * Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list)
3582
     *
3583
     * @param int       $socid          Id third party
3584
     * @param string    $selected       Preselected product
3585
     * @param string    $htmlname       Name of HTML Select
3586
     * @param string    $filtertype     Filter on product type (''=nofilter, 0=product, 1=service)
3587
     * @param string    $filtre         For a SQL filter
3588
     * @param array<string,string|string[]> $ajaxoptions    Options for ajax_autocompleter
3589
     * @param int<0,1>  $hidelabel      Hide label (0=no, 1=yes)
3590
     * @param int<0,1>  $alsoproductwithnosupplierprice 1=Add also product without supplier prices
3591
     * @param string    $morecss        More CSS
3592
     * @param string    $placeholder    Placeholder
3593
     * @return    void
3594
     */
3595
    public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3596
    {
3597
		// phpcs:enable
3598
        global $langs, $conf;
3599
        global $price_level, $status, $finished;
3600
3601
        if (!isset($status)) {
3602
            $status = 1;
3603
        }
3604
3605
        $selected_input_value = '';
3606
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3607
            if ($selected > 0) {
3608
                $producttmpselect = new Product($this->db);
3609
                $producttmpselect->fetch($selected);
3610
                $selected_input_value = $producttmpselect->ref;
3611
                unset($producttmpselect);
3612
            }
3613
3614
            // mode=2 means suppliers products
3615
            $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
3616
            print ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/product/ajax/products.php', $urloption, getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
3617
3618
            print($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
3619
        } else {
3620
            print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3621
        }
3622
    }
3623
3624
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3625
3626
    /**
3627
     *    Return list of suppliers products
3628
     *
3629
     * @param int $socid Id of supplier thirdparty (0 = no filter)
3630
     * @param string $selected Product price preselected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD')
3631
     * @param string $htmlname Name of HTML select
3632
     * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service)
3633
     * @param string $filtre Generic filter. Data must not come from user input.
3634
     * @param string $filterkey Filter of produdts
3635
     * @param int $statut -1=Return all products, 0=Products not on buy, 1=Products on buy
3636
     * @param int $outputmode 0=HTML select string, 1=Array
3637
     * @param int $limit Limit of line number
3638
     * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices
3639
     * @param string $morecss Add more CSS
3640
     * @param int $showstockinlist Show stock information (slower).
3641
     * @param string $placeholder Placeholder
3642
     * @return array|string                Array of keys for json or HTML component
3643
     */
3644
    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 = '')
3645
    {
3646
		// phpcs:enable
3647
        global $langs, $conf, $user;
3648
        global $hookmanager;
3649
3650
        $out = '';
3651
        $outarray = array();
3652
3653
        $maxlengtharticle = (!getDolGlobalString('PRODUCT_MAX_LENGTH_COMBO') ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3654
3655
        $langs->load('stocks');
3656
        // Units
3657
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3658
            $langs->load('other');
3659
        }
3660
3661
        $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock, p.tva_tx as tva_tx_sale, p.default_vat_code as default_vat_code_sale,";
3662
        $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
3663
        if (isModEnabled('multicurrency')) {
3664
            $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
3665
        }
3666
        $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
3667
        $sql .= ", pfp.supplier_reputation";
3668
        // if we use supplier description of the products
3669
        if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
3670
            $sql .= ", pfp.desc_fourn as description";
3671
        } else {
3672
            $sql .= ", p.description";
3673
        }
3674
        // Units
3675
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3676
            $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";
3677
        }
3678
        $sql .= " FROM " . $this->db->prefix() . "product as p";
3679
        $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
3680
        if ($socid > 0) {
3681
            $sql .= " AND pfp.fk_soc = " . ((int) $socid);
3682
        }
3683
        $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
3684
        // Units
3685
        if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3686
            $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
3687
        }
3688
        $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
3689
        if ($statut != -1) {
3690
            $sql .= " AND p.tobuy = " . ((int) $statut);
3691
        }
3692
        if (strval($filtertype) != '') {
3693
            $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3694
        }
3695
        if (!empty($filtre)) {
3696
            $sql .= " " . $filtre;
3697
        }
3698
        // Add where from hooks
3699
        $parameters = array();
3700
        $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3701
        $sql .= $hookmanager->resPrint;
3702
        // Add criteria on ref/label
3703
        if ($filterkey != '') {
3704
            $sql .= ' AND (';
3705
            $prefix = !getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3706
            // For natural search
3707
            $search_crit = explode(' ', $filterkey);
3708
            $i = 0;
3709
            if (count($search_crit) > 1) {
3710
                $sql .= "(";
3711
            }
3712
            foreach ($search_crit as $crit) {
3713
                if ($i > 0) {
3714
                    $sql .= " AND ";
3715
                }
3716
                $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) . "%'";
3717
                if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
3718
                    $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3719
                }
3720
                $sql .= ")";
3721
                $i++;
3722
            }
3723
            if (count($search_crit) > 1) {
3724
                $sql .= ")";
3725
            }
3726
            if (isModEnabled('barcode')) {
3727
                $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3728
                $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3729
            }
3730
            $sql .= ')';
3731
        }
3732
        $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3733
        $sql .= $this->db->plimit($limit, 0);
3734
3735
        // Build output string
3736
3737
        dol_syslog(get_only_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
3738
        $result = $this->db->query($sql);
3739
        if ($result) {
3740
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/product.lib.php';
3741
3742
            $num = $this->db->num_rows($result);
3743
3744
            //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';  // remove select to have id same with combo and ajax
3745
            $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
3746
            if (!$selected) {
3747
                $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
3748
            } else {
3749
                $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
3750
            }
3751
3752
            $i = 0;
3753
            while ($i < $num) {
3754
                $objp = $this->db->fetch_object($result);
3755
3756
                if (is_null($objp->idprodfournprice)) {
3757
                    // There is no supplier price found, we will use the vat rate for sale
3758
                    $objp->tva_tx = $objp->tva_tx_sale;
3759
                    $objp->default_vat_code = $objp->default_vat_code_sale;
3760
                }
3761
3762
                $outkey = $objp->idprodfournprice; // id in table of price
3763
                if (!$outkey && $alsoproductwithnosupplierprice) {
3764
                    $outkey = 'idprod_' . $objp->rowid; // id of product
3765
                }
3766
3767
                $outref = $objp->ref;
3768
                $outbarcode = $objp->barcode;
3769
                $outqty = 1;
3770
                $outdiscount = 0;
3771
                $outtype = $objp->fk_product_type;
3772
                $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3773
                $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
3774
3775
                // Units
3776
                $outvalUnits = '';
3777
                if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3778
                    if (!empty($objp->unit_short)) {
3779
                        $outvalUnits .= ' - ' . $objp->unit_short;
3780
                    }
3781
                    if (!empty($objp->weight) && $objp->weight_units !== null) {
3782
                        $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3783
                        $outvalUnits .= ' - ' . $unitToShow;
3784
                    }
3785
                    if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3786
                        $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
3787
                        $outvalUnits .= ' - ' . $unitToShow;
3788
                    }
3789
                    if (!empty($objp->surface) && $objp->surface_units !== null) {
3790
                        $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3791
                        $outvalUnits .= ' - ' . $unitToShow;
3792
                    }
3793
                    if (!empty($objp->volume) && $objp->volume_units !== null) {
3794
                        $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3795
                        $outvalUnits .= ' - ' . $unitToShow;
3796
                    }
3797
                    if ($outdurationvalue && $outdurationunit) {
3798
                        $da = array(
3799
                            'h' => $langs->trans('Hour'),
3800
                            'd' => $langs->trans('Day'),
3801
                            'w' => $langs->trans('Week'),
3802
                            'm' => $langs->trans('Month'),
3803
                            'y' => $langs->trans('Year')
3804
                        );
3805
                        if (isset($da[$outdurationunit])) {
3806
                            $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
3807
                        }
3808
                    }
3809
                }
3810
3811
                $objRef = $objp->ref;
3812
                if ($filterkey && $filterkey != '') {
3813
                    $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
3814
                }
3815
                $objRefFourn = $objp->ref_fourn;
3816
                if ($filterkey && $filterkey != '') {
3817
                    $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
3818
                }
3819
                $label = $objp->label;
3820
                if ($filterkey && $filterkey != '') {
3821
                    $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
3822
                }
3823
3824
                switch ($objp->fk_product_type) {
3825
                    case Product::TYPE_PRODUCT:
3826
                        $picto = 'product';
3827
                        break;
3828
                    case Product::TYPE_SERVICE:
3829
                        $picto = 'service';
3830
                        break;
3831
                    default:
3832
                        $picto = '';
3833
                        break;
3834
                }
3835
3836
                if (empty($picto)) {
3837
                    $optlabel = '';
3838
                } else {
3839
                    $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
3840
                }
3841
3842
                $optlabel .= $objp->ref;
3843
                if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3844
                    $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
3845
                }
3846
                if (isModEnabled('barcode') && !empty($objp->barcode)) {
3847
                    $optlabel .= ' (' . $outbarcode . ')';
3848
                }
3849
                $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
3850
3851
                $outvallabel = $objRef;
3852
                if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3853
                    $outvallabel .= ' (' . $objRefFourn . ')';
3854
                }
3855
                if (isModEnabled('barcode') && !empty($objp->barcode)) {
3856
                    $outvallabel .= ' (' . $outbarcode . ')';
3857
                }
3858
                $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
3859
3860
                // Units
3861
                $optlabel .= $outvalUnits;
3862
                $outvallabel .= $outvalUnits;
3863
3864
                if (!empty($objp->idprodfournprice)) {
3865
                    $outqty = $objp->quantity;
3866
                    $outdiscount = $objp->remise_percent;
3867
                    if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3868
                        $prod_supplier = new ProductFournisseur($this->db);
3869
                        $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3870
                        $prod_supplier->id = $objp->fk_product;
3871
                        $prod_supplier->fourn_qty = $objp->quantity;
3872
                        $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3873
                        $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3874
3875
                        $priceparser = new PriceParser($this->db);
3876
                        $price_result = $priceparser->parseProductSupplier($prod_supplier);
3877
                        if ($price_result >= 0) {
3878
                            $objp->fprice = $price_result;
3879
                            if ($objp->quantity >= 1) {
3880
                                $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3881
                            }
3882
                        }
3883
                    }
3884
                    if ($objp->quantity == 1) {
3885
                        $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
3886
                        $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
3887
                        $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3888
                        $outvallabel .= $langs->transnoentities("Unit");
3889
                    } else {
3890
                        $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3891
                        $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3892
                        $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3893
                        $outvallabel .= ' ' . $langs->transnoentities("Units");
3894
                    }
3895
3896
                    if ($objp->quantity > 1) {
3897
                        $optlabel .= " (" . price($objp->unitprice * (getDolGlobalString('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
3898
                        $outvallabel .= " (" . price($objp->unitprice * (getDolGlobalString('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
3899
                    }
3900
                    if ($objp->remise_percent >= 1) {
3901
                        $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3902
                        $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3903
                    }
3904
                    if ($objp->duration) {
3905
                        $optlabel .= " - " . $objp->duration;
3906
                        $outvallabel .= " - " . $objp->duration;
3907
                    }
3908
                    if (!$socid) {
3909
                        $optlabel .= " - " . dol_trunc($objp->name, 8);
3910
                        $outvallabel .= " - " . dol_trunc($objp->name, 8);
3911
                    }
3912
                    if ($objp->supplier_reputation) {
3913
                        //TODO dictionary
3914
                        $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
3915
3916
                        $optlabel .= " - " . $reputations[$objp->supplier_reputation];
3917
                        $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
3918
                    }
3919
                } else {
3920
                    $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
3921
                    $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
3922
                }
3923
3924
                if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
3925
                    $novirtualstock = ($showstockinlist == 2);
3926
3927
                    if ($user->hasRight('stock', 'lire')) {
3928
                        $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3929
3930
                        if ($objp->stock > 0) {
3931
                            $optlabel .= ' - <span class="product_line_stock_ok">';
3932
                        } elseif ($objp->stock <= 0) {
3933
                            $optlabel .= ' - <span class="product_line_stock_too_low">';
3934
                        }
3935
                        $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
3936
                        $optlabel .= '</span>';
3937
                        if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) {  // Warning, this option may slow down combo list generation
3938
                            $langs->load("stocks");
3939
3940
                            $tmpproduct = new Product($this->db);
3941
                            $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3942
                            $tmpproduct->load_virtual_stock();
3943
                            $virtualstock = $tmpproduct->stock_theorique;
3944
3945
                            $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
3946
3947
                            $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
3948
                            if ($virtualstock > 0) {
3949
                                $optlabel .= '<span class="product_line_stock_ok">';
3950
                            } elseif ($virtualstock <= 0) {
3951
                                $optlabel .= '<span class="product_line_stock_too_low">';
3952
                            }
3953
                            $optlabel .= $virtualstock;
3954
                            $optlabel .= '</span>';
3955
3956
                            unset($tmpproduct);
3957
                        }
3958
                    }
3959
                }
3960
3961
                $optstart = '<option value="' . $outkey . '"';
3962
                if ($selected && $selected == $objp->idprodfournprice) {
3963
                    $optstart .= ' selected';
3964
                }
3965
                if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3966
                    $optstart .= ' disabled';
3967
                }
3968
3969
                if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3970
                    $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
3971
                    $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
3972
                    $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
3973
                    $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"';
3974
                    $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"';
3975
                    $optstart .= ' data-discount="' . dol_escape_htmltag($outdiscount) . '"';
3976
                    $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"';
3977
                    $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"';
3978
                    $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
3979
                    $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
3980
                    if (isModEnabled('multicurrency')) {
3981
                        $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
3982
                        $optstart .= ' data-multicurrency-up="' . dol_escape_htmltag($objp->multicurrency_unitprice) . '"';
3983
                    }
3984
                }
3985
                $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
3986
3987
                $outarrayentry = array(
3988
                    'key' => $outkey,
3989
                    'value' => $outref,
3990
                    'label' => $outvallabel,
3991
                    'qty' => $outqty,
3992
                    'price_qty_ht' => price2num($objp->fprice, 'MU'),    // Keep higher resolution for price for the min qty
3993
                    'price_unit_ht' => price2num($objp->unitprice, 'MU'),    // This is used to fill the Unit Price
3994
                    'price_ht' => price2num($objp->unitprice, 'MU'),        // This is used to fill the Unit Price (for compatibility)
3995
                    'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
3996
                    'tva_tx' => price2num($objp->tva_tx),
3997
                    'default_vat_code' => $objp->default_vat_code,
3998
                    'supplier_ref' => $objp->ref_fourn,
3999
                    'discount' => $outdiscount,
4000
                    'type' => $outtype,
4001
                    'duration_value' => $outdurationvalue,
4002
                    'duration_unit' => $outdurationunit,
4003
                    'disabled' => empty($objp->idprodfournprice),
4004
                    'description' => $objp->description
4005
                );
4006
                if (isModEnabled('multicurrency')) {
4007
                    $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4008
                    $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4009
                }
4010
4011
                $parameters = array(
4012
                    'objp' => &$objp,
4013
                    'optstart' => &$optstart,
4014
                    'optlabel' => &$optlabel,
4015
                    'outvallabel' => &$outvallabel,
4016
                    'outarrayentry' => &$outarrayentry
4017
                );
4018
                $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4019
4020
4021
                // Add new entry
4022
                // "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
4023
                // "label" value of json key array is used by jQuery automatically as text for combo box
4024
                $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4025
                $outarraypush = array(
4026
                    'key' => $outkey,
4027
                    'value' => $outref,
4028
                    'label' => $outvallabel,
4029
                    'qty' => $outqty,
4030
                    'price_qty_ht' => price2num($objp->fprice, 'MU'),        // Keep higher resolution for price for the min qty
4031
                    'price_qty_ht_locale' => price($objp->fprice),
4032
                    'price_unit_ht' => price2num($objp->unitprice, 'MU'),    // This is used to fill the Unit Price
4033
                    'price_unit_ht_locale' => price($objp->unitprice),
4034
                    'price_ht' => price2num($objp->unitprice, 'MU'),        // This is used to fill the Unit Price (for compatibility)
4035
                    'tva_tx_formated' => price($objp->tva_tx),
4036
                    'tva_tx' => price2num($objp->tva_tx),
4037
                    'default_vat_code' => $objp->default_vat_code,
4038
                    'supplier_ref' => $objp->ref_fourn,
4039
                    'discount' => $outdiscount,
4040
                    'type' => $outtype,
4041
                    'duration_value' => $outdurationvalue,
4042
                    'duration_unit' => $outdurationunit,
4043
                    'disabled' => empty($objp->idprodfournprice),
4044
                    'description' => $objp->description
4045
                );
4046
                if (isModEnabled('multicurrency')) {
4047
                    $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4048
                    $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4049
                }
4050
                array_push($outarray, $outarraypush);
4051
4052
                // Example of var_dump $outarray
4053
                // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4054
                //           ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
4055
                //           ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4056
                //}
4057
                //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4058
                //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
4059
                //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4060
4061
                $i++;
4062
            }
4063
            $out .= '</select>';
4064
4065
            $this->db->free($result);
4066
4067
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4068
            $out .= ajax_combobox($htmlname);
4069
        } else {
4070
            dol_print_error($this->db);
4071
        }
4072
4073
        if (empty($outputmode)) {
4074
            return $out;
4075
        }
4076
        return $outarray;
4077
    }
4078
4079
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4080
4081
    /**
4082
     *    Return list of suppliers prices for a product
4083
     *
4084
     * @param int $productid Id of product
4085
     * @param string $htmlname Name of HTML field
4086
     * @param int $selected_supplier Pre-selected supplier if more than 1 result
4087
     * @return        string
4088
     */
4089
    public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4090
    {
4091
		// phpcs:enable
4092
        global $langs, $conf;
4093
4094
        $langs->load('stocks');
4095
4096
        $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4097
        $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4098
        $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4099
        $sql .= " FROM " . $this->db->prefix() . "product as p";
4100
        $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4101
        $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4102
        $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4103
        $sql .= " AND p.tobuy = 1";
4104
        $sql .= " AND s.fournisseur = 1";
4105
        $sql .= " AND p.rowid = " . ((int) $productid);
4106
        if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4107
            $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4108
        } else {
4109
            $sql .= " ORDER BY pfp.unitprice ASC";
4110
        }
4111
4112
        dol_syslog(get_only_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4113
        $result = $this->db->query($sql);
4114
4115
        if ($result) {
4116
            $num = $this->db->num_rows($result);
4117
4118
            $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4119
4120
            if (!$num) {
4121
                $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4122
            } else {
4123
                $form .= '<option value="0">&nbsp;</option>';
4124
4125
                $i = 0;
4126
                while ($i < $num) {
4127
                    $objp = $this->db->fetch_object($result);
4128
4129
                    $opt = '<option value="' . $objp->idprodfournprice . '"';
4130
                    //if there is only one supplier, preselect it
4131
                    if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
4132
                        $opt .= ' selected';
4133
                    }
4134
                    $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
4135
4136
                    if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4137
                        $prod_supplier = new ProductFournisseur($this->db);
4138
                        $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4139
                        $prod_supplier->id = $productid;
4140
                        $prod_supplier->fourn_qty = $objp->quantity;
4141
                        $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4142
                        $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4143
4144
                        $priceparser = new PriceParser($this->db);
4145
                        $price_result = $priceparser->parseProductSupplier($prod_supplier);
4146
                        if ($price_result >= 0) {
4147
                            $objp->fprice = $price_result;
4148
                            if ($objp->quantity >= 1) {
4149
                                $objp->unitprice = $objp->fprice / $objp->quantity;
4150
                            }
4151
                        }
4152
                    }
4153
                    if ($objp->quantity == 1) {
4154
                        $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4155
                    }
4156
4157
                    $opt .= $objp->quantity . ' ';
4158
4159
                    if ($objp->quantity == 1) {
4160
                        $opt .= $langs->trans("Unit");
4161
                    } else {
4162
                        $opt .= $langs->trans("Units");
4163
                    }
4164
                    if ($objp->quantity > 1) {
4165
                        $opt .= " - ";
4166
                        $opt .= price($objp->unitprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit");
4167
                    }
4168
                    if ($objp->duration) {
4169
                        $opt .= " - " . $objp->duration;
4170
                    }
4171
                    $opt .= "</option>\n";
4172
4173
                    $form .= $opt;
4174
                    $i++;
4175
                }
4176
            }
4177
4178
            $form .= '</select>';
4179
            $this->db->free($result);
4180
            return $form;
4181
        } else {
4182
            dol_print_error($this->db);
4183
            return '';
4184
        }
4185
    }
4186
4187
4188
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4189
    /**
4190
     *      Load into cache list of payment terms
4191
     *
4192
     * @return     int             Nb of lines loaded, <0 if KO
4193
     */
4194
    public function load_cache_conditions_paiements()
4195
    {
4196
		// phpcs:enable
4197
        global $langs;
4198
4199
        $num = count($this->cache_conditions_paiements);
4200
        if ($num > 0) {
4201
            return 0; // Cache already loaded
4202
        }
4203
4204
        dol_syslog(__METHOD__, LOG_DEBUG);
4205
4206
        $sql = "SELECT rowid, code, libelle as label, deposit_percent";
4207
        $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
4208
        $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
4209
        $sql .= " AND active > 0";
4210
        $sql .= " ORDER BY sortorder";
4211
4212
        $resql = $this->db->query($sql);
4213
        if ($resql) {
4214
            $num = $this->db->num_rows($resql);
4215
            $i = 0;
4216
            while ($i < $num) {
4217
                $obj = $this->db->fetch_object($resql);
4218
4219
                // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4220
                $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4221
                $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
4222
                $this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
4223
                $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = $obj->deposit_percent;
4224
                $i++;
4225
            }
4226
4227
            //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1);     // We use the field sortorder of table
4228
4229
            return $num;
4230
        } else {
4231
            dol_print_error($this->db);
4232
            return -1;
4233
        }
4234
    }
4235
4236
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4237
4238
    /**
4239
     *      Load int a cache property th elist of possible delivery delays.
4240
     *
4241
     * @return     int             Nb of lines loaded, <0 if KO
4242
     */
4243
    public function load_cache_availability()
4244
    {
4245
		// phpcs:enable
4246
        global $langs;
4247
4248
        $num = count($this->cache_availability);    // TODO Use $conf->cache['availability'] instead of $this->cache_availability
4249
        if ($num > 0) {
4250
            return 0; // Cache already loaded
4251
        }
4252
4253
        dol_syslog(__METHOD__, LOG_DEBUG);
4254
4255
        $langs->load('propal');
4256
4257
        $sql = "SELECT rowid, code, label, position";
4258
        $sql .= " FROM " . $this->db->prefix() . 'c_availability';
4259
        $sql .= " WHERE active > 0";
4260
4261
        $resql = $this->db->query($sql);
4262
        if ($resql) {
4263
            $num = $this->db->num_rows($resql);
4264
            $i = 0;
4265
            while ($i < $num) {
4266
                $obj = $this->db->fetch_object($resql);
4267
4268
                // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4269
                $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4270
                $this->cache_availability[$obj->rowid]['code'] = $obj->code;
4271
                $this->cache_availability[$obj->rowid]['label'] = $label;
4272
                $this->cache_availability[$obj->rowid]['position'] = $obj->position;
4273
                $i++;
4274
            }
4275
4276
            $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
4277
4278
            return $num;
4279
        } else {
4280
            dol_print_error($this->db);
4281
            return -1;
4282
        }
4283
    }
4284
4285
    /**
4286
     * Return the list of type of delay available.
4287
     *
4288
     * @param   string      $selected Id du type de delais pre-selectionne
4289
     * @param   string      $htmlname Nom de la zone select
4290
     * @param   string      $filtertype To add a filter
4291
     * @param   int         $addempty Add empty entry
4292
     * @param   string      $morecss More CSS
4293
     * @return  void
4294
     */
4295
    public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
4296
    {
4297
        global $langs, $user;
4298
4299
        $this->load_cache_availability();
4300
4301
        dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
4302
4303
        print '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4304
        if ($addempty) {
4305
            print '<option value="0">&nbsp;</option>';
4306
        }
4307
        foreach ($this->cache_availability as $id => $arrayavailability) {
4308
            if ($selected == $id) {
4309
                print '<option value="' . $id . '" selected>';
4310
            } else {
4311
                print '<option value="' . $id . '">';
4312
            }
4313
            print dol_escape_htmltag($arrayavailability['label']);
4314
            print '</option>';
4315
        }
4316
        print '</select>';
4317
        if ($user->admin) {
4318
            print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4319
        }
4320
        print ajax_combobox($htmlname);
4321
    }
4322
4323
    /**
4324
     * Load into cache cache_demand_reason, array of input reasons
4325
     *
4326
     * @return     int             Nb of lines loaded, <0 if KO
4327
     */
4328
    public function loadCacheInputReason()
4329
    {
4330
        global $langs;
4331
4332
        $num = count($this->cache_demand_reason);    // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
4333
        if ($num > 0) {
4334
            return 0; // Cache already loaded
4335
        }
4336
4337
        $sql = "SELECT rowid, code, label";
4338
        $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
4339
        $sql .= " WHERE active > 0";
4340
4341
        $resql = $this->db->query($sql);
4342
        if ($resql) {
4343
            $num = $this->db->num_rows($resql);
4344
            $i = 0;
4345
            $tmparray = array();
4346
            while ($i < $num) {
4347
                $obj = $this->db->fetch_object($resql);
4348
4349
                // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4350
                $label = ($obj->label != '-' ? $obj->label : '');
4351
                if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
4352
                    $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
4353
                }
4354
                if ($langs->trans($obj->code) != $obj->code) {
4355
                    $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
4356
                }
4357
4358
                $tmparray[$obj->rowid]['id'] = $obj->rowid;
4359
                $tmparray[$obj->rowid]['code'] = $obj->code;
4360
                $tmparray[$obj->rowid]['label'] = $label;
4361
                $i++;
4362
            }
4363
4364
            $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
4365
4366
            unset($tmparray);
4367
            return $num;
4368
        } else {
4369
            dol_print_error($this->db);
4370
            return -1;
4371
        }
4372
    }
4373
4374
    /**
4375
     * Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
4376
     * List found into table c_input_reason loaded by loadCacheInputReason
4377
     *
4378
     * @param   string      $selected   Id or code of type origin to select by default
4379
     * @param   string      $htmlname   Nom de la zone select
4380
     * @param   string      $exclude    To exclude a code value (Example: SRC_PROP)
4381
     * @param   int         $addempty   Add an empty entry
4382
     * @param   string      $morecss    Add more css to the HTML select component
4383
     * @param   int         $notooltip  Do not show the tooltip for admin
4384
     * @return  void
4385
     */
4386
    public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
4387
    {
4388
        global $langs, $user;
4389
4390
        $this->loadCacheInputReason();
4391
4392
        print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4393
        if ($addempty) {
4394
            print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
4395
        }
4396
        foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
4397
            if ($arraydemandreason['code'] == $exclude) {
4398
                continue;
4399
            }
4400
4401
            if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
4402
                print '<option value="' . $arraydemandreason['id'] . '" selected>';
4403
            } else {
4404
                print '<option value="' . $arraydemandreason['id'] . '">';
4405
            }
4406
            $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
4407
            print $langs->trans($label);
4408
            print '</option>';
4409
        }
4410
        print '</select>';
4411
        if ($user->admin && empty($notooltip)) {
4412
            print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4413
        }
4414
        print ajax_combobox('select_' . $htmlname);
4415
    }
4416
4417
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4418
4419
    /**
4420
     *      Charge dans cache la liste des types de paiements possibles
4421
     *
4422
     * @return     int                 Nb of lines loaded, <0 if KO
4423
     */
4424
    public function load_cache_types_paiements()
4425
    {
4426
		// phpcs:enable
4427
        global $langs;
4428
4429
        $num = count($this->cache_types_paiements);        // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
4430
        if ($num > 0) {
4431
            return $num; // Cache already loaded
4432
        }
4433
4434
        dol_syslog(__METHOD__, LOG_DEBUG);
4435
4436
        $this->cache_types_paiements = array();
4437
4438
        $sql = "SELECT id, code, libelle as label, type, active";
4439
        $sql .= " FROM " . $this->db->prefix() . "c_paiement";
4440
        $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
4441
4442
        $resql = $this->db->query($sql);
4443
        if ($resql) {
4444
            $num = $this->db->num_rows($resql);
4445
            $i = 0;
4446
            while ($i < $num) {
4447
                $obj = $this->db->fetch_object($resql);
4448
4449
                // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4450
                $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4451
                $this->cache_types_paiements[$obj->id]['id'] = $obj->id;
4452
                $this->cache_types_paiements[$obj->id]['code'] = $obj->code;
4453
                $this->cache_types_paiements[$obj->id]['label'] = $label;
4454
                $this->cache_types_paiements[$obj->id]['type'] = $obj->type;
4455
                $this->cache_types_paiements[$obj->id]['active'] = $obj->active;
4456
                $i++;
4457
            }
4458
4459
            $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
4460
4461
            return $num;
4462
        } else {
4463
            dol_print_error($this->db);
4464
            return -1;
4465
        }
4466
    }
4467
4468
4469
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4470
4471
    /**
4472
     *    print list of payment modes.
4473
     *    Constant MAIN_DEFAULT_PAYMENT_TERM_ID can be used to set default value but scope is all application, probably not what you want.
4474
     *    See instead to force the default value by the caller.
4475
     *
4476
     * @param int $selected Id of payment term to preselect by default
4477
     * @param string $htmlname Nom de la zone select
4478
     * @param int $filtertype If > 0, include payment terms with deposit percentage (for objects other than invoices and invoice templates)
4479
     * @param int $addempty Add an empty entry
4480
     * @param int $noinfoadmin 0=Add admin info, 1=Disable admin info
4481
     * @param string $morecss Add more CSS on select tag
4482
     * @param int    $deposit_percent < 0 : deposit_percent input makes no sense (for example, in list filters)
4483
     *                                0 : use default deposit percentage from entry
4484
     *                                > 0 : force deposit percentage (for example, from company object)
4485
     * @param int $noprint if set to one we return the html to print, if 0 (default) we print it
4486
     * @return    void|string
4487
     * @deprecated Use getSelectConditionsPaiements() instead and handle noprint locally.
4488
     */
4489
    public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
4490
    {
4491
		// phpcs:enable
4492
        $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
4493
        if (empty($noprint)) {
4494
            print $out;
4495
        } else {
4496
            return $out;
4497
        }
4498
    }
4499
4500
4501
    /**
4502
     *    Return list of payment modes.
4503
     *    Constant MAIN_DEFAULT_PAYMENT_TERM_ID can be used to set default value but scope is all application, probably not what you want.
4504
     *    See instead to force the default value by the caller.
4505
     *
4506
     * @param int $selected Id of payment term to preselect by default
4507
     * @param string $htmlname Nom de la zone select
4508
     * @param int $filtertype If > 0, include payment terms with deposit percentage (for objects other than invoices and invoice templates)
4509
     * @param int $addempty Add an empty entry
4510
     * @param int $noinfoadmin 0=Add admin info, 1=Disable admin info
4511
     * @param string $morecss Add more CSS on select tag
4512
     * @param int    $deposit_percent < 0 : deposit_percent input makes no sense (for example, in list filters)
4513
     *                                0 : use default deposit percentage from entry
4514
     *                                > 0 : force deposit percentage (for example, from company object)
4515
     * @return    string                        String for the HTML select component
4516
     */
4517
    public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4518
    {
4519
        global $langs, $user, $conf;
4520
4521
        $out = '';
4522
        dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
4523
4524
        $this->load_cache_conditions_paiements();
4525
4526
        // Set default value if not already set by caller
4527
        if (empty($selected) && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
4528
            dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
4529
            $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TERM_ID');
4530
        }
4531
4532
        $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4533
        if ($addempty) {
4534
            $out .= '<option value="0">&nbsp;</option>';
4535
        }
4536
4537
        $selectedDepositPercent = null;
4538
4539
        foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4540
            if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
4541
                continue;
4542
            }
4543
4544
            if ($selected == $id) {
4545
                $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
4546
                $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
4547
            } else {
4548
                $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
4549
            }
4550
            $label = $arrayconditions['label'];
4551
4552
            if (!empty($arrayconditions['deposit_percent'])) {
4553
                $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
4554
            }
4555
4556
            $out .= $label;
4557
            $out .= '</option>';
4558
        }
4559
        $out .= '</select>';
4560
        if ($user->admin && empty($noinfoadmin)) {
4561
            $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4562
        }
4563
        $out .= ajax_combobox($htmlname);
4564
4565
        if ($deposit_percent >= 0) {
4566
            $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
4567
            $out .= $langs->trans('DepositPercent') . ' : ';
4568
            $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
4569
            $out .= '</span>';
4570
            $out .= '
4571
				<script nonce="' . getNonce() . '">
4572
					$(document).ready(function () {
4573
						$("#' . $htmlname . '").change(function () {
4574
							let $selected = $(this).find("option:selected");
4575
							let depositPercent = $selected.attr("data-deposit_percent");
4576
4577
							if (depositPercent.length > 0) {
4578
								$("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
4579
							} else {
4580
								$("#' . $htmlname . '_deposit_percent_container").hide();
4581
							}
4582
4583
							return true;
4584
						});
4585
					});
4586
				</script>';
4587
        }
4588
4589
        return $out;
4590
    }
4591
4592
4593
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4594
4595
    /**
4596
     * Return list of payment methods
4597
     * Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want.
4598
     *
4599
     * @param   string  $selected       Id or code or preselected payment mode
4600
     * @param   string  $htmlname       Name of select field
4601
     * @param   string  $filtertype     To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
4602
     * @param   int     $format         0=id+label, 1=code+code, 2=code+label, 3=id+code
4603
     * @param   int     $empty          1=can be empty, 0 otherwise
4604
     * @param   int     $noadmininfo    0=Add admin info, 1=Disable admin info
4605
     * @param   int     $maxlength      Max length of label
4606
     * @param   int     $active         Active or not, -1 = all
4607
     * @param   string  $morecss        Add more CSS on select tag
4608
     * @param   int     $nooutput       1=Return string, do not send to output
4609
     * @return  string|void             String for the HTML select component
4610
     */
4611
    public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4612
    {
4613
		// phpcs:enable
4614
        global $langs, $user, $conf;
4615
4616
        $out = '';
4617
4618
        dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
4619
4620
        $filterarray = array();
4621
        if ($filtertype == 'CRDT') {
4622
            $filterarray = array(0, 2, 3);
4623
        } elseif ($filtertype == 'DBIT') {
4624
            $filterarray = array(1, 2, 3);
4625
        } elseif ($filtertype != '' && $filtertype != '-1') {
4626
            $filterarray = explode(',', $filtertype);
4627
        }
4628
4629
        $this->load_cache_types_paiements();
4630
4631
        // Set default value if not already set by caller
4632
        if (empty($selected) && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
4633
            dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
4634
            $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
4635
        }
4636
4637
        $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4638
        if ($empty) {
4639
            $out .= '<option value="">&nbsp;</option>';
4640
        }
4641
        foreach ($this->cache_types_paiements as $id => $arraytypes) {
4642
            // If not good status
4643
            if ($active >= 0 && $arraytypes['active'] != $active) {
4644
                continue;
4645
            }
4646
4647
            // We skip of the user requested to filter on specific payment methods
4648
            if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4649
                continue;
4650
            }
4651
4652
            // We discard empty lines if showempty is on because an empty line has already been output.
4653
            if ($empty && empty($arraytypes['code'])) {
4654
                continue;
4655
            }
4656
4657
            if ($format == 0) {
4658
                $out .= '<option value="' . $id . '"';
4659
            } elseif ($format == 1) {
4660
                $out .= '<option value="' . $arraytypes['code'] . '"';
4661
            } elseif ($format == 2) {
4662
                $out .= '<option value="' . $arraytypes['code'] . '"';
4663
            } elseif ($format == 3) {
4664
                $out .= '<option value="' . $id . '"';
4665
            }
4666
            // Print attribute selected or not
4667
            if ($format == 1 || $format == 2) {
4668
                if ($selected == $arraytypes['code']) {
4669
                    $out .= ' selected';
4670
                }
4671
            } else {
4672
                if ($selected == $id) {
4673
                    $out .= ' selected';
4674
                }
4675
            }
4676
            $out .= '>';
4677
            $value = '';
4678
            if ($format == 0) {
4679
                $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4680
            } elseif ($format == 1) {
4681
                $value = $arraytypes['code'];
4682
            } elseif ($format == 2) {
4683
                $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4684
            } elseif ($format == 3) {
4685
                $value = $arraytypes['code'];
4686
            }
4687
            $out .= $value ? $value : '&nbsp;';
4688
            $out .= '</option>';
4689
        }
4690
        $out .= '</select>';
4691
        if ($user->admin && !$noadmininfo) {
4692
            $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4693
        }
4694
        $out .= ajax_combobox('select' . $htmlname);
4695
4696
        if (empty($nooutput)) {
4697
            print $out;
4698
        } else {
4699
            return $out;
4700
        }
4701
    }
4702
4703
4704
    /**
4705
     *  Selection HT or TTC
4706
     *
4707
     * @param string $selected Id pre-selectionne
4708
     * @param string $htmlname Nom de la zone select
4709
     * @param int    $addjscombo Add js combo
4710
     * @return    string                    Code of HTML select to chose tax or not
4711
     */
4712
    public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4713
    {
4714
        global $langs;
4715
4716
        $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4717
        $options = array(
4718
            'HT' => $langs->trans("HT"),
4719
            'TTC' => $langs->trans("TTC")
4720
        );
4721
        foreach ($options as $id => $value) {
4722
            if ($selected == $id) {
4723
                $return .= '<option value="' . $id . '" selected>' . $value;
4724
            } else {
4725
                $return .= '<option value="' . $id . '">' . $value;
4726
            }
4727
            $return .= '</option>';
4728
        }
4729
        $return .= '</select>';
4730
        if ($addjscombo) {
4731
            $return .= ajax_combobox('select_' . $htmlname);
4732
        }
4733
4734
        return $return;
4735
    }
4736
4737
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4738
4739
    /**
4740
     *      Load in cache list of transport mode
4741
     *
4742
     * @return     int                 Nb of lines loaded, <0 if KO
4743
     */
4744
    public function load_cache_transport_mode()
4745
    {
4746
		// phpcs:enable
4747
        global $langs;
4748
4749
        $num = count($this->cache_transport_mode);        // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4750
        if ($num > 0) {
4751
            return $num; // Cache already loaded
4752
        }
4753
4754
        dol_syslog(__METHOD__, LOG_DEBUG);
4755
4756
        $this->cache_transport_mode = array();
4757
4758
        $sql = "SELECT rowid, code, label, active";
4759
        $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
4760
        $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
4761
4762
        $resql = $this->db->query($sql);
4763
        if ($resql) {
4764
            $num = $this->db->num_rows($resql);
4765
            $i = 0;
4766
            while ($i < $num) {
4767
                $obj = $this->db->fetch_object($resql);
4768
4769
                // If traduction exist, we use it else we take the default label
4770
                $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4771
                $this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4772
                $this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4773
                $this->cache_transport_mode[$obj->rowid]['label'] = $label;
4774
                $this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4775
                $i++;
4776
            }
4777
4778
            $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4779
4780
            return $num;
4781
        } else {
4782
            dol_print_error($this->db);
4783
            return -1;
4784
        }
4785
    }
4786
4787
    /**
4788
     *      Return list of transport mode for intracomm report
4789
     *
4790
     * @param string $selected Id of the transport mode preselected
4791
     * @param string $htmlname Name of the select field
4792
     * @param int $format 0=id+label, 1=code+code, 2=code+label, 3=id+code
4793
     * @param int $empty 1=can be empty, 0 else
4794
     * @param int $noadmininfo 0=Add admin info, 1=Disable admin info
4795
     * @param int $maxlength Max length of label
4796
     * @param int $active Active or not, -1 = all
4797
     * @param string $morecss Add more CSS on select tag
4798
     * @return    void
4799
     */
4800
    public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4801
    {
4802
        global $langs, $user;
4803
4804
        dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
4805
4806
        $this->load_cache_transport_mode();
4807
4808
        print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4809
        if ($empty) {
4810
            print '<option value="">&nbsp;</option>';
4811
        }
4812
        foreach ($this->cache_transport_mode as $id => $arraytypes) {
4813
            // If not good status
4814
            if ($active >= 0 && $arraytypes['active'] != $active) {
4815
                continue;
4816
            }
4817
4818
            // We discard empty line if showempty is on because an empty line has already been output.
4819
            if ($empty && empty($arraytypes['code'])) {
4820
                continue;
4821
            }
4822
4823
            if ($format == 0) {
4824
                print '<option value="' . $id . '"';
4825
            } elseif ($format == 1) {
4826
                print '<option value="' . $arraytypes['code'] . '"';
4827
            } elseif ($format == 2) {
4828
                print '<option value="' . $arraytypes['code'] . '"';
4829
            } elseif ($format == 3) {
4830
                print '<option value="' . $id . '"';
4831
            }
4832
            // If text is selected, we compare with code, else with id
4833
            if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4834
                print ' selected';
4835
            } elseif ($selected == $id) {
4836
                print ' selected';
4837
            }
4838
            print '>';
4839
            $value = '';
4840
            if ($format == 0) {
4841
                $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4842
            } elseif ($format == 1) {
4843
                $value = $arraytypes['code'];
4844
            } elseif ($format == 2) {
4845
                $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4846
            } elseif ($format == 3) {
4847
                $value = $arraytypes['code'];
4848
            }
4849
            print $value ? $value : '&nbsp;';
4850
            print '</option>';
4851
        }
4852
        print '</select>';
4853
        if ($user->admin && !$noadmininfo) {
4854
            print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4855
        }
4856
    }
4857
4858
    /**
4859
     * Return a HTML select list of shipping mode
4860
     *
4861
     * @param string    $selected       Id shipping mode preselected
4862
     * @param string    $htmlname       Name of select zone
4863
     * @param string    $filtre         To filter list. This parameter must not come from input of users
4864
     * @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.
4865
     * @param string    $moreattrib     To add more attribute on select
4866
     * @param int       $noinfoadmin    0=Add admin info, 1=Disable admin info
4867
     * @param string    $morecss        More CSS
4868
     * @return void
4869
     */
4870
    public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4871
    {
4872
        global $langs, $user;
4873
4874
        $langs->load("admin");
4875
        $langs->load("deliveries");
4876
4877
        $sql = "SELECT rowid, code, libelle as label";
4878
        $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
4879
        $sql .= " WHERE active > 0";
4880
        if ($filtre) {
4881
            $sql .= " AND " . $filtre;
4882
        }
4883
        $sql .= " ORDER BY libelle ASC";
4884
4885
        dol_syslog(get_only_class($this) . "::selectShippingMode", LOG_DEBUG);
4886
        $result = $this->db->query($sql);
4887
        if ($result) {
4888
            $num = $this->db->num_rows($result);
4889
            $i = 0;
4890
            if ($num) {
4891
                print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
4892
                if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4893
                    print '<option value="-1">&nbsp;</option>';
4894
                }
4895
                while ($i < $num) {
4896
                    $obj = $this->db->fetch_object($result);
4897
                    if ($selected == $obj->rowid) {
4898
                        print '<option value="' . $obj->rowid . '" selected>';
4899
                    } else {
4900
                        print '<option value="' . $obj->rowid . '">';
4901
                    }
4902
                    print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
4903
                    print '</option>';
4904
                    $i++;
4905
                }
4906
                print "</select>";
4907
                if ($user->admin && empty($noinfoadmin)) {
4908
                    print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4909
                }
4910
4911
                print ajax_combobox('select' . $htmlname);
4912
            } else {
4913
                print $langs->trans("NoShippingMethodDefined");
4914
            }
4915
        } else {
4916
            dol_print_error($this->db);
4917
        }
4918
    }
4919
4920
    /**
4921
     *    Display form to select shipping mode
4922
     *
4923
     * @param string $page Page
4924
     * @param string $selected Id of shipping mode
4925
     * @param string $htmlname Name of select html field
4926
     * @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.
4927
     * @return    void
4928
     */
4929
    public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4930
    {
4931
        global $langs;
4932
4933
        $langs->load("deliveries");
4934
4935
        if ($htmlname != "none") {
4936
            print '<form method="POST" action="' . $page . '">';
4937
            print '<input type="hidden" name="action" value="setshippingmethod">';
4938
            print '<input type="hidden" name="token" value="' . newToken() . '">';
4939
            $this->selectShippingMethod($selected, $htmlname, '', $addempty);
4940
            print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
4941
            print '</form>';
4942
        } else {
4943
            if ($selected) {
4944
                $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4945
                print $langs->trans("SendingMethod" . strtoupper($code));
4946
            } else {
4947
                print "&nbsp;";
4948
            }
4949
        }
4950
    }
4951
4952
    /**
4953
     * Creates HTML last in cycle situation invoices selector
4954
     *
4955
     * @param string $selected Preselected ID
4956
     * @param int $socid Company ID
4957
     *
4958
     * @return    string                     HTML select
4959
     */
4960
    public function selectSituationInvoices($selected = '', $socid = 0)
4961
    {
4962
        global $langs;
4963
4964
        $langs->load('bills');
4965
4966
        $opt = '<option value="" selected></option>';
4967
        $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4968
        $sql .= ' FROM ' . $this->db->prefix() . 'facture';
4969
        $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
4970
        $sql .= ' AND situation_counter >= 1';
4971
        $sql .= ' AND fk_soc = ' . (int) $socid;
4972
        $sql .= ' AND type <> 2';
4973
        $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4974
        $resql = $this->db->query($sql);
4975
4976
        if ($resql && $this->db->num_rows($resql) > 0) {
4977
            // Last seen cycle
4978
            $ref = 0;
4979
            while ($obj = $this->db->fetch_object($resql)) {
4980
                //Same cycle ?
4981
                if ($obj->situation_cycle_ref != $ref) {
4982
                    // Just seen this cycle
4983
                    $ref = $obj->situation_cycle_ref;
4984
                    //not final ?
4985
                    if ($obj->situation_final != 1) {
4986
                        //Not prov?
4987
                        if (substr($obj->ref, 1, 4) != 'PROV') {
4988
                            if ($selected == $obj->rowid) {
4989
                                $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
4990
                            } else {
4991
                                $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
4992
                            }
4993
                        }
4994
                    }
4995
                }
4996
            }
4997
        } else {
4998
            dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4999
        }
5000
        if ($opt == '<option value ="" selected></option>') {
5001
            $opt = '<option value ="0" selected>' . $langs->trans('NoSituations') . '</option>';
5002
        }
5003
        return $opt;
5004
    }
5005
5006
    /**
5007
     * Creates HTML units selector (code => label)
5008
     *
5009
     * @param   string      $selected   Preselected Unit ID
5010
     * @param   string      $htmlname   Select name
5011
     * @param   int<0,1>    $showempty  Add an empty line
5012
     * @param   string      $unit_type  Restrict to one given unit type
5013
     * @return  string                  HTML select
5014
     */
5015
    public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
5016
    {
5017
        global $langs;
5018
5019
        $langs->load('products');
5020
5021
        $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
5022
5023
        $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
5024
        $sql .= ' WHERE active > 0';
5025
        if (!empty($unit_type)) {
5026
            $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
5027
        }
5028
        $sql .= " ORDER BY sortorder";
5029
5030
        $resql = $this->db->query($sql);
5031
        if ($resql && $this->db->num_rows($resql) > 0) {
5032
            if ($showempty) {
5033
                $return .= '<option value="none"></option>';
5034
            }
5035
5036
            while ($res = $this->db->fetch_object($resql)) {
5037
                $unitLabel = $res->label;
5038
                if (!empty($langs->tab_translate['unit' . $res->code])) {    // check if Translation is available before
5039
                    $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
5040
                }
5041
5042
                if ($selected == $res->rowid) {
5043
                    $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
5044
                } else {
5045
                    $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
5046
                }
5047
            }
5048
            $return .= '</select>';
5049
        }
5050
        return $return;
5051
    }
5052
5053
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5054
5055
    /**
5056
     *  Return a HTML select list of bank accounts
5057
     *
5058
     * @param int|string    $selected       Id account preselected
5059
     * @param string        $htmlname       Name of select zone
5060
     * @param int           $status         Status of searched accounts (0=open, 1=closed, 2=both)
5061
     * @param string        $filtre         To filter the list. This parameter must not come from input of users
5062
     * @param int|string    $useempty       1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries.
5063
     * @param string        $moreattrib     To add more attribute on select
5064
     * @param int           $showcurrency   Show currency in label
5065
     * @param string        $morecss        More CSS
5066
     * @param int           $nooutput       1=Return string, do not send to output
5067
     * @return int|string                   If noouput=0: Return integer <0 if error, Num of bank account found if OK (0, 1, 2, ...), If nooutput=1: Return a HTML select string.
5068
     */
5069
    public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
5070
    {
5071
		// phpcs:enable
5072
        global $langs;
5073
5074
        $out = '';
5075
5076
        $langs->load("admin");
5077
        $num = 0;
5078
5079
        $sql = "SELECT rowid, label, bank, clos as status, currency_code";
5080
        $sql .= " FROM " . $this->db->prefix() . "bank_account";
5081
        $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
5082
        if ($status != 2) {
5083
            $sql .= " AND clos = " . (int) $status;
5084
        }
5085
        if ($filtre) {  // TODO Support USF
5086
            $sql .= " AND " . $filtre;
5087
        }
5088
        $sql .= " ORDER BY label";
5089
5090
        dol_syslog(get_only_class($this) . "::select_comptes", LOG_DEBUG);
5091
        $result = $this->db->query($sql);
5092
        if ($result) {
5093
            $num = $this->db->num_rows($result);
5094
            $i = 0;
5095
            if ($num) {
5096
                $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5097
5098
                if (!empty($useempty) && !is_numeric($useempty)) {
5099
                    $out .= '<option value="-1">' . $langs->trans($useempty) . '</option>';
5100
                } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5101
                    $out .= '<option value="-1">&nbsp;</option>';
5102
                }
5103
5104
                while ($i < $num) {
5105
                    $obj = $this->db->fetch_object($result);
5106
                    if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
5107
                        $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" selected>';
5108
                    } else {
5109
                        $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '">';
5110
                    }
5111
                    $out .= trim($obj->label);
5112
                    if ($showcurrency) {
5113
                        $out .= ' (' . $obj->currency_code . ')';
5114
                    }
5115
                    if ($status == 2 && $obj->status == 1) {
5116
                        $out .= ' (' . $langs->trans("Closed") . ')';
5117
                    }
5118
                    $out .= '</option>';
5119
                    $i++;
5120
                }
5121
                $out .= "</select>";
5122
                $out .= ajax_combobox('select' . $htmlname);
5123
            } else {
5124
                if ($status == 0) {
5125
                    $out .= '<span class="opacitymedium">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
5126
                } else {
5127
                    $out .= '<span class="opacitymedium">' . $langs->trans("NoBankAccountFound") . '</span>';
5128
                }
5129
            }
5130
        } else {
5131
            dol_print_error($this->db);
5132
        }
5133
5134
        // Output or return
5135
        if (empty($nooutput)) {
5136
            print $out;
5137
        } else {
5138
            return $out;
5139
        }
5140
5141
        return $num;
5142
    }
5143
5144
    /**
5145
     * Return a HTML select list of establishment
5146
     *
5147
     * @param   string  $selected       Id establishment preselected
5148
     * @param   string  $htmlname       Name of select zone
5149
     * @param   int     $status         Status of searched establishment (0=open, 1=closed, 2=both)
5150
     * @param   string  $filtre         To filter list. This parameter must not come from input of users
5151
     * @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.
5152
     * @param   string  $moreattrib     To add more attribute on select
5153
     * @return  int                     Return integer <0 if error, Num of establishment found if OK (0, 1, 2, ...)
5154
     */
5155
    public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
5156
    {
5157
        global $langs;
5158
5159
        $langs->load("admin");
5160
        $num = 0;
5161
5162
        $sql = "SELECT rowid, name, fk_country, status, entity";
5163
        $sql .= " FROM " . $this->db->prefix() . "establishment";
5164
        $sql .= " WHERE 1=1";
5165
        if ($status != 2) {
5166
            $sql .= " AND status = " . (int) $status;
5167
        }
5168
        if ($filtre) {  // TODO Support USF
5169
            $sql .= " AND " . $filtre;
5170
        }
5171
        $sql .= " ORDER BY name";
5172
5173
        dol_syslog(get_only_class($this) . "::select_establishment", LOG_DEBUG);
5174
        $result = $this->db->query($sql);
5175
        if ($result) {
5176
            $num = $this->db->num_rows($result);
5177
            $i = 0;
5178
            if ($num) {
5179
                print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5180
                if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5181
                    print '<option value="-1">&nbsp;</option>';
5182
                }
5183
5184
                while ($i < $num) {
5185
                    $obj = $this->db->fetch_object($result);
5186
                    if ($selected == $obj->rowid) {
5187
                        print '<option value="' . $obj->rowid . '" selected>';
5188
                    } else {
5189
                        print '<option value="' . $obj->rowid . '">';
5190
                    }
5191
                    print trim($obj->name);
5192
                    if ($status == 2 && $obj->status == 1) {
5193
                        print ' (' . $langs->trans("Closed") . ')';
5194
                    }
5195
                    print '</option>';
5196
                    $i++;
5197
                }
5198
                print "</select>";
5199
            } else {
5200
                if ($status == 0) {
5201
                    print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
5202
                } else {
5203
                    print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
5204
                }
5205
            }
5206
5207
            return $num;
5208
        } else {
5209
            dol_print_error($this->db);
5210
            return -1;
5211
        }
5212
    }
5213
5214
    /**
5215
     * Display form to select bank account
5216
     *
5217
     * @param string    $page       Page
5218
     * @param string    $selected   Id of bank account
5219
     * @param string    $htmlname   Name of select html field
5220
     * @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.
5221
     * @return                      void
5222
     */
5223
    public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
5224
    {
5225
        global $langs;
5226
        if ($htmlname != "none") {
5227
            print '<form method="POST" action="' . $page . '">';
5228
            print '<input type="hidden" name="action" value="setbankaccount">';
5229
            print '<input type="hidden" name="token" value="' . newToken() . '">';
5230
            print img_picto('', 'bank_account', 'class="pictofixedwidth"');
5231
            $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
5232
            if ($nbaccountfound > 0) {
5233
                print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5234
            }
5235
            print '</form>';
5236
        } else {
5237
            $langs->load('banks');
5238
5239
            if ($selected) {
5240
                $bankstatic = new Account($this->db);
5241
                $result = $bankstatic->fetch($selected);
5242
                if ($result) {
5243
                    print $bankstatic->getNomUrl(1);
5244
                }
5245
            } else {
5246
                print "&nbsp;";
5247
            }
5248
        }
5249
    }
5250
5251
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5252
5253
    /**
5254
     * Return list of categories having chosen type
5255
     *
5256
     * @param   string|int          $type           Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
5257
     * @param   string              $selected       Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1.
5258
     * @param   string              $htmlname       HTML field name
5259
     * @param   int                 $maxlength      Maximum length for labels
5260
     * @param   int|string|array    $fromid         Keep only or Exclude (depending on $include parameter) all categories (including the leaf $fromid) into the tree after this id $fromid.
5261
     *                                              $fromid can be an :
5262
     *                                              - int (id of category)
5263
     *                                              - string (categories ids separated by comma)
5264
     *                                              - array (list of categories ids)
5265
     * @param   int<0,3>            $outputmode     0=HTML select string, 1=Array with full label only, 2=Array extended, 3=Array with full picto + label
5266
     * @param   int<0,1>            $include        [=0] Removed or 1=Keep only
5267
     * @param   string              $morecss        More CSS
5268
     * @param     int<0,2>          $useempty       0=No empty value, 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. Default is 1.
5269
     * @return  string|array<int,string>|array<int,array{id:int,fulllabel:string,color:string,picto:string}>|array<int,array{rowid:int,id:int,fk_parent:int,label:string,description:string,color:string,position:string,visible:int,ref_ext:string,picto:string,fullpath:string,fulllabel:string}>     String list or Array of categories
5270
     * @see select_categories()
5271
     */
5272
    public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
5273
    {
5274
		// phpcs:enable
5275
        global $conf, $langs;
5276
        $langs->load("categories");
5277
5278
        // For backward compatibility
5279
        if (is_numeric($type)) {
5280
            dol_syslog(__METHOD__ . ': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
5281
        }
5282
5283
        if ($type === Categorie::TYPE_BANK_LINE) {
5284
            // TODO Move this into common category feature
5285
            $cate_arbo = array();
5286
            $sql = "SELECT c.label, c.rowid";
5287
            $sql .= " FROM " . $this->db->prefix() . "bank_categ as c";
5288
            $sql .= " WHERE entity = " . $conf->entity;
5289
            $sql .= " ORDER BY c.label";
5290
            $result = $this->db->query($sql);
5291
            if ($result) {
5292
                $num = $this->db->num_rows($result);
5293
                $i = 0;
5294
                while ($i < $num) {
5295
                    $objp = $this->db->fetch_object($result);
5296
                    if ($objp) {
5297
                        $cate_arbo[$objp->rowid] = array('id' => $objp->rowid, 'fulllabel' => $objp->label, 'color' => '', 'picto' => 'category');
5298
                    }
5299
                    $i++;
5300
                }
5301
                $this->db->free($result);
5302
            } else {
5303
                dol_print_error($this->db);
5304
            }
5305
        } else {
5306
            $cat = new Categorie($this->db);
5307
            $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
5308
        }
5309
5310
        $outarray = array();
5311
        $outarrayrichhtml = array();
5312
5313
5314
        $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
5315
        if (is_array($cate_arbo)) {
5316
            $num = count($cate_arbo);
5317
5318
            if (!$num) {
5319
                $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
5320
            } else {
5321
                if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5322
                    $output .= '<option value="-1">&nbsp;</option>';
5323
                }
5324
                foreach ($cate_arbo as $key => $value) {
5325
                    if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
5326
                        $add = 'selected ';
5327
                    } else {
5328
                        $add = '';
5329
                    }
5330
5331
                    $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth" style="color: #' . $cate_arbo[$key]['color'] . '"');
5332
                    $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
5333
5334
                    $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
5335
5336
                    $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
5337
5338
                    $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
5339
                    $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
5340
                    $output .= '>';
5341
                    $output .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
5342
                    $output .= '</option>';
5343
5344
                    $cate_arbo[$key]['data-html'] = $labeltoshow;
5345
                }
5346
            }
5347
        }
5348
        $output .= '</select>';
5349
        $output .= "\n";
5350
5351
        if ($outputmode == 2) {
5352
            // TODO: handle error when $cate_arbo is not an array
5353
            return $cate_arbo;
5354
        } elseif ($outputmode == 1) {
5355
            return $outarray;
5356
        } elseif ($outputmode == 3) {
5357
            return $outarrayrichhtml;
5358
        }
5359
        return $output;
5360
    }
5361
5362
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5363
5364
    /**
5365
     *     Show a confirmation HTML form or AJAX popup
5366
     *
5367
     * @param string $page Url of page to call if confirmation is OK
5368
     * @param string $title Title
5369
     * @param string $question Question
5370
     * @param string $action Action
5371
     * @param array{text:string}|array<array{label:string,type:string,size:string,morecss:string,moreattr:string,style:string}> $formquestion   An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>'autofocus' or 'style=...'))
5372
     * @param string $selectedchoice "" or "no" or "yes"
5373
     * @param int|string $useajax 0=No, 1=Yes use Ajax to show the popup, 2=Yes and also submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx
5374
     * @param int $height Force height of box
5375
     * @param int $width Force width of box
5376
     * @return    void
5377
     * @deprecated
5378
     * @see formconfirm()
5379
     */
5380
    public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
5381
    {
5382
		// phpcs:enable
5383
        dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
5384
        print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
5385
    }
5386
5387
    /**
5388
     *     Show a confirmation HTML form or AJAX popup.
5389
     *     Easiest way to use this is with useajax=1.
5390
     *     If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters)
5391
     *     just after calling this method. For example:
5392
     *       print '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
5393
     *       print 'jQuery(document).ready(function() {'."\n";
5394
     *       print 'jQuery(".xxxlink").click(function(e) { jQuery("#aparamid").val(jQuery(this).attr("rel")); jQuery("#dialog-confirm-xxx").dialog("open"); return false; });'."\n";
5395
     *       print '});'."\n";
5396
     *       print '</script>'."\n";
5397
     *
5398
     * @param string        $page               Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformatted)
5399
     * @param string        $title              Title
5400
     * @param string        $question           Question
5401
     * @param string        $action             Action
5402
     * @param array<array{name:string,value:string,values:string[],default:string,label:string,type:string,size:string,morecss:string,moreattr:string,style:string,inputko?:int<0,1>}>|string|null  $formquestion       An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>'autofocus' or 'style=...'))
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array{name:string,...:int<0,1>}>|string|null at position 48 could not be parsed: Expected '}' at position 48, but found 'int'.
Loading history...
5403
     *                                                                                                                                                                                                                  'type' can be 'text', 'password', 'checkbox', 'radio', 'date', 'datetime', 'select', 'multiselect', 'morecss',
5404
     *                                                                                                                                                                                                                  'other', 'onecolumn' or 'hidden'...
5405
     * @param int<0,1>|''|'no'|'yes'|'1'|'0'    $selectedchoice     '' or 'no', or 'yes' or '1', 1, '0' or 0
5406
     * @param int<0,2>|string   $useajax            0=No, 1=Yes use Ajax to show the popup, 2=Yes and also submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx
5407
     * @param int|string    $height             Force height of box (0 = auto)
5408
     * @param int           $width              Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones.
5409
     * @param int           $disableformtag     1=Disable form tag. Can be used if we are already inside a <form> section.
5410
     * @param string        $labelbuttonyes     Label for Yes
5411
     * @param string        $labelbuttonno      Label for No
5412
     * @return string                           HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form
5413
     */
5414
    public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No')
5415
    {
5416
        global $langs, $conf;
5417
5418
        $more = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
5419
        $formconfirm = '';
5420
        $inputok = array();
5421
        $inputko = array();
5422
5423
        // Clean parameters
5424
        $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
5425
        if ($conf->browser->layout == 'phone') {
5426
            $width = '95%';
5427
        }
5428
5429
        // Set height automatically if not defined
5430
        if (empty($height)) {
5431
            $height = 220;
5432
            if (is_array($formquestion) && count($formquestion) > 2) {
5433
                $height += ((count($formquestion) - 2) * 24);
5434
            }
5435
        }
5436
5437
        if (is_array($formquestion) && !empty($formquestion)) {
5438
            // First add hidden fields and value
5439
            foreach ($formquestion as $key => $input) {
5440
                if (is_array($input) && !empty($input)) {
5441
                    if ($input['type'] == 'hidden') {
5442
                        $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
5443
                        $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
5444
5445
                        $more .= '<input type="hidden" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . dol_escape_htmltag($input['value']) . '" class="' . $morecss . '"' . $moreattr . '>' . "\n";
5446
                    }
5447
                }
5448
            }
5449
5450
            // Now add questions
5451
            $moreonecolumn = '';
5452
            $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
5453
            foreach ($formquestion as $key => $input) {
5454
                if (is_array($input) && !empty($input)) {
5455
                    $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : '');    // deprecated. Use morecss instead.
5456
                    $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
5457
                    $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
5458
5459
                    if ($input['type'] == 'text') {
5460
                        $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div><div class="tagtd"><input type="text" class="flat' . $morecss . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $size . ' value="' . (empty($input['value']) ? '' : $input['value']) . '"' . $moreattr . ' /></div></div>' . "\n";
5461
                    } elseif ($input['type'] == 'password') {
5462
                        $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div><div class="tagtd"><input type="password" class="flat' . $morecss . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $size . ' value="' . (empty($input['value']) ? '' : $input['value']) . '"' . $moreattr . ' /></div></div>' . "\n";
5463
                    } elseif ($input['type'] == 'textarea') {
5464
                        /*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
5465
                        $more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
5466
                        $more .= $input['value'];
5467
                        $more .= '</textarea>';
5468
                        $more .= '</div></div>'."\n";*/
5469
                        $moreonecolumn .= '<div class="margintoponly">';
5470
                        $moreonecolumn .= $input['label'] . '<br>';
5471
                        $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
5472
                        $moreonecolumn .= $input['value'];
5473
                        $moreonecolumn .= '</textarea>';
5474
                        $moreonecolumn .= '</div>';
5475
                    } elseif (in_array($input['type'], ['select', 'multiselect'])) {
5476
                        if (empty($morecss)) {
5477
                            $morecss = 'minwidth100';
5478
                        }
5479
5480
                        $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
5481
                        $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
5482
                        $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
5483
                        $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
5484
                        $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
5485
                        $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
5486
                        $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
5487
5488
                        $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
5489
                        if (!empty($input['label'])) {
5490
                            $more .= $input['label'] . '</div><div class="tagtd left">';
5491
                        }
5492
                        if ($input['type'] == 'select') {
5493
                            $more .= $this->selectarray($input['name'], $input['values'], isset($input['default']) ? $input['default'] : '-1', $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
5494
                        } else {
5495
                            $more .= $this->multiselectarray($input['name'], $input['values'], is_array($input['default']) ? $input['default'] : [$input['default']], $key_in_label, $value_as_key, $morecss, $translate, $maxlen, $moreattr);
5496
                        }
5497
                        $more .= '</div></div>' . "\n";
5498
                    } elseif ($input['type'] == 'checkbox') {
5499
                        $more .= '<div class="tagtr">';
5500
                        $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
5501
                        $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
5502
                        if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
5503
                            $more .= ' checked';
5504
                        }
5505
                        if (is_bool($input['value']) && $input['value']) {
5506
                            $more .= ' checked';
5507
                        }
5508
                        if (isset($input['disabled'])) {
5509
                            $more .= ' disabled';
5510
                        }
5511
                        $more .= ' /></div>';
5512
                        $more .= '</div>' . "\n";
5513
                    } elseif ($input['type'] == 'radio') {
5514
                        $i = 0;
5515
                        foreach ($input['values'] as $selkey => $selval) {
5516
                            $more .= '<div class="tagtr">';
5517
                            if (isset($input['label'])) {
5518
                                if ($i == 0) {
5519
                                    $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
5520
                                } else {
5521
                                    $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
5522
                                }
5523
                            }
5524
                            $more .= '<div class="tagtd' . ($i == 0 ? ' tdtop' : '') . '"><input type="radio" class="flat' . $morecss . '" id="' . dol_escape_htmltag($input['name'] . $selkey) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . $selkey . '"' . $moreattr;
5525
                            if (!empty($input['disabled'])) {
5526
                                $more .= ' disabled';
5527
                            }
5528
                            if (isset($input['default']) && $input['default'] === $selkey) {
5529
                                $more .= ' checked="checked"';
5530
                            }
5531
                            $more .= ' /> ';
5532
                            $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
5533
                            $more .= '</div></div>' . "\n";
5534
                            $i++;
5535
                        }
5536
                    } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
5537
                        $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
5538
                        $more .= '<div class="tagtd">';
5539
                        $addnowlink = (empty($input['datenow']) ? 0 : 1);
5540
                        $h = $m = 0;
5541
                        if ($input['type'] == 'datetime') {
5542
                            $h = isset($input['hours']) ? $input['hours'] : 1;
5543
                            $m = isset($input['minutes']) ? $input['minutes'] : 1;
5544
                        }
5545
                        $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
5546
                        $more .= '</div></div>' . "\n";
5547
                        $formquestion[] = array('name' => $input['name'] . 'day');
5548
                        $formquestion[] = array('name' => $input['name'] . 'month');
5549
                        $formquestion[] = array('name' => $input['name'] . 'year');
5550
                        $formquestion[] = array('name' => $input['name'] . 'hour');
5551
                        $formquestion[] = array('name' => $input['name'] . 'min');
5552
                    } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
5553
                        $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
5554
                        if (!empty($input['label'])) {
5555
                            $more .= $input['label'] . '</div><div class="tagtd">';
5556
                        }
5557
                        $more .= $input['value'];
5558
                        $more .= '</div></div>' . "\n";
5559
                    } elseif ($input['type'] == 'onecolumn') {
5560
                        $moreonecolumn .= '<div class="margintoponly">';
5561
                        $moreonecolumn .= $input['value'];
5562
                        $moreonecolumn .= '</div>' . "\n";
5563
                    } elseif ($input['type'] == 'hidden') {
5564
                        // Do nothing more, already added by a previous loop
5565
                    } elseif ($input['type'] == 'separator') {
5566
                        $more .= '<br>';
5567
                    } else {
5568
                        $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
5569
                    }
5570
                }
5571
            }
5572
            $more .= '</div>' . "\n";
5573
            $more .= $moreonecolumn;
5574
        }
5575
5576
        // JQUERY method dialog is broken with smartphone, we use standard HTML.
5577
        // 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
5578
        // See page product/card.php for example
5579
        if (!empty($conf->dol_use_jmobile)) {
5580
            $useajax = 0;
5581
        }
5582
        if (empty($conf->use_javascript_ajax)) {
5583
            $useajax = 0;
5584
        }
5585
5586
        if ($useajax) {
5587
            $autoOpen = true;
5588
            $dialogconfirm = 'dialog-confirm';
5589
            $button = '';
5590
            if (!is_numeric($useajax)) {
5591
                $button = $useajax;
5592
                $useajax = 1;
5593
                $autoOpen = false;
5594
                $dialogconfirm .= '-' . $button;
5595
            }
5596
            $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
5597
            $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
5598
5599
            // Add input fields into list of fields to read during submit (inputok and inputko)
5600
            if (is_array($formquestion)) {
5601
                foreach ($formquestion as $key => $input) {
5602
                    //print "xx ".$key." rr ".is_array($input)."<br>\n";
5603
                    // Add name of fields to propagate with the GET when submitting the form with button OK.
5604
                    if (is_array($input) && isset($input['name'])) {
5605
                        if (strpos($input['name'], ',') > 0) {
5606
                            $inputok = array_merge($inputok, explode(',', $input['name']));
5607
                        } else {
5608
                            array_push($inputok, $input['name']);
5609
                        }
5610
                    }
5611
                    // Add name of fields to propagate with the GET when submitting the form with button KO.
5612
                    // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
5613
                    if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
5614
                        array_push($inputko, $input['name']);
5615
                    }
5616
                }
5617
            }
5618
5619
            // Show JQuery confirm box.
5620
            $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
5621
            if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
5622
                $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
5623
            }
5624
            if (!empty($more)) {
5625
                $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
5626
            }
5627
            $formconfirm .= ($question ? '<div class="confirmmessage">' . img_help(0, '') . ' ' . $question . '</div>' : '');
5628
            $formconfirm .= '</div>' . "\n";
5629
5630
            $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
5631
            $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
5632
            $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5633
            $formconfirm .= 'jQuery(document).ready(function() {
5634
            $(function() {
5635
            	$( "#' . $dialogconfirm . '" ).dialog(
5636
            	{
5637
                    autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
5638
            if ($newselectedchoice == 'no') {
5639
                $formconfirm .= '
5640
						open: function() {
5641
            				$(this).parent().find("button.ui-button:eq(2)").focus();
5642
						},';
5643
            }
5644
5645
            $jsforcursor = '';
5646
            if ($useajax == 1) {
5647
                $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
5648
                $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
5649
            }
5650
5651
            $postconfirmas = 'GET';
5652
5653
            $formconfirm .= '
5654
                    resizable: false,
5655
                    height: "' . $height . '",
5656
                    width: "' . $width . '",
5657
                    modal: true,
5658
                    closeOnEscape: false,
5659
                    buttons: {
5660
                        "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
5661
							var options = "token=' . urlencode(newToken()) . '";
5662
                        	var inputok = ' . json_encode($inputok) . ';	/* List of fields into form */
5663
							var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
5664
                         	var pageyes = "' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '";
5665
5666
                         	if (inputok.length > 0) {
5667
                         		$.each(inputok, function(i, inputname) {
5668
                         			var more = "";
5669
									var inputvalue;
5670
                         			if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5671
										inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5672
									} else {
5673
                         		    	if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5674
                         				inputvalue = $("#" + inputname + more).val();
5675
									}
5676
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
5677
									console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5678
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5679
                         		});
5680
                         	}
5681
                         	var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
5682
            				if (pageyes.length > 0) {';
5683
            if ($postconfirmas == 'GET') {
5684
                $formconfirm .= 'location.href = urljump;';
5685
            } else {
5686
                $formconfirm .= $jsforcursor;
5687
                $formconfirm .= 'var post = $.post(
5688
									pageyes,
5689
									options,
5690
									function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5691
								);';
5692
            }
5693
            $formconfirm .= '
5694
								console.log("after post ok");
5695
							}
5696
	                        $(this).dialog("close");
5697
                        },
5698
                        "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
5699
                        	var options = "token=' . urlencode(newToken()) . '";
5700
                         	var inputko = ' . json_encode($inputko) . ';	/* List of fields into form */
5701
							var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
5702
                         	var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
5703
                         	if (inputko.length > 0) {
5704
                         		$.each(inputko, function(i, inputname) {
5705
                         			var more = "";
5706
                         			if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5707
                         			var inputvalue = $("#" + inputname + more).val();
5708
                         			if (typeof inputvalue == "undefined") { inputvalue=""; }
5709
                         			options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5710
                         		});
5711
                         	}
5712
                         	var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
5713
                         	//alert(urljump);
5714
            				if (pageno.length > 0) {';
5715
            if ($postconfirmas == 'GET') {
5716
                $formconfirm .= 'location.href = urljump;';
5717
            } else {
5718
                $formconfirm .= $jsforcursor;
5719
                $formconfirm .= 'var post = $.post(
5720
									pageno,
5721
									options,
5722
									function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5723
								);';
5724
            }
5725
            $formconfirm .= '
5726
								console.log("after post ko");
5727
							}
5728
                            $(this).dialog("close");
5729
                        }
5730
                    }
5731
                }
5732
                );
5733
5734
            	var button = "' . $button . '";
5735
            	if (button.length > 0) {
5736
                	$( "#" + button ).click(function() {
5737
                		$("#' . $dialogconfirm . '").dialog("open");
5738
        			});
5739
                }
5740
            });
5741
            });
5742
            </script>';
5743
            $formconfirm .= "<!-- end ajax formconfirm -->\n";
5744
        } else {
5745
            $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
5746
5747
            if (empty($disableformtag)) {
5748
                $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftroright">' . "\n";
5749
            }
5750
5751
            $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
5752
            $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
5753
5754
            $formconfirm .= '<table class="valid centpercent">' . "\n";
5755
5756
            // Line title
5757
            $formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5758
            $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
5759
            $formconfirm .= '</td></tr>' . "\n";
5760
5761
            // Line text
5762
            if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
5763
                $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">' . $formquestion['text'] . '</td></tr>' . "\n";
5764
            }
5765
5766
            // Line form fields
5767
            if ($more) {
5768
                $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">' . "\n";
5769
                $formconfirm .= $more;
5770
                $formconfirm .= '</td></tr>' . "\n";
5771
            }
5772
5773
            // Line with question
5774
            $formconfirm .= '<tr class="valid">';
5775
            $formconfirm .= '<td class="valid">' . $question . '</td>';
5776
            $formconfirm .= '<td class="valid center">';
5777
            $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
5778
            $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
5779
            $formconfirm .= '</td>';
5780
            $formconfirm .= '</tr>' . "\n";
5781
5782
            $formconfirm .= '</table>' . "\n";
5783
5784
            if (empty($disableformtag)) {
5785
                $formconfirm .= "</form>\n";
5786
            }
5787
            $formconfirm .= '<br>';
5788
5789
            if (!empty($conf->use_javascript_ajax)) {
5790
                $formconfirm .= '<!-- code to disable button to avoid double clic -->';
5791
                $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
5792
                $formconfirm .= '
5793
				$(document).ready(function () {
5794
					$(".confirmvalidatebutton").on("click", function() {
5795
						console.log("We click on button confirmvalidatebutton");
5796
						$(this).attr("disabled", "disabled");
5797
						setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5798
						//console.log($(this).closest("form"));
5799
						$(this).closest("form").submit();
5800
					});
5801
				});
5802
				';
5803
                $formconfirm .= '</script>' . "\n";
5804
            }
5805
5806
            $formconfirm .= "<!-- end formconfirm -->\n";
5807
        }
5808
5809
        return $formconfirm;
5810
    }
5811
5812
5813
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5814
5815
    /**
5816
     * Show a form to select a project
5817
     *
5818
     * @param   int         $page               Page
5819
     * @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)
5820
     * @param   string      $selected           Id preselected project
5821
     * @param   string      $htmlname           Name of select field
5822
     * @param   int         $discard_closed     Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable)
5823
     * @param   int         $maxlength          Max length
5824
     * @param   int         $forcefocus         Force focus on field (works with javascript only)
5825
     * @param   int         $nooutput           No print is done. String is returned.
5826
     * @param   string      $textifnoproject    Text to show if no project
5827
     * @param   string      $morecss            More CSS
5828
     * @return  string                          Return html content
5829
     */
5830
    public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '')
5831
    {
5832
		// phpcs:enable
5833
        global $langs;
5834
5835
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/project.lib.php';
5836
5837
        $out = '';
5838
5839
        $formproject = new FormProjets($this->db);
5840
5841
        $langs->load("project");
5842
        if ($htmlname != "none") {
5843
            $out .= '<form method="post" action="' . $page . '">';
5844
            $out .= '<input type="hidden" name="action" value="classin">';
5845
            $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5846
            $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
5847
            $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5848
            $out .= '</form>';
5849
        } else {
5850
            $out .= '<span class="project_head_block">';
5851
            if ($selected) {
5852
                $projet = new Project($this->db);
5853
                $projet->fetch($selected);
5854
                $out .= $projet->getNomUrl(0, '', 1);
5855
            } else {
5856
                $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
5857
            }
5858
            $out .= '</span>';
5859
        }
5860
5861
        if (empty($nooutput)) {
5862
            print $out;
5863
            return '';
5864
        }
5865
        return $out;
5866
    }
5867
5868
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5869
5870
    /**
5871
     * Show a form to select payment conditions
5872
     *
5873
     * @param int       $page               Page
5874
     * @param string    $selected           Id condition pre-selectionne
5875
     * @param string    $htmlname           Name of select html field
5876
     * @param int       $addempty           Add empty entry
5877
     * @param string    $type               Type ('direct-debit' or 'bank-transfer')
5878
     * @param int       $filtertype         If > 0, include payment terms with deposit percentage (for objects other than invoices and invoice templates)
5879
     * @param int       $deposit_percent    < 0 : deposit_percent input makes no sense (for example, in list filters)
5880
     *                                      0 : use default deposit percentage from entry
5881
     *                                      > 0 : force deposit percentage (for example, from company object)
5882
     * @param int       $nooutput           No print is done. String is returned.
5883
     * @return string                       HTML output or ''
5884
     */
5885
    public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
5886
    {
5887
		// phpcs:enable
5888
        global $langs;
5889
5890
        $out = '';
5891
5892
        if ($htmlname != "none") {
5893
            $out .= '<form method="POST" action="' . $page . '">';
5894
            $out .= '<input type="hidden" name="action" value="setconditions">';
5895
            $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5896
            if ($type) {
5897
                $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
5898
            }
5899
            $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
5900
            $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5901
            $out .= '</form>';
5902
        } else {
5903
            if ($selected) {
5904
                $this->load_cache_conditions_paiements();
5905
                if (isset($this->cache_conditions_paiements[$selected])) {
5906
                    $label = $this->cache_conditions_paiements[$selected]['label'];
5907
5908
                    if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
5909
                        $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
5910
                    }
5911
5912
                    $out .= $label;
5913
                } else {
5914
                    $langs->load('errors');
5915
                    $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
5916
                }
5917
            } else {
5918
                $out .= '&nbsp;';
5919
            }
5920
        }
5921
5922
        if (empty($nooutput)) {
5923
            print $out;
5924
            return '';
5925
        }
5926
        return $out;
5927
    }
5928
5929
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5930
5931
    /**
5932
     *  Show a form to select a delivery delay
5933
     *
5934
     * @param   int         $page       Page
5935
     * @param   string      $selected   Id condition pre-selectionne
5936
     * @param   string      $htmlname   Name of select html field
5937
     * @param   int         $addempty   Add an empty entry
5938
     * @return  void
5939
     */
5940
    public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5941
    {
5942
		// phpcs:enable
5943
        global $langs;
5944
        if ($htmlname != "none") {
5945
            print '<form method="post" action="' . $page . '">';
5946
            print '<input type="hidden" name="action" value="setavailability">';
5947
            print '<input type="hidden" name="token" value="' . newToken() . '">';
5948
            $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5949
            print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5950
            print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
5951
            print '</form>';
5952
        } else {
5953
            if ($selected) {
5954
                $this->load_cache_availability();
5955
                print $this->cache_availability[$selected]['label'];
5956
            } else {
5957
                print "&nbsp;";
5958
            }
5959
        }
5960
    }
5961
5962
    /**
5963
     *  Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...)
5964
     *  List found into table c_input_reason loaded by loadCacheInputReason
5965
     *
5966
     * @param string $page Page
5967
     * @param string $selected Id condition pre-selectionne
5968
     * @param string $htmlname Name of select html field
5969
     * @param int $addempty Add empty entry
5970
     * @return    void
5971
     */
5972
    public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5973
    {
5974
        global $langs;
5975
        if ($htmlname != "none") {
5976
            print '<form method="post" action="' . $page . '">';
5977
            print '<input type="hidden" name="action" value="setdemandreason">';
5978
            print '<input type="hidden" name="token" value="' . newToken() . '">';
5979
            $this->selectInputReason($selected, $htmlname, -1, $addempty);
5980
            print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5981
            print '</form>';
5982
        } else {
5983
            if ($selected) {
5984
                $this->loadCacheInputReason();
5985
                foreach ($this->cache_demand_reason as $key => $val) {
5986
                    if ($val['id'] == $selected) {
5987
                        print $val['label'];
5988
                        break;
5989
                    }
5990
                }
5991
            } else {
5992
                print "&nbsp;";
5993
            }
5994
        }
5995
    }
5996
5997
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5998
5999
    /**
6000
     *    Show a form + html select a date
6001
     *
6002
     * @param string $page Page
6003
     * @param string $selected Date preselected
6004
     * @param string $htmlname Html name of date input fields or 'none'
6005
     * @param int $displayhour Display hour selector
6006
     * @param int $displaymin Display minutes selector
6007
     * @param int $nooutput 1=No print output, return string
6008
     * @param string $type 'direct-debit' or 'bank-transfer'
6009
     * @return    string
6010
     * @see        selectDate()
6011
     */
6012
    public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
6013
    {
6014
		// phpcs:enable
6015
        global $langs;
6016
6017
        $ret = '';
6018
6019
        if ($htmlname != "none") {
6020
            $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
6021
            $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
6022
            $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
6023
            if ($type) {
6024
                $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
6025
            }
6026
            $ret .= '<table class="nobordernopadding">';
6027
            $ret .= '<tr><td>';
6028
            $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
6029
            $ret .= '</td>';
6030
            $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
6031
            $ret .= '</tr></table></form>';
6032
        } else {
6033
            if ($displayhour) {
6034
                $ret .= dol_print_date($selected, 'dayhour');
6035
            } else {
6036
                $ret .= dol_print_date($selected, 'day');
6037
            }
6038
        }
6039
6040
        if (empty($nooutput)) {
6041
            print $ret;
6042
        }
6043
        return $ret;
6044
    }
6045
6046
6047
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6048
6049
    /**
6050
     *  Show a select form to choose a user
6051
     *
6052
     * @param string $page Page
6053
     * @param string $selected Id of user preselected
6054
     * @param string $htmlname Name of input html field. If 'none', we just output the user link.
6055
     * @param int[] $exclude List of users id to exclude
6056
     * @param int[] $include List of users id to include
6057
     * @return    void
6058
     */
6059
    public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
6060
    {
6061
		// phpcs:enable
6062
        global $langs;
6063
6064
        if ($htmlname != "none") {
6065
            print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
6066
            print '<input type="hidden" name="action" value="set' . $htmlname . '">';
6067
            print '<input type="hidden" name="token" value="' . newToken() . '">';
6068
            print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
6069
            print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6070
            print '</form>';
6071
        } else {
6072
            if ($selected) {
6073
                $theuser = new User($this->db);
6074
                $theuser->fetch($selected);
6075
                print $theuser->getNomUrl(1);
6076
            } else {
6077
                print "&nbsp;";
6078
            }
6079
        }
6080
    }
6081
6082
6083
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6084
6085
    /**
6086
     *    Show form with payment mode
6087
     *
6088
     * @param string $page Page
6089
     * @param string $selected Id mode pre-selectionne
6090
     * @param string $htmlname Name of select html field
6091
     * @param string $filtertype To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz))
6092
     * @param int $active Active or not, -1 = all
6093
     * @param int $addempty 1=Add empty entry
6094
     * @param string $type Type ('direct-debit' or 'bank-transfer')
6095
     * @param int $nooutput 1=Return string, no output
6096
     * @return    string                    HTML output or ''
6097
     */
6098
    public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
6099
    {
6100
		// phpcs:enable
6101
        global $langs;
6102
6103
        $out = '';
6104
        if ($htmlname != "none") {
6105
            $out .= '<form method="POST" action="' . $page . '">';
6106
            $out .= '<input type="hidden" name="action" value="setmode">';
6107
            $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
6108
            if ($type) {
6109
                $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
6110
            }
6111
            $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
6112
            $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6113
            $out .= '</form>';
6114
        } else {
6115
            if ($selected) {
6116
                $this->load_cache_types_paiements();
6117
                $out .= $this->cache_types_paiements[$selected]['label'];
6118
            } else {
6119
                $out .= "&nbsp;";
6120
            }
6121
        }
6122
6123
        if ($nooutput) {
6124
            return $out;
6125
        } else {
6126
            print $out;
6127
        }
6128
        return '';
6129
    }
6130
6131
    /**
6132
     *    Show form with transport mode
6133
     *
6134
     * @param string $page Page
6135
     * @param string $selected Id mode pre-select
6136
     * @param string $htmlname Name of select html field
6137
     * @param int $active Active or not, -1 = all
6138
     * @param int $addempty 1=Add empty entry
6139
     * @return    void
6140
     */
6141
    public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
6142
    {
6143
        global $langs;
6144
        if ($htmlname != "none") {
6145
            print '<form method="POST" action="' . $page . '">';
6146
            print '<input type="hidden" name="action" value="settransportmode">';
6147
            print '<input type="hidden" name="token" value="' . newToken() . '">';
6148
            $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
6149
            print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6150
            print '</form>';
6151
        } else {
6152
            if ($selected) {
6153
                $this->load_cache_transport_mode();
6154
                print $this->cache_transport_mode[$selected]['label'];
6155
            } else {
6156
                print "&nbsp;";
6157
            }
6158
        }
6159
    }
6160
6161
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6162
6163
    /**
6164
     *    Show form with multicurrency code
6165
     *
6166
     * @param string $page Page
6167
     * @param string $selected code pre-selectionne
6168
     * @param string $htmlname Name of select html field
6169
     * @return    void
6170
     */
6171
    public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
6172
    {
6173
		// phpcs:enable
6174
        global $langs;
6175
        if ($htmlname != "none") {
6176
            print '<form method="POST" action="' . $page . '">';
6177
            print '<input type="hidden" name="action" value="setmulticurrencycode">';
6178
            print '<input type="hidden" name="token" value="' . newToken() . '">';
6179
            print $this->selectMultiCurrency($selected, $htmlname, 0);
6180
            print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6181
            print '</form>';
6182
        } else {
6183
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/company.lib.php';
6184
            print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
6185
        }
6186
    }
6187
6188
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6189
6190
    /**
6191
     *    Show form with multicurrency rate
6192
     *
6193
     * @param string $page Page
6194
     * @param double $rate Current rate
6195
     * @param string $htmlname Name of select html field
6196
     * @param string $currency Currency code to explain the rate
6197
     * @return    void
6198
     */
6199
    public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '')
6200
    {
6201
		// phpcs:enable
6202
        global $langs, $mysoc, $conf;
6203
6204
        if ($htmlname != "none") {
6205
            print '<form method="POST" action="' . $page . '">';
6206
            print '<input type="hidden" name="action" value="setmulticurrencyrate">';
6207
            print '<input type="hidden" name="token" value="' . newToken() . '">';
6208
            print '<input type="text" class="maxwidth100" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" /> ';
6209
            print '<select name="calculation_mode">';
6210
            print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
6211
            print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
6212
            print '</select> ';
6213
            print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6214
            print '</form>';
6215
        } else {
6216
            if (!empty($rate)) {
6217
                print price($rate, 1, $langs, 0, 0);
6218
                if ($currency && $rate != 1) {
6219
                    print ' &nbsp; (' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')';
6220
                }
6221
            } else {
6222
                print 1;
6223
            }
6224
        }
6225
    }
6226
6227
6228
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6229
6230
    /**
6231
     *    Show a select box with available absolute discounts
6232
     *
6233
     * @param string $page Page URL where form is shown
6234
     * @param int $selected Value preselected
6235
     * @param string $htmlname Name of SELECT component. If 'none', not changeable. Example 'remise_id'.
6236
     * @param int $socid Third party id
6237
     * @param float $amount Total amount available
6238
     * @param string $filter SQL filter on discounts
6239
     * @param int $maxvalue Max value for lines that can be selected
6240
     * @param string $more More string to add
6241
     * @param int $hidelist 1=Hide list
6242
     * @param int $discount_type 0 => customer discount, 1 => supplier discount
6243
     * @return    void
6244
     */
6245
    public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
6246
    {
6247
		// phpcs:enable
6248
        global $conf, $langs;
6249
        if ($htmlname != "none") {
6250
            print '<form method="post" action="' . $page . '">';
6251
            print '<input type="hidden" name="action" value="setabsolutediscount">';
6252
            print '<input type="hidden" name="token" value="' . newToken() . '">';
6253
            print '<div class="inline-block">';
6254
            if (!empty($discount_type)) {
6255
                if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
6256
                    if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
6257
                        $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
6258
                    } else {
6259
                        $translationKey = 'HasCreditNoteFromSupplier';
6260
                    }
6261
                } else {
6262
                    if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
6263
                        $translationKey = 'HasAbsoluteDiscountFromSupplier';
6264
                    } else {
6265
                        $translationKey = 'HasCreditNoteFromSupplier';
6266
                    }
6267
                }
6268
            } else {
6269
                if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
6270
                    if (!$filter || $filter == "fk_facture_source IS NULL") {
6271
                        $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
6272
                    } else {
6273
                        $translationKey = 'CompanyHasCreditNote';
6274
                    }
6275
                } else {
6276
                    if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
6277
                        $translationKey = 'CompanyHasAbsoluteDiscount';
6278
                    } else {
6279
                        $translationKey = 'CompanyHasCreditNote';
6280
                    }
6281
                }
6282
            }
6283
            print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
6284
            if (empty($hidelist)) {
6285
                print ' ';
6286
            }
6287
            print '</div>';
6288
            if (empty($hidelist)) {
6289
                print '<div class="inline-block" style="padding-right: 10px">';
6290
                $newfilter = 'discount_type=' . intval($discount_type);
6291
                if (!empty($discount_type)) {
6292
                    $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
6293
                } else {
6294
                    $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
6295
                }
6296
                if ($filter) {
6297
                    $newfilter .= ' AND (' . $filter . ')';
6298
                }
6299
                // output the combo of discounts
6300
                $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
6301
                if ($nbqualifiedlines > 0) {
6302
                    print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
6303
                    if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
6304
                        print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
6305
                    }
6306
                    if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
6307
                        print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
6308
                    }
6309
6310
                    print '>';
6311
                }
6312
                print '</div>';
6313
            }
6314
            if ($more) {
6315
                print '<div class="inline-block">';
6316
                print $more;
6317
                print '</div>';
6318
            }
6319
            print '</form>';
6320
        } else {
6321
            if ($selected) {
6322
                print $selected;
6323
            } else {
6324
                print "0";
6325
            }
6326
        }
6327
    }
6328
6329
6330
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6331
6332
    /**
6333
     *  Show forms to select a contact
6334
     *
6335
     * @param string    $page       Page
6336
     * @param Societe   $societe    Filter on third party
6337
     * @param string    $selected   Id contact pre-selectionne
6338
     * @param string    $htmlname   Name of HTML select. If 'none', we just show contact link.
6339
     * @return    void
6340
     */
6341
    public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
6342
    {
6343
		// phpcs:enable
6344
        global $langs;
6345
6346
        if ($htmlname != "none") {
6347
            print '<form method="post" action="' . $page . '">';
6348
            print '<input type="hidden" name="action" value="set_contact">';
6349
            print '<input type="hidden" name="token" value="' . newToken() . '">';
6350
            print '<table class="nobordernopadding">';
6351
            print '<tr><td>';
6352
            print $this->selectcontacts($societe->id, $selected, $htmlname);
6353
            $num = $this->num;
6354
            if ($num == 0) {
6355
                $addcontact = (getDolGlobalString('SOCIETE_ADDRESSES_MANAGEMENT') ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
6356
                print '<a href="' . constant('BASE_URL') . '/contact/card.php?socid=' . $societe->id . '&amp;action=create&amp;backtoreferer=1">' . $addcontact . '</a>';
6357
            }
6358
            print '</td>';
6359
            print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
6360
            print '</tr></table></form>';
6361
        } else {
6362
            if ($selected) {
6363
                $contact = new Contact($this->db);
6364
                $contact->fetch($selected);
6365
                print $contact->getFullName($langs);
6366
            } else {
6367
                print "&nbsp;";
6368
            }
6369
        }
6370
    }
6371
6372
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6373
6374
    /**
6375
     *  Output html select to select thirdparty
6376
     *
6377
     * @param string    $page                   Page
6378
     * @param string    $selected               Id preselected
6379
     * @param string    $htmlname               Name of HTML select
6380
     * @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)'). Do not use a filter coming from input of users.
6381
     * @param string|int<0,1>   $showempty      Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty')
6382
     * @param int<0,1>  $showtype               Show third party type in combolist (customer, prospect or supplier)
6383
     * @param int<0,1>  $forcecombo             Force to use combo box
6384
     * @param   array<array{method:string,url:string,htmlname:string,params:array<string,string>}>  $events     Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
6385
     * @param int       $nooutput               No print output. Return it only.
6386
     * @param int[]     $excludeids             Exclude IDs from the select combo
6387
     * @param string    $textifnothirdparty     Text to show if no thirdparty
6388
     * @return    string                        HTML output or ''
6389
     */
6390
    public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
6391
    {
6392
		// phpcs:enable
6393
        global $langs;
6394
6395
        $out = '';
6396
        if ($htmlname != "none") {
6397
            $out .= '<form method="post" action="' . $page . '">';
6398
            $out .= '<input type="hidden" name="action" value="set_thirdparty">';
6399
            $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
6400
            $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
6401
            $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6402
            $out .= '</form>';
6403
        } else {
6404
            if ($selected) {
6405
                $soc = new Societe($this->db);
6406
                $soc->fetch($selected);
6407
                $out .= $soc->getNomUrl(0, '');
6408
            } else {
6409
                $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
6410
            }
6411
        }
6412
6413
        if ($nooutput) {
6414
            return $out;
6415
        } else {
6416
            print $out;
6417
        }
6418
6419
        return '';
6420
    }
6421
6422
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6423
6424
    /**
6425
     *    Retourne la liste des devises, dans la langue de l'utilisateur
6426
     *
6427
     * @param string $selected preselected currency code
6428
     * @param string $htmlname name of HTML select list
6429
     * @deprecated
6430
     * @return    void
6431
     */
6432
    public function select_currency($selected = '', $htmlname = 'currency_id')
6433
    {
6434
		// phpcs:enable
6435
        print $this->selectCurrency($selected, $htmlname);
6436
    }
6437
6438
    /**
6439
     *  Retourne la liste des devises, dans la langue de l'utilisateur
6440
     *
6441
     * @param string $selected preselected currency code
6442
     * @param string $htmlname name of HTML select list
6443
     * @param int    $mode 0 = Add currency symbol into label, 1 = Add 3 letter iso code
6444
     * @param string $useempty '1'=Allow empty value
6445
     * @return    string
6446
     */
6447
    public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
6448
    {
6449
        global $langs, $user;
6450
6451
        $langs->loadCacheCurrencies('');
6452
6453
        $out = '';
6454
6455
        if ($selected == 'euro' || $selected == 'euros') {
6456
            $selected = 'EUR'; // Pour compatibilite
6457
        }
6458
6459
        $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
6460
        if ($useempty) {
6461
            $out .= '<option value="-1" selected></option>';
6462
        }
6463
        foreach ($langs->cache_currencies as $code_iso => $currency) {
6464
            $labeltoshow = $currency['label'];
6465
            if ($mode == 1) {
6466
                $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
6467
            } else {
6468
                $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
6469
            }
6470
6471
            if ($selected && $selected == $code_iso) {
6472
                $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
6473
            } else {
6474
                $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
6475
            }
6476
            $out .= $labeltoshow;
6477
            $out .= '</option>';
6478
        }
6479
        $out .= '</select>';
6480
        if ($user->admin) {
6481
            $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
6482
        }
6483
6484
        // Make select dynamic
6485
        include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
6486
        $out .= ajax_combobox($htmlname);
6487
6488
        return $out;
6489
    }
6490
6491
    /**
6492
     *    Return array of currencies in user language
6493
     *
6494
     * @param string $selected Preselected currency code
6495
     * @param string $htmlname Name of HTML select list
6496
     * @param integer $useempty 1=Add empty line
6497
     * @param string $filter Optional filters criteras (example: 'code <> x', ' in (1,3)')
6498
     * @param bool $excludeConfCurrency false = If company current currency not in table, we add it into list. Should always be available.
6499
     *                                  true = we are in currency_rate update , we don't want to see conf->currency in select
6500
     * @param string $morecss More css
6501
     * @return    string
6502
     */
6503
    public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = '')
6504
    {
6505
        global $conf, $langs;
6506
6507
        $langs->loadCacheCurrencies(''); // Load ->cache_currencies
6508
6509
        $TCurrency = array();
6510
6511
        $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
6512
        $sql .= " WHERE entity IN ('" . getEntity('mutlicurrency') . "')";
6513
        if ($filter) {
6514
            $sql .= " AND " . $filter;
6515
        }
6516
        $resql = $this->db->query($sql);
6517
        if ($resql) {
6518
            while ($obj = $this->db->fetch_object($resql)) {
6519
                $TCurrency[$obj->code] = $obj->code;
6520
            }
6521
        }
6522
6523
        $out = '';
6524
        $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6525
        if ($useempty) {
6526
            $out .= '<option value="">&nbsp;</option>';
6527
        }
6528
        // If company current currency not in table, we add it into list. Should always be available.
6529
        if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
6530
            $TCurrency[$conf->currency] = $conf->currency;
6531
        }
6532
        if (count($TCurrency) > 0) {
6533
            foreach ($langs->cache_currencies as $code_iso => $currency) {
6534
                if (isset($TCurrency[$code_iso])) {
6535
                    if (!empty($selected) && $selected == $code_iso) {
6536
                        $out .= '<option value="' . $code_iso . '" selected="selected">';
6537
                    } else {
6538
                        $out .= '<option value="' . $code_iso . '">';
6539
                    }
6540
6541
                    $out .= $currency['label'];
6542
                    $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
6543
                    $out .= '</option>';
6544
                }
6545
            }
6546
        }
6547
6548
        $out .= '</select>';
6549
6550
        // Make select dynamic
6551
        include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
6552
        $out .= ajax_combobox($htmlname);
6553
6554
        return $out;
6555
    }
6556
6557
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6558
6559
    /**
6560
     *  Load into the cache ->cache_vatrates, all the vat rates of a country
6561
     *
6562
     *  @param  string  $country_code       Country code with quotes ("'CA'", or "'CA,IN,...'")
6563
     *  @return int                         Nb of loaded lines, 0 if already loaded, <0 if KO
6564
     */
6565
    public function load_cache_vatrates($country_code)
6566
    {
6567
		// phpcs:enable
6568
        global $langs, $user;
6569
6570
        $num = count($this->cache_vatrates);
6571
        if ($num > 0) {
6572
            return $num; // Cache already loaded
6573
        }
6574
6575
        dol_syslog(__METHOD__, LOG_DEBUG);
6576
6577
        $sql = "SELECT t.rowid, t.type_vat, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
6578
        $sql .= " FROM " . $this->db->prefix() . "c_tva as t, " . $this->db->prefix() . "c_country as c";
6579
        $sql .= " WHERE t.fk_pays = c.rowid";
6580
        $sql .= " AND t.active > 0";
6581
        $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
6582
        $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
6583
        $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
6584
6585
        $resql = $this->db->query($sql);
6586
        if ($resql) {
6587
            $num = $this->db->num_rows($resql);
6588
            if ($num) {
6589
                for ($i = 0; $i < $num; $i++) {
6590
                    $obj = $this->db->fetch_object($resql);
6591
6592
                    $tmparray = array();
6593
                    $tmparray['rowid']          = $obj->rowid;
6594
                    $tmparray['type_vat']       = $obj->type_vat;
6595
                    $tmparray['code']           = $obj->code;
6596
                    $tmparray['txtva']          = $obj->taux;
6597
                    $tmparray['nprtva']         = $obj->recuperableonly;
6598
                    $tmparray['localtax1']      = $obj->localtax1;
6599
                    $tmparray['localtax1_type'] = $obj->localtax1_type;
6600
                    $tmparray['localtax2']      = $obj->localtax2;
6601
                    $tmparray['localtax2_type'] = $obj->localtax1_type;
6602
                    $tmparray['label']          = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
6603
                    $tmparray['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
6604
                    $positiverates = '';
6605
                    if ($obj->taux) {
6606
                        $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
6607
                    }
6608
                    if ($obj->localtax1) {
6609
                        $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
6610
                    }
6611
                    if ($obj->localtax2) {
6612
                        $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
6613
                    }
6614
                    if (empty($positiverates)) {
6615
                        $positiverates = '0';
6616
                    }
6617
                    $tmparray['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
6618
6619
                    $this->cache_vatrates[$obj->rowid] = $tmparray;
6620
                }
6621
6622
                return $num;
6623
            } else {
6624
                $this->error = '<span class="error">';
6625
                $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
6626
                $reg = array();
6627
                if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
6628
                    $langs->load("errors");
6629
                    $new_country_code = $reg[1];
6630
                    $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_pays', 'code', 'rowid');
6631
                    $this->error .= '<br>' . $langs->trans("ErrorFixThisHere", constant('BASE_URL') . '/admin/dict.php?id=10' . ($country_id > 0 ? '&countryidforinsert=' . $country_id : ''));
6632
                }
6633
                $this->error .= '</span>';
6634
                return -1;
6635
            }
6636
        } else {
6637
            $this->error = '<span class="error">' . $this->db->error() . '</span>';
6638
            return -2;
6639
        }
6640
    }
6641
6642
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6643
6644
    /**
6645
     *  Output an HTML select vat rate.
6646
     *  The name of this function should be selectVat. We keep bad name for compatibility purpose.
6647
     *
6648
     *  @param  string        $htmlname           Name of HTML select field
6649
     *  @param  float|string  $selectedrate       Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing.
6650
     *  @param  Societe       $societe_vendeuse   Thirdparty seller
6651
     *  @param  Societe       $societe_acheteuse  Thirdparty buyer
6652
     *  @param  int           $idprod             Id product. O if unknown of NA.
6653
     *  @param  int           $info_bits          Miscellaneous information on line (1 for NPR)
6654
     *  @param  int|string    $type               ''=Unknown, 0=Product, 1=Service (Used if idprod not defined)
6655
     *                                            If seller not subject to VAT, default VAT=0. End of rule.
6656
     *                                            If (seller country==buyer country), then default VAT=product's VAT. End of rule.
6657
     *                                            If (seller and buyer in EU) and sold product = new means of transportation (car, boat, airplane), default VAT =0 (VAT must be paid by the buyer to his country's tax office and not the seller). End of rule.
6658
     *                                            If (seller and buyer in EU) and buyer=private person, then default VAT=VAT of sold product.  End of rule.
6659
     *                                            If (seller and buyer in EU) and buyer=company then default VAT =0. End of rule.
6660
     *                                            Else, default proposed VAT==0. End of rule.
6661
     *  @param  bool         $options_only        Return HTML options lines only (for ajax treatment)
6662
     *  @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
6663
     *  @param  int          $type_vat            0=All type, 1=VAT rate sale, 2=VAT rate purchase
6664
     *  @return string
6665
     */
6666
    public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = null, $societe_acheteuse = null, $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0, $type_vat = 0)
6667
    {
6668
		// phpcs:enable
6669
        global $langs, $mysoc;
6670
6671
        $langs->load('errors');
6672
6673
        $return = '';
6674
6675
        // Define defaultnpr, defaultttx and defaultcode
6676
        $defaultnpr = ($info_bits & 0x01);
6677
        $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
6678
        $defaulttx = str_replace('*', '', $selectedrate);
6679
        $defaultcode = '';
6680
        $reg = array();
6681
        if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6682
            $defaultcode = $reg[1];
6683
            $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6684
        }
6685
        //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
6686
6687
        // Check parameters
6688
        if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
6689
            if ($societe_vendeuse->id == $mysoc->id) {
6690
                $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
6691
            } else {
6692
                $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
6693
            }
6694
            return $return;
6695
        }
6696
6697
        //var_dump($societe_acheteuse);
6698
        //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";
6699
        //exit;
6700
6701
        // Define list of countries to use to search VAT rates to show
6702
        // First we defined code_country to use to find list
6703
        if (is_object($societe_vendeuse)) {
6704
            $code_country = "'" . $societe_vendeuse->country_code . "'";
6705
        } else {
6706
            $code_country = "'" . $mysoc->country_code . "'"; // Pour compatibilite ascendente
6707
        }
6708
        if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {    // If option to have vat for end customer for services is on
6709
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/company.lib.php';
6710
            // If SERVICE_ARE_ECOMMERCE_200238EC=1 combo list vat rate of purchaser and seller countries
6711
            // If SERVICE_ARE_ECOMMERCE_200238EC=2 combo list only the vat rate of the purchaser country
6712
            $selectVatComboMode = getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC');
6713
            if (isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()) {
0 ignored issues
show
Bug introduced by
The method isACompany() does not exist on null. ( Ignorable by Annotation )

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

6713
            if (isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->/** @scrutinizer ignore-call */ isACompany()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
6714
                // We also add the buyer country code
6715
                if (is_numeric($type)) {
6716
                    if ($type == 1) { // We know product is a service
6717
                        switch ($selectVatComboMode) {
6718
                            case '1':
6719
                                $code_country .= ",'" . $societe_acheteuse->country_code . "'";
6720
                                break;
6721
                            case '2':
6722
                                $code_country = "'" . $societe_acheteuse->country_code . "'";
6723
                                break;
6724
                        }
6725
                    }
6726
                } elseif (!$idprod) {  // We don't know type of product
6727
                    switch ($selectVatComboMode) {
6728
                        case '1':
6729
                            $code_country .= ",'" . $societe_acheteuse->country_code . "'";
6730
                            break;
6731
                        case '2':
6732
                            $code_country = "'" . $societe_acheteuse->country_code . "'";
6733
                            break;
6734
                    }
6735
                } else {
6736
                    $prodstatic = new Product($this->db);
6737
                    $prodstatic->fetch($idprod);
6738
                    if ($prodstatic->type == Product::TYPE_SERVICE) {   // We know product is a service
6739
                        $code_country .= ",'" . $societe_acheteuse->country_code . "'";
6740
                    }
6741
                }
6742
            }
6743
        }
6744
6745
        // Now we load the list of VAT
6746
        $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
6747
6748
        // Keep only the VAT qualified for $type_vat
6749
        $arrayofvatrates = array();
6750
        foreach ($this->cache_vatrates as $cachevalue) {
6751
            if (empty($cachevalue['type_vat']) || $cachevalue['type_vat'] != $type_vat) {
6752
                $arrayofvatrates[] = $cachevalue;
6753
            }
6754
        }
6755
6756
        $num = count($arrayofvatrates);
6757
6758
        if ($num > 0) {
6759
            // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
6760
            if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6761
                $tmpthirdparty = new Societe($this->db);
6762
6763
                $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6764
                $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6765
6766
                if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6767
                    $defaultcode = $reg[1];
6768
                    $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6769
                }
6770
                if (empty($defaulttx)) {
6771
                    $defaultnpr = 0;
6772
                }
6773
            }
6774
6775
            // If we fails to find a default vat rate, we take the last one in list
6776
            // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
6777
            if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6778
                if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
6779
                    // We take the last one found in list
6780
                    $defaulttx = $arrayofvatrates[$num - 1]['txtva'];
6781
                } else {
6782
                    // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
6783
                    $defaulttx = '';
6784
                    if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
6785
                        $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
6786
                    }
6787
                    if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6788
                        $defaultcode = $reg[1];
6789
                        $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6790
                    }
6791
                }
6792
            }
6793
6794
            // Disabled if seller is not subject to VAT
6795
            $disabled = false;
6796
            $title = '';
6797
            if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
6798
                // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
6799
                // of using supplier invoices (this is a very bad idea !)
6800
                if (!getDolGlobalString('EXPENSEREPORT_OVERRIDE_VAT')) {
6801
                    $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
6802
                    $disabled = true;
6803
                }
6804
            }
6805
6806
            if (!$options_only) {
6807
                $return .= '<select class="flat minwidth75imp maxwidth100 right" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
6808
            }
6809
6810
            $selectedfound = false;
6811
            foreach ($arrayofvatrates as $rate) {
6812
                // Keep only 0 if seller is not subject to VAT
6813
                if ($disabled && $rate['txtva'] != 0) {
6814
                    continue;
6815
                }
6816
6817
                // Define key to use into select list
6818
                $key = $rate['txtva'];
6819
                $key .= $rate['nprtva'] ? '*' : '';
6820
                if ($mode > 0 && $rate['code']) {
6821
                    $key .= ' (' . $rate['code'] . ')';
6822
                }
6823
                if ($mode < 0) {
6824
                    $key = $rate['rowid'];
6825
                }
6826
6827
                $return .= '<option value="' . $key . '"';
6828
                if (!$selectedfound) {
6829
                    if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
6830
                        if ($defaultcode == $rate['code']) {
6831
                            $return .= ' selected';
6832
                            $selectedfound = true;
6833
                        }
6834
                    } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
6835
                        $return .= ' selected';
6836
                        $selectedfound = true;
6837
                    }
6838
                }
6839
                $return .= '>';
6840
6841
                // Show label of VAT
6842
                if ($mysoc->country_code == 'IN' || getDolGlobalString('MAIN_VAT_LABEL_IS_POSITIVE_RATES')) {
6843
                    // Label with all localtax and code. For example:  x.y / a.b / c.d (CODE)'
6844
                    $return .= $rate['labelpositiverates'];
6845
                } else {
6846
                    // Simple label
6847
                    $return .= vatrate($rate['label']);
6848
                }
6849
6850
                //$return.=($rate['code']?' '.$rate['code']:'');
6851
                $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the *  (old behaviour only if new vat code is not used)
6852
6853
                $return .= '</option>';
6854
            }
6855
6856
            if (!$options_only) {
6857
                $return .= '</select>';
6858
                //$return .= ajax_combobox($htmlname);      // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
6859
            }
6860
        } else {
6861
            $return .= $this->error;
6862
        }
6863
6864
        $this->num = $num;
6865
        return $return;
6866
    }
6867
6868
6869
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6870
6871
    /**
6872
     *  Show a HTML widget to input a date or combo list for day, month, years and optionally hours and minutes.
6873
     *  Fields are preselected with :
6874
     *                - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6875
     *                - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6876
     *                - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6877
     *
6878
     * @param integer|string $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).
6879
     * @param string $prefix Prefix for fields name
6880
     * @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
6881
     * @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
6882
     * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6883
     * @param string $form_name Not used
6884
     * @param int $d 1=Show days, month, years
6885
     * @param int $addnowlink Add a link "Now"
6886
     * @param int $nooutput Do not output html string but return it
6887
     * @param int $disabled Disable input fields
6888
     * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59
6889
     * @param string $addplusone Add a link "+1 hour". Value must be name of another select_date field.
6890
     * @param int|string $adddateof Add a link "Date of invoice" using the following date.
6891
     * @return    string                        '' or HTML component string if nooutput is 1
6892
     * @deprecated
6893
     * @see    selectDate(), form_date(), select_month(), select_year(), select_dayofweek()
6894
     */
6895
    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 = 0, $addplusone = '', $adddateof = '')
6896
    {
6897
		// phpcs:enable
6898
        dol_syslog(__METHOD__ . ': using select_date is deprecated. Use selectDate instead.', LOG_WARNING);
6899
        $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6900
        if (!empty($nooutput)) {
6901
            return $retstring;
6902
        }
6903
        print $retstring;
6904
6905
        return '';
6906
    }
6907
6908
    /**
6909
     *  Show 2 HTML widget to input a date or combo list for day, month, years and optionally hours and minutes.
6910
     *  Fields are preselected with :
6911
     *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6912
     *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6913
     *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6914
     *
6915
     * @param integer|string $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).
6916
     * @param integer|string $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).
6917
     * @param string $prefix Prefix for fields name
6918
     * @param int    $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6919
     * @param int    $forcenewline Force new line between the 2 dates.
6920
     * @return string                        Html for selectDate
6921
     * @see    form_date(), select_month(), select_year(), select_dayofweek()
6922
     */
6923
    public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
6924
    {
6925
        global $langs;
6926
6927
        $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6928
        if ($forcenewline) {
6929
            $ret .= '<br>';
6930
        }
6931
        $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6932
        return $ret;
6933
    }
6934
6935
    /**
6936
     *  Show a HTML widget to input a date or combo list for day, month, years and optionally hours and minutes.
6937
     *  Fields are preselected with :
6938
     *              - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM')
6939
     *              - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location)
6940
     *              - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1)
6941
     *
6942
     * @param integer|string        $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).
6943
     * @param string                $prefix         Prefix for fields name
6944
     * @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 or 4 (4=hours on a new line)=Show hour always empty
6945
     * @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
6946
     * @param int                   $empty          0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only
6947
     * @param string                $form_name      Not used
6948
     * @param int<0,1>              $d              1=Show days, month, years
6949
     * @param int<0,2>              $addnowlink     Add a link "Now", 1 with server time, 2 with local computer time
6950
     * @param int<0,1>              $disabled       Disable input fields
6951
     * @param int|string            $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')
6952
     * @param string                $addplusone     Add a link "+1 hour". Value must be name of another selectDate field.
6953
     * @param int|string|array      $adddateof      Add a link "Date of ..." using the following date. Must be array(array('adddateof'=>..., 'labeladddateof'=>...))
6954
     * @param string                $openinghours   Specify hour start and hour end for the select ex 8,20
6955
     * @param int                   $stepminutes    Specify step for minutes between 1 and 30
6956
     * @param string                $labeladddateof Label to use for the $adddateof parameter. Deprecated. Used only when $adddateof is not an array.
6957
     * @param string                $placeholder    Placeholder
6958
     * @param mixed                 $gm             'auto' (for backward compatibility, avoid this), 'gmt' or 'tzserver' or 'tzuserrel'
6959
     * @return string                               Html for selectDate
6960
     * @see    form_date(), select_month(), select_year(), select_dayofweek()
6961
     */
6962
    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')
6963
    {
6964
        global $conf, $langs;
6965
6966
        if ($gm === 'auto') {
6967
            $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6968
        }
6969
6970
        $retstring = '';
6971
6972
        if ($prefix == '') {
6973
            $prefix = 're';
6974
        }
6975
        if ($h == '') {
6976
            $h = 0;
6977
        }
6978
        if ($m == '') {
6979
            $m = 0;
6980
        }
6981
        $emptydate = 0;
6982
        $emptyhours = 0;
6983
        if ($stepminutes <= 0 || $stepminutes > 30) {
6984
            $stepminutes = 1;
6985
        }
6986
        if ($empty == 1) {
6987
            $emptydate = 1;
6988
            $emptyhours = 1;
6989
        }
6990
        if ($empty == 2) {
6991
            $emptydate = 0;
6992
            $emptyhours = 1;
6993
        }
6994
        $orig_set_time = $set_time;
6995
6996
        if ($set_time === '' && $emptydate == 0) {
6997
            include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
6998
            if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6999
                $set_time = dol_now($gm);
7000
            } else {
7001
                $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
7002
            }
7003
        }
7004
7005
        // Analysis of the pre-selection date
7006
        $reg = array();
7007
        $shour = '';
7008
        $smin = '';
7009
        $ssec = '';
7010
        if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) {    // deprecated usage
7011
            // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
7012
            $syear = (!empty($reg[1]) ? $reg[1] : '');
7013
            $smonth = (!empty($reg[2]) ? $reg[2] : '');
7014
            $sday = (!empty($reg[3]) ? $reg[3] : '');
7015
            $shour = (!empty($reg[4]) ? $reg[4] : '');
7016
            $smin = (!empty($reg[5]) ? $reg[5] : '');
7017
        } elseif (strval($set_time) != '' && $set_time != -1) {
7018
            // set_time est un timestamps (0 possible)
7019
            $syear = dol_print_date($set_time, "%Y", $gm);
7020
            $smonth = dol_print_date($set_time, "%m", $gm);
7021
            $sday = dol_print_date($set_time, "%d", $gm);
7022
            if ($orig_set_time != '') {
7023
                $shour = dol_print_date($set_time, "%H", $gm);
7024
                $smin = dol_print_date($set_time, "%M", $gm);
7025
                $ssec = dol_print_date($set_time, "%S", $gm);
7026
            }
7027
        } else {
7028
            // Date est '' ou vaut -1
7029
            $syear = '';
7030
            $smonth = '';
7031
            $sday = '';
7032
            $shour = getDolGlobalString('MAIN_DEFAULT_DATE_HOUR', ($h == -1 ? '23' : ''));
7033
            $smin = getDolGlobalString('MAIN_DEFAULT_DATE_MIN', ($h == -1 ? '59' : ''));
7034
            $ssec = getDolGlobalString('MAIN_DEFAULT_DATE_SEC', ($h == -1 ? '59' : ''));
7035
        }
7036
        if ($h == 3 || $h == 4) {
7037
            $shour = '';
7038
        }
7039
        if ($m == 3) {
7040
            $smin = '';
7041
        }
7042
7043
        $nowgmt = dol_now('gmt');
7044
        //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
7045
7046
        // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
7047
        $usecalendar = 'combo';
7048
        if (!empty($conf->use_javascript_ajax) && (!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') != "none")) {
7049
            $usecalendar = ((!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
7050
        }
7051
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
7052
            // If we use a text browser or screen reader, we use the 'combo' date selector
7053
            $usecalendar = 'html';
7054
        }
7055
7056
        if ($d) {
7057
            // Show date with popup
7058
            if ($usecalendar != 'combo') {
7059
                $formated_date = '';
7060
                //print "e".$set_time." t ".$conf->format_date_short;
7061
                if (strval($set_time) != '' && $set_time != -1) {
7062
                    //$formated_date=dol_print_date($set_time,$conf->format_date_short);
7063
                    $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
7064
                }
7065
7066
                // Calendrier popup version eldy
7067
                if ($usecalendar == "eldy") {
7068
                    // Input area to enter date manually
7069
                    $retstring .= '<input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formated_date . '"';
7070
                    $retstring .= ($disabled ? ' disabled' : '');
7071
                    $retstring .= ' onChange="dpChangeDay(\'' . $prefix . '\',\'' . $langs->trans("FormatDateShortJavaInput") . '\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
7072
                    $retstring .= ' autocomplete="off">';
7073
7074
                    // Icon calendar
7075
                    $retstringbuttom = '';
7076
                    if (!$disabled) {
7077
                        $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
7078
                        $base = constant('BASE_URL') . '/core/';
7079
                        $retstringbuttom .= ' onClick="showDP(\'' . $base . '\',\'' . $prefix . '\',\'' . $langs->trans("FormatDateShortJavaInput") . '\',\'' . $langs->defaultlang . '\');"';
7080
                        $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"') . '</button>';
7081
                    } else {
7082
                        $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"') . '</button>';
7083
                    }
7084
                    $retstring = $retstringbuttom . $retstring;
7085
7086
                    $retstring .= '<input type="hidden" id="' . $prefix . 'day"   name="' . $prefix . 'day"   value="' . $sday . '">' . "\n";
7087
                    $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
7088
                    $retstring .= '<input type="hidden" id="' . $prefix . 'year"  name="' . $prefix . 'year"  value="' . $syear . '">' . "\n";
7089
                } elseif ($usecalendar == 'jquery' || $usecalendar == 'html') {
7090
                    if (!$disabled && $usecalendar != 'html') {
7091
                        // Output javascript for datepicker
7092
                        $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (idate('Y') - 100));
7093
                        $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (idate('Y') + 100));
7094
7095
                        $retstring .= '<script nonce="' . getNonce() . '" type="text/javascript">';
7096
                        $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
7097
							dateFormat: '" . $langs->trans("FormatDateShortJQueryInput") . "',
7098
							autoclose: true,
7099
							todayHighlight: true,
7100
							yearRange: '" . $minYear . ":" . $maxYear . "',";
7101
                        if (!empty($conf->dol_use_jmobile)) {
7102
                            $retstring .= "
7103
								beforeShow: function (input, datePicker) {
7104
									input.disabled = true;
7105
								},
7106
								onClose: function (dateText, datePicker) {
7107
									this.disabled = false;
7108
								},
7109
								";
7110
                        }
7111
                        // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
7112
                        if (!getDolGlobalString('MAIN_POPUP_CALENDAR_ON_FOCUS')) {
7113
                            $retstring .= "
7114
								showOn: 'button',	/* both has problem with autocompletion */
7115
								buttonImage: '" . constant('DOL_URL_ROOT') . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png',
7116
								buttonImageOnly: true";
7117
                        }
7118
                        $retstring .= "
7119
							}) });";
7120
                        $retstring .= "</script>";
7121
                    }
7122
7123
                    // Input area to enter date manually
7124
                    $retstring .= '<div class="nowraponall inline-block divfordateinput">';
7125
                    $retstring .= '<input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formated_date . '"';
7126
                    $retstring .= ($disabled ? ' disabled' : '');
7127
                    $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
7128
                    $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
7129
                    $retstring .= ' autocomplete="off">';
7130
7131
                    // Icone calendrier
7132
                    if ($disabled) {
7133
                        $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"') . '</button>';
7134
                        $retstring = $retstringbutton . $retstring;
7135
                    }
7136
7137
                    $retstring .= '</div>';
7138
                    $retstring .= '<input type="hidden" id="' . $prefix . 'day"   name="' . $prefix . 'day"   value="' . $sday . '">' . "\n";
7139
                    $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
7140
                    $retstring .= '<input type="hidden" id="' . $prefix . 'year"  name="' . $prefix . 'year"  value="' . $syear . '">' . "\n";
7141
                } else {
7142
                    $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
7143
                }
7144
            } else {
7145
                // Show date with combo selects
7146
                // Day
7147
                $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
7148
7149
                if ($emptydate || $set_time == -1) {
7150
                    $retstring .= '<option value="0" selected>&nbsp;</option>';
7151
                }
7152
7153
                for ($day = 1; $day <= 31; $day++) {
7154
                    $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
7155
                }
7156
7157
                $retstring .= "</select>";
7158
7159
                $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
7160
                if ($emptydate || $set_time == -1) {
7161
                    $retstring .= '<option value="0" selected>&nbsp;</option>';
7162
                }
7163
7164
                // Month
7165
                for ($month = 1; $month <= 12; $month++) {
7166
                    $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
7167
                    $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
7168
                    $retstring .= "</option>";
7169
                }
7170
                $retstring .= "</select>";
7171
7172
                // Year
7173
                if ($emptydate || $set_time == -1) {
7174
                    $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 . '">';
7175
                } else {
7176
                    $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
7177
7178
                    $syear = (int) $syear;
7179
                    for ($year = $syear - 10; $year < (int) $syear + 10; $year++) {
7180
                        $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
7181
                    }
7182
                    $retstring .= "</select>\n";
7183
                }
7184
            }
7185
        }
7186
7187
        if ($d && $h) {
7188
            $retstring .= (($h == 2 || $h == 4) ? '<br>' : ' ');
7189
            $retstring .= '<span class="nowraponall">';
7190
        }
7191
7192
        if ($h) {
7193
            $hourstart = 0;
7194
            $hourend = 24;
7195
            if ($openinghours != '') {
7196
                $openinghours = explode(',', $openinghours);
7197
                $hourstart = $openinghours[0];
7198
                $hourend = $openinghours[1];
7199
                if ($hourend < $hourstart) {
7200
                    $hourend = $hourstart;
7201
                }
7202
            }
7203
            // Show hour
7204
            $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
7205
            if ($emptyhours) {
7206
                $retstring .= '<option value="-1">&nbsp;</option>';
7207
            }
7208
            for ($hour = $hourstart; $hour < $hourend; $hour++) {
7209
                if (strlen($hour) < 2) {
7210
                    $hour = "0" . $hour;
7211
                }
7212
                $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
7213
                //$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
7214
                $retstring .= '</option>';
7215
            }
7216
            $retstring .= '</select>';
7217
            //if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
7218
            if ($m) {
7219
                $retstring .= ":";
7220
            }
7221
        }
7222
7223
        if ($m) {
7224
            // Show minutes
7225
            $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
7226
            if ($emptyhours) {
7227
                $retstring .= '<option value="-1">&nbsp;</option>';
7228
            }
7229
            for ($min = 0; $min < 60; $min += $stepminutes) {
7230
                $min_str = sprintf("%02d", $min);
7231
                $retstring .= '<option value="' . $min_str . '"' . (($min_str == $smin) ? ' selected' : '') . '>' . $min_str . '</option>';
7232
            }
7233
            $retstring .= '</select>';
7234
7235
            $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
7236
        }
7237
7238
        if ($d && $h) {
7239
            $retstring .= '</span>';
7240
        }
7241
7242
        // Add a "Now" link
7243
        if (!empty($conf->use_javascript_ajax) && $addnowlink) {
7244
            // Script which will be inserted in the onClick of the "Now" link
7245
            $reset_scripts = "";
7246
            if ($addnowlink == 2) { // local computer time
7247
                // pad add leading 0 on numbers
7248
                $reset_scripts .= "Number.prototype.pad = function(size) {
7249
                        var s = String(this);
7250
                        while (s.length < (size || 2)) {s = '0' + s;}
7251
                        return s;
7252
                    };
7253
                    var d = new Date();";
7254
            }
7255
7256
            // Generate the date part, depending on the use or not of the javascript calendar
7257
            if ($addnowlink == 1) { // server time expressed in user time setup
7258
                $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
7259
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
7260
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
7261
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
7262
            } elseif ($addnowlink == 2) {
7263
                /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
7264
                 * This break application for foreign languages.
7265
                $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
7266
                $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
7267
                $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
7268
                $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
7269
                */
7270
                $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
7271
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
7272
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
7273
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
7274
            }
7275
            /*if ($usecalendar == "eldy")
7276
            {
7277
                $base=DOL_URL_ROOT.'/core/';
7278
                $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
7279
            }
7280
            else
7281
            {
7282
                $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
7283
                $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
7284
                $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
7285
            }*/
7286
            // Update the hour part
7287
            if ($h) {
7288
                if ($fullday) {
7289
                    $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7290
                }
7291
                //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
7292
                if ($addnowlink == 1) {
7293
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
7294
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
7295
                } elseif ($addnowlink == 2) {
7296
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
7297
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
7298
                }
7299
7300
                if ($fullday) {
7301
                    $reset_scripts .= ' } ';
7302
                }
7303
            }
7304
            // Update the minute part
7305
            if ($m) {
7306
                if ($fullday) {
7307
                    $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7308
                }
7309
                //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
7310
                if ($addnowlink == 1) {
7311
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
7312
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
7313
                } elseif ($addnowlink == 2) {
7314
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
7315
                    $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
7316
                }
7317
                if ($fullday) {
7318
                    $reset_scripts .= ' } ';
7319
                }
7320
            }
7321
            // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
7322
            if ($reset_scripts && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
7323
                $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
7324
                $retstring .= $langs->trans("Now");
7325
                $retstring .= '</button> ';
7326
            }
7327
        }
7328
7329
        // Add a "Plus one hour" link
7330
        if ($conf->use_javascript_ajax && $addplusone) {
7331
            // Script which will be inserted in the onClick of the "Add plusone" link
7332
            $reset_scripts = "";
7333
7334
            // Generate the date part, depending on the use or not of the javascript calendar
7335
            $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
7336
            $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
7337
            $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
7338
            $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
7339
            // Update the hour part
7340
            if ($h) {
7341
                if ($fullday) {
7342
                    $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7343
                }
7344
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
7345
                if ($fullday) {
7346
                    $reset_scripts .= ' } ';
7347
                }
7348
            }
7349
            // Update the minute part
7350
            if ($m) {
7351
                if ($fullday) {
7352
                    $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7353
                }
7354
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
7355
                if ($fullday) {
7356
                    $reset_scripts .= ' } ';
7357
                }
7358
            }
7359
            // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
7360
            if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
7361
                $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
7362
                $retstring .= $langs->trans("DateStartPlusOne");
7363
                $retstring .= '</button> ';
7364
            }
7365
        }
7366
7367
        // Add a link to set data
7368
        if ($conf->use_javascript_ajax && !empty($adddateof)) {
7369
            if (!is_array($adddateof)) {
7370
                $arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
7371
            } else {
7372
                $arrayofdateof = $adddateof;
7373
            }
7374
            foreach ($arrayofdateof as $valuedateof) {
7375
                $tmpadddateof = empty($valuedateof['adddateof']) ? 0 : $valuedateof['adddateof'];
7376
                $tmplabeladddateof = empty($valuedateof['labeladddateof']) ? '' : $valuedateof['labeladddateof'];
7377
                $tmparray = dol_getdate($tmpadddateof);
7378
                if (empty($tmplabeladddateof)) {
7379
                    $tmplabeladddateof = $langs->trans("DateInvoice");
7380
                }
7381
                $reset_scripts = 'console.log(\'Click on now link\'); ';
7382
                $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($tmpadddateof, 'dayinputnoreduce') . '\');';
7383
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . $tmparray['mday'] . '\');';
7384
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . $tmparray['mon'] . '\');';
7385
                $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . $tmparray['year'] . '\');';
7386
                $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="' . $reset_scripts . '">' . $tmplabeladddateof . '</button>';
7387
            }
7388
        }
7389
7390
        return $retstring;
7391
    }
7392
7393
    /**
7394
     * selectTypeDuration
7395
     *
7396
     * @param string $prefix Prefix
7397
     * @param string $selected Selected duration type
7398
     * @param string[] $excludetypes Array of duration types to exclude. Example array('y', 'm')
7399
     * @return  string                    HTML select string
7400
     */
7401
    public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
7402
    {
7403
        global $langs;
7404
7405
        $TDurationTypes = array(
7406
            'y' => $langs->trans('Years'),
7407
            'm' => $langs->trans('Month'),
7408
            'w' => $langs->trans('Weeks'),
7409
            'd' => $langs->trans('Days'),
7410
            'h' => $langs->trans('Hours'),
7411
            'i' => $langs->trans('Minutes')
7412
        );
7413
7414
        // Removed undesired duration types
7415
        foreach ($excludetypes as $value) {
7416
            unset($TDurationTypes[$value]);
7417
        }
7418
7419
        $retstring = '<select class="flat minwidth75 maxwidth100" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
7420
        foreach ($TDurationTypes as $key => $typeduration) {
7421
            $retstring .= '<option value="' . $key . '"';
7422
            if ($key == $selected) {
7423
                $retstring .= " selected";
7424
            }
7425
            $retstring .= ">" . $typeduration . "</option>";
7426
        }
7427
        $retstring .= "</select>";
7428
7429
        $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
7430
7431
        return $retstring;
7432
    }
7433
7434
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7435
7436
    /**
7437
     *  Function to show a form to select a duration on a page
7438
     *
7439
     * @param string $prefix Prefix for input fields
7440
     * @param int|string $iSecond Default preselected duration (number of seconds or '')
7441
     * @param int $disabled Disable the combo box
7442
     * @param string $typehour If 'select' then input hour and input min is a combo,
7443
     *                         If 'text' input hour is in text and input min is a text,
7444
     *                         If 'textselect' input hour is in text and input min is a combo
7445
     * @param integer $minunderhours If 1, show minutes selection under the hours
7446
     * @param int $nooutput Do not output html string but return it
7447
     * @return    string                        HTML component
7448
     */
7449
    public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
7450
    {
7451
		// phpcs:enable
7452
        global $langs;
7453
7454
        $retstring = '<span class="nowraponall">';
7455
7456
        $hourSelected = '';
7457
        $minSelected = '';
7458
7459
        // Hours
7460
        if ($iSecond != '') {
7461
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php';
7462
7463
            $hourSelected = convertSecondToTime($iSecond, 'allhour');
7464
            $minSelected = convertSecondToTime($iSecond, 'min');
7465
        }
7466
7467
        if ($typehour == 'select') {
7468
            $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
7469
            for ($hour = 0; $hour < 25; $hour++) {    // For a duration, we allow 24 hours
7470
                $retstring .= '<option value="' . $hour . '"';
7471
                if (is_numeric($hourSelected) && $hourSelected == $hour) {
7472
                    $retstring .= " selected";
7473
                }
7474
                $retstring .= ">" . $hour . "</option>";
7475
            }
7476
            $retstring .= "</select>";
7477
        } elseif ($typehour == 'text' || $typehour == 'textselect') {
7478
            $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
7479
        } else {
7480
            return 'BadValueForParameterTypeHour';
7481
        }
7482
7483
        if ($typehour != 'text') {
7484
            $retstring .= ' ' . $langs->trans('HourShort');
7485
        } else {
7486
            $retstring .= '<span class="">:</span>';
7487
        }
7488
7489
        // Minutes
7490
        if ($minunderhours) {
7491
            $retstring .= '<br>';
7492
        } else {
7493
            if ($typehour != 'text') {
7494
                $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
7495
            }
7496
        }
7497
7498
        if ($typehour == 'select' || $typehour == 'textselect') {
7499
            $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
7500
            for ($min = 0; $min <= 55; $min += 5) {
7501
                $retstring .= '<option value="' . $min . '"';
7502
                if (is_numeric($minSelected) && $minSelected == $min) {
7503
                    $retstring .= ' selected';
7504
                }
7505
                $retstring .= '>' . $min . '</option>';
7506
            }
7507
            $retstring .= "</select>";
7508
        } elseif ($typehour == 'text') {
7509
            $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
7510
        }
7511
7512
        if ($typehour != 'text') {
7513
            $retstring .= ' ' . $langs->trans('MinuteShort');
7514
        }
7515
7516
        $retstring .= "</span>";
7517
7518
        if (!empty($nooutput)) {
7519
            return $retstring;
7520
        }
7521
7522
        print $retstring;
7523
7524
        return '';
7525
    }
7526
7527
    /**
7528
     *  Return list of tickets in Ajax if Ajax activated or go to selectTicketsList
7529
     *
7530
     * @param   string      $selected       Preselected tickets
7531
     * @param   string      $htmlname       Name of HTML select field (must be unique in page).
7532
     * @param   string      $filtertype     To add a filter
7533
     * @param   int         $limit          Limit on number of returned lines
7534
     * @param   int         $status         Ticket status
7535
     * @param   string      $selected_input_value Value of preselected input text (for use with ajax)
7536
     * @param   int<0,3>    $hidelabel      Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
7537
     * @param   array<string,string|string[]>   $ajaxoptions    Options for ajax_autocompleter
7538
     * @param   int         $socid Thirdparty Id (to get also price dedicated to this customer)
7539
     * @param   string|int<0,1> $showempty  '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7540
     * @param   int<0,1>    $forcecombo Force to use combo box
7541
     * @param   string      $morecss        Add more css on select
7542
     * @param   array<string,string>    $selected_combinations  Selected combinations. Format: array([attrid] => attrval, [...])
7543
     * @param   int<0,1>    $nooutput       No print, return the output into a string
7544
     * @return  string
7545
     */
7546
    public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7547
    {
7548
        global $langs, $conf;
7549
7550
        $out = '';
7551
7552
        // check parameters
7553
        if (is_null($ajaxoptions)) {
7554
            $ajaxoptions = array();
7555
        }
7556
7557
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
7558
            $placeholder = '';
7559
7560
            if ($selected && empty($selected_input_value)) {
7561
                $tickettmpselect = new Ticket($this->db);
7562
                $tickettmpselect->fetch($selected);
7563
                $selected_input_value = $tickettmpselect->ref;
7564
                unset($tickettmpselect);
7565
            }
7566
7567
            $urloption = '';
7568
            $out .= ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
7569
7570
            if (empty($hidelabel)) {
7571
                $out .= $langs->trans("RefOrLabel") . ' : ';
7572
            } elseif ($hidelabel > 1) {
7573
                $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
7574
                if ($hidelabel == 2) {
7575
                    $out .= img_picto($langs->trans("Search"), 'search');
7576
                }
7577
            }
7578
            $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
7579
            if ($hidelabel == 3) {
7580
                $out .= img_picto($langs->trans("Search"), 'search');
7581
            }
7582
        } else {
7583
            $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
7584
        }
7585
7586
        if (empty($nooutput)) {
7587
            print $out;
7588
        } else {
7589
            return $out;
7590
        }
7591
        return '';
7592
    }
7593
7594
7595
    /**
7596
     * Return list of tickets.
7597
     *  Called by selectTickets.
7598
     *
7599
     * @param   string      $selected       Preselected ticket
7600
     * @param   string      $htmlname       Name of select html
7601
     * @param   string      $filtertype     Filter on ticket type
7602
     * @param   int         $limit Limit on number of returned lines
7603
     * @param   string      $filterkey      Filter on ticket ref or subject
7604
     * @param   int         $status         Ticket status
7605
     * @param   int         $outputmode     0=HTML select string, 1=Array
7606
     * @param   string|int<0,1> $showempty  '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7607
     * @param int $forcecombo Force to use combo box
7608
     * @param   string $morecss Add more css on select
7609
     * @return  array|string                Array of keys for json or HTML component
7610
     */
7611
    public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7612
    {
7613
        global $langs, $conf;
7614
7615
        $out = '';
7616
        $outarray = array();
7617
7618
        $selectFields = " p.rowid, p.ref, p.message";
7619
7620
        $sql = "SELECT ";
7621
        $sql .= $selectFields;
7622
        $sql .= " FROM " . $this->db->prefix() . "ticket as p";
7623
        $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
7624
7625
        // Add criteria on ref/label
7626
        if ($filterkey != '') {
7627
            $sql .= ' AND (';
7628
            $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7629
            // For natural search
7630
            $search_crit = explode(' ', $filterkey);
7631
            $i = 0;
7632
            if (count($search_crit) > 1) {
7633
                $sql .= "(";
7634
            }
7635
            foreach ($search_crit as $crit) {
7636
                if ($i > 0) {
7637
                    $sql .= " AND ";
7638
                }
7639
                $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
7640
                $sql .= ")";
7641
                $i++;
7642
            }
7643
            if (count($search_crit) > 1) {
7644
                $sql .= ")";
7645
            }
7646
            $sql .= ')';
7647
        }
7648
7649
        $sql .= $this->db->plimit($limit, 0);
7650
7651
        // Build output string
7652
        dol_syslog(get_only_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
7653
        $result = $this->db->query($sql);
7654
        if ($result) {
7655
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/ticket.lib.php';
7656
7657
            $num = $this->db->num_rows($result);
7658
7659
            $events = array();
7660
7661
            if (!$forcecombo) {
7662
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7663
                $out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT);
7664
            }
7665
7666
            $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7667
7668
            $textifempty = '';
7669
            // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7670
            //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7671
            if (getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
7672
                if ($showempty && !is_numeric($showempty)) {
7673
                    $textifempty = $langs->trans($showempty);
7674
                } else {
7675
                    $textifempty .= $langs->trans("All");
7676
                }
7677
            } else {
7678
                if ($showempty && !is_numeric($showempty)) {
7679
                    $textifempty = $langs->trans($showempty);
7680
                }
7681
            }
7682
            if ($showempty) {
7683
                $out .= '<option value="0" selected>' . $textifempty . '</option>';
7684
            }
7685
7686
            $i = 0;
7687
            while ($num && $i < $num) {
7688
                $opt = '';
7689
                $optJson = array();
7690
                $objp = $this->db->fetch_object($result);
7691
7692
                $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
7693
                // Add new entry
7694
                // "key" value of json key array is used by jQuery automatically as selected value
7695
                // "label" value of json key array is used by jQuery automatically as text for combo box
7696
                $out .= $opt;
7697
                array_push($outarray, $optJson);
7698
7699
                $i++;
7700
            }
7701
7702
            $out .= '</select>';
7703
7704
            $this->db->free($result);
7705
7706
            if (empty($outputmode)) {
7707
                return $out;
7708
            }
7709
            return $outarray;
7710
        } else {
7711
            dol_print_error($this->db);
7712
        }
7713
7714
        return array();
7715
    }
7716
7717
    /**
7718
     * constructTicketListOption.
7719
     * This define value for &$opt and &$optJson.
7720
     *
7721
     * @param object    $objp       Result set of fetch
7722
     * @param string    $opt        Option (var used for returned value in string option format)
7723
     * @param mixed[]   $optJson    Option (var used for returned value in json format)
7724
     * @param string    $selected   Preselected value
7725
     * @param string    $filterkey  Filter key to highlight
7726
     * @return    void
7727
     */
7728
    protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7729
    {
7730
        $outkey = '';
7731
        $outref = '';
7732
        $outtype = '';
7733
7734
        $outkey = $objp->rowid;
7735
        $outref = $objp->ref;
7736
        $outtype = $objp->fk_product_type;
7737
7738
        $opt = '<option value="' . $objp->rowid . '"';
7739
        $opt .= ($objp->rowid == $selected) ? ' selected' : '';
7740
        $opt .= '>';
7741
        $opt .= $objp->ref;
7742
        $objRef = $objp->ref;
7743
        if (!empty($filterkey) && $filterkey != '') {
7744
            $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
7745
        }
7746
7747
        $opt .= "</option>\n";
7748
        $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
7749
    }
7750
7751
    /**
7752
     *  Return list of projects in Ajax if Ajax activated or go to selectTicketsList
7753
     *
7754
     * @param   string  $selected               Preselected tickets
7755
     * @param   string  $htmlname               Name of HTML select field (must be unique in page).
7756
     * @param   string  $filtertype             To add a filter
7757
     * @param   int     $limit                  Limit on number of returned lines
7758
     * @param   int     $status                 Ticket status
7759
     * @param   string  $selected_input_value   Value of preselected input text (for use with ajax)
7760
     * @param   int<0,3>    $hidelabel              Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
7761
     * @param   array<string,string|string[]>   $ajaxoptions            Options for ajax_autocompleter
7762
     * @param   int     $socid                  Thirdparty Id (to get also price dedicated to this customer)
7763
     * @param   string|int<0,1> $showempty      '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7764
     * @param   int<0,1>    $forcecombo             Force to use combo box
7765
     * @param   string  $morecss                Add more css on select
7766
     * @param   array<string,string> $selected_combinations     Selected combinations. Format: array([attrid] => attrval, [...])
7767
     * @param   int<0,1>    $nooutput               No print, return the output into a string
7768
     * @return  string
7769
     */
7770
    public function selectProjects($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7771
    {
7772
        global $langs, $conf;
7773
7774
        $out = '';
7775
7776
        // check parameters
7777
        if (is_null($ajaxoptions)) {
7778
            $ajaxoptions = array();
7779
        }
7780
7781
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
7782
            $placeholder = '';
7783
7784
            if ($selected && empty($selected_input_value)) {
7785
                $projecttmpselect = new Project($this->db);
7786
                $projecttmpselect->fetch($selected);
7787
                $selected_input_value = $projecttmpselect->ref;
7788
                unset($projecttmpselect);
7789
            }
7790
7791
            $urloption = '';
7792
            $out .= ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
7793
7794
            if (empty($hidelabel)) {
7795
                $out .= $langs->trans("RefOrLabel") . ' : ';
7796
            } elseif ($hidelabel > 1) {
7797
                $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
7798
                if ($hidelabel == 2) {
7799
                    $out .= img_picto($langs->trans("Search"), 'search');
7800
                }
7801
            }
7802
            $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
7803
            if ($hidelabel == 3) {
7804
                $out .= img_picto($langs->trans("Search"), 'search');
7805
            }
7806
        } else {
7807
            $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
7808
        }
7809
7810
        if (empty($nooutput)) {
7811
            print $out;
7812
        } else {
7813
            return $out;
7814
        }
7815
        return '';
7816
    }
7817
7818
    /**
7819
     *    Return list of projects.
7820
     *  Called by selectProjects.
7821
     *
7822
     * @param string $selected Preselected project
7823
     * @param string $htmlname Name of select html
7824
     * @param string $filtertype Filter on project type
7825
     * @param int $limit Limit on number of returned lines
7826
     * @param string $filterkey Filter on project ref or subject
7827
     * @param int $status Ticket status
7828
     * @param int $outputmode 0=HTML select string, 1=Array
7829
     * @param string|int<0,1> $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7830
     * @param int $forcecombo Force to use combo box
7831
     * @param string $morecss Add more css on select
7832
     * @return     array|string                Array of keys for json or HTML component
7833
     */
7834
    public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7835
    {
7836
        global $langs, $conf;
7837
7838
        $out = '';
7839
        $outarray = array();
7840
7841
        $selectFields = " p.rowid, p.ref";
7842
7843
        $sql = "SELECT ";
7844
        $sql .= $selectFields;
7845
        $sql .= " FROM " . $this->db->prefix() . "projet as p";
7846
        $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
7847
7848
        // Add criteria on ref/label
7849
        if ($filterkey != '') {
7850
            $sql .= ' AND (';
7851
            $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7852
            // For natural search
7853
            $search_crit = explode(' ', $filterkey);
7854
            $i = 0;
7855
            if (count($search_crit) > 1) {
7856
                $sql .= "(";
7857
            }
7858
            foreach ($search_crit as $crit) {
7859
                if ($i > 0) {
7860
                    $sql .= " AND ";
7861
                }
7862
                $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
7863
                $sql .= "";
7864
                $i++;
7865
            }
7866
            if (count($search_crit) > 1) {
7867
                $sql .= ")";
7868
            }
7869
            $sql .= ')';
7870
        }
7871
7872
        $sql .= $this->db->plimit($limit, 0);
7873
7874
        // Build output string
7875
        dol_syslog(get_only_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
7876
        $result = $this->db->query($sql);
7877
        if ($result) {
7878
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/project.lib.php';
7879
7880
            $num = $this->db->num_rows($result);
7881
7882
            $events = array();
7883
7884
            if (!$forcecombo) {
7885
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7886
                $out .= ajax_combobox($htmlname, $events, $conf->global->PROJECT_USE_SEARCH_TO_SELECT);
7887
            }
7888
7889
            $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7890
7891
            $textifempty = '';
7892
            // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7893
            //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7894
            if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
7895
                if ($showempty && !is_numeric($showempty)) {
7896
                    $textifempty = $langs->trans($showempty);
7897
                } else {
7898
                    $textifempty .= $langs->trans("All");
7899
                }
7900
            } else {
7901
                if ($showempty && !is_numeric($showempty)) {
7902
                    $textifempty = $langs->trans($showempty);
7903
                }
7904
            }
7905
            if ($showempty) {
7906
                $out .= '<option value="0" selected>' . $textifempty . '</option>';
7907
            }
7908
7909
            $i = 0;
7910
            while ($num && $i < $num) {
7911
                $opt = '';
7912
                $optJson = array();
7913
                $objp = $this->db->fetch_object($result);
7914
7915
                $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
7916
                // Add new entry
7917
                // "key" value of json key array is used by jQuery automatically as selected value
7918
                // "label" value of json key array is used by jQuery automatically as text for combo box
7919
                $out .= $opt;
7920
                array_push($outarray, $optJson);
7921
7922
                $i++;
7923
            }
7924
7925
            $out .= '</select>';
7926
7927
            $this->db->free($result);
7928
7929
            if (empty($outputmode)) {
7930
                return $out;
7931
            }
7932
            return $outarray;
7933
        } else {
7934
            dol_print_error($this->db);
7935
        }
7936
7937
        return array();
7938
    }
7939
7940
    /**
7941
     * constructProjectListOption.
7942
     * This define value for &$opt and &$optJson.
7943
     *
7944
     * @param stdClass  $objp       Result set of fetch
7945
     * @param string    $opt        Option (var used for returned value in string option format)
7946
     * @param array{key:string,value:string,type:string}    $optJson    Option (var used for returned value in json format)
7947
     * @param string    $selected   Preselected value
7948
     * @param string    $filterkey  Filter key to highlight
7949
     * @return    void
7950
     */
7951
    protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7952
    {
7953
        $outkey = '';
7954
        $outref = '';
7955
        $outtype = '';
7956
7957
        $label = $objp->label;
7958
7959
        $outkey = $objp->rowid;
7960
        $outref = $objp->ref;
7961
        $outlabel = $objp->label;
7962
        $outtype = $objp->fk_product_type;
7963
7964
        $opt = '<option value="' . $objp->rowid . '"';
7965
        $opt .= ($objp->rowid == $selected) ? ' selected' : '';
7966
        $opt .= '>';
7967
        $opt .= $objp->ref;
7968
        $objRef = $objp->ref;
7969
        if (!empty($filterkey) && $filterkey != '') {
7970
            $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
7971
        }
7972
7973
        $opt .= "</option>\n";
7974
        $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
7975
    }
7976
7977
7978
    /**
7979
     *  Return list of members in Ajax if Ajax activated or go to selectTicketsList
7980
     *
7981
     * @param string $selected Preselected tickets
7982
     * @param string $htmlname Name of HTML select field (must be unique in page).
7983
     * @param string $filtertype To add a filter
7984
     * @param int $limit Limit on number of returned lines
7985
     * @param int $status Ticket status
7986
     * @param string $selected_input_value Value of preselected input text (for use with ajax)
7987
     * @param int<0,3> $hidelabel Hide label (0=no, 1=yes, 2=show search icon before and placeholder, 3 search icon after)
7988
     * @param array<string,string|string[]> $ajaxoptions Options for ajax_autocompleter
7989
     * @param int $socid Thirdparty Id (to get also price dedicated to this customer)
7990
     * @param string|int<0,1> $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
7991
     * @param int $forcecombo Force to use combo box
7992
     * @param string $morecss Add more css on select
7993
     * @param array<string,string> $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...])
7994
     * @param int<0,1>  $nooutput No print, return the output into a string
7995
     * @return        string
7996
     */
7997
    public function selectMembers($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7998
    {
7999
        global $langs, $conf;
8000
8001
        $out = '';
8002
8003
        // check parameters
8004
        if (is_null($ajaxoptions)) {
8005
            $ajaxoptions = array();
8006
        }
8007
8008
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
8009
            $placeholder = '';
8010
8011
            if ($selected && empty($selected_input_value)) {
8012
                $adherenttmpselect = new Adherent($this->db);
8013
                $adherenttmpselect->fetch($selected);
8014
                $selected_input_value = $adherenttmpselect->ref;
8015
                unset($adherenttmpselect);
8016
            }
8017
8018
            $urloption = '';
8019
8020
            $out .= ajax_autocompleter($selected, $htmlname, constant('BASE_URL') . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
8021
8022
            if (empty($hidelabel)) {
8023
                $out .= $langs->trans("RefOrLabel") . ' : ';
8024
            } elseif ($hidelabel > 1) {
8025
                $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
8026
                if ($hidelabel == 2) {
8027
                    $out .= img_picto($langs->trans("Search"), 'search');
8028
                }
8029
            }
8030
            $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
8031
            if ($hidelabel == 3) {
8032
                $out .= img_picto($langs->trans("Search"), 'search');
8033
            }
8034
        } else {
8035
            $filterkey = '';
8036
8037
            $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss);
8038
        }
8039
8040
        if (empty($nooutput)) {
8041
            print $out;
8042
        } else {
8043
            return $out;
8044
        }
8045
        return '';
8046
    }
8047
8048
    /**
8049
     *    Return list of adherents.
8050
     *  Called by selectMembers.
8051
     *
8052
     * @param string $selected Preselected adherent
8053
     * @param string $htmlname Name of select html
8054
     * @param string $filtertype Filter on adherent type
8055
     * @param int $limit Limit on number of returned lines
8056
     * @param string $filterkey Filter on member status
8057
     * @param int $status Member status
8058
     * @param int $outputmode 0=HTML select string, 1=Array
8059
     * @param string|int<0,1> $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text.
8060
     * @param int $forcecombo Force to use combo box
8061
     * @param string $morecss Add more css on select
8062
     * @return     array|string                Array of keys for json or HTML string component
8063
     */
8064
    public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
8065
    {
8066
        global $langs, $conf;
8067
8068
        $out = '';
8069
        $outarray = array();
8070
8071
        $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
8072
8073
        $sql = "SELECT ";
8074
        $sql .= $selectFields;
8075
        $sql .= " FROM " . $this->db->prefix() . "adherent as p";
8076
        $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
8077
8078
        // Add criteria on ref/label
8079
        if ($filterkey != '') {
8080
            $sql .= ' AND (';
8081
            $prefix = !getDolGlobalString('MEMBER_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
8082
            // For natural search
8083
            $search_crit = explode(' ', $filterkey);
8084
            $i = 0;
8085
            if (count($search_crit) > 1) {
8086
                $sql .= "(";
8087
            }
8088
            foreach ($search_crit as $crit) {
8089
                if ($i > 0) {
8090
                    $sql .= " AND ";
8091
                }
8092
                $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
8093
                $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
8094
                $i++;
8095
            }
8096
            if (count($search_crit) > 1) {
8097
                $sql .= ")";
8098
            }
8099
            $sql .= ')';
8100
        }
8101
        if ($status != -1) {
8102
            $sql .= ' AND statut = ' . ((int) $status);
8103
        }
8104
        $sql .= $this->db->plimit($limit, 0);
8105
8106
        // Build output string
8107
        dol_syslog(get_only_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
8108
        $result = $this->db->query($sql);
8109
        if ($result) {
8110
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/member.lib.php';
8111
8112
            $num = $this->db->num_rows($result);
8113
8114
            $events = array();
8115
8116
            if (!$forcecombo) {
8117
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
8118
                $out .= ajax_combobox($htmlname, $events, getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT') ? $conf->global->PROJECT_USE_SEARCH_TO_SELECT : '');
8119
            }
8120
8121
            $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
8122
8123
            $textifempty = '';
8124
            // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
8125
            //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
8126
            if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
8127
                if ($showempty && !is_numeric($showempty)) {
8128
                    $textifempty = $langs->trans($showempty);
8129
                } else {
8130
                    $textifempty .= $langs->trans("All");
8131
                }
8132
            } else {
8133
                if ($showempty && !is_numeric($showempty)) {
8134
                    $textifempty = $langs->trans($showempty);
8135
                }
8136
            }
8137
            if ($showempty) {
8138
                $out .= '<option value="-1" selected>' . $textifempty . '</option>';
8139
            }
8140
8141
            $i = 0;
8142
            while ($num && $i < $num) {
8143
                $opt = '';
8144
                $optJson = array();
8145
                $objp = $this->db->fetch_object($result);
8146
8147
                $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
8148
8149
                // Add new entry
8150
                // "key" value of json key array is used by jQuery automatically as selected value
8151
                // "label" value of json key array is used by jQuery automatically as text for combo box
8152
                $out .= $opt;
8153
                array_push($outarray, $optJson);
8154
8155
                $i++;
8156
            }
8157
8158
            $out .= '</select>';
8159
8160
            $this->db->free($result);
8161
8162
            if (empty($outputmode)) {
8163
                return $out;
8164
            }
8165
            return $outarray;
8166
        } else {
8167
            dol_print_error($this->db);
8168
        }
8169
8170
        return array();
8171
    }
8172
8173
    /**
8174
     * constructMemberListOption.
8175
     * This define value for &$opt and &$optJson.
8176
     *
8177
     * @param object    $objp           Result set of fetch
8178
     * @param string    $opt            Option (var used for returned value in string option format)
8179
     * @param mixed[]   $optJson        Option (var used for returned value in json format)
8180
     * @param string    $selected       Preselected value
8181
     * @param string    $filterkey      Filter key to highlight
8182
     * @return    void
8183
     */
8184
    protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
8185
    {
8186
        $outkey = '';
8187
        $outlabel = '';
8188
        $outtype = '';
8189
8190
        $outkey = $objp->rowid;
8191
        $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
8192
        $outtype = $objp->fk_adherent_type;
8193
8194
        $opt = '<option value="' . $objp->rowid . '"';
8195
        $opt .= ($objp->rowid == $selected) ? ' selected' : '';
8196
        $opt .= '>';
8197
        if (!empty($filterkey) && $filterkey != '') {
8198
            $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
8199
        }
8200
        $opt .= $outlabel;
8201
        $opt .= "</option>\n";
8202
8203
        $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
8204
    }
8205
8206
    /**
8207
     * Generic method to select a component from a combo list.
8208
     * Can use autocomplete with ajax after x key pressed or a full combo, depending on setup.
8209
     * This is the generic method that will replace all specific existing methods.
8210
     *
8211
     * @param   string      $objectdesc             'ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'. For hard coded custom needs. Try to prefer method using $objectfield instead of $objectdesc.
8212
     * @param   string      $htmlname               Name of HTML select component
8213
     * @param   int         $preSelectedValue       Preselected value (ID of element)
8214
     * @param   string|int<0,1> $showempty          ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
8215
     * @param   string      $searchkey              Search criteria
8216
     * @param   string      $placeholder            Place holder
8217
     * @param   string      $morecss                More CSS
8218
     * @param   string      $moreparams             More params provided to ajax call
8219
     * @param   int         $forcecombo             Force to load all values and output a standard combobox (with no beautification)
8220
     * @param   int<0,1>    $disabled               1=Html component is disabled
8221
     * @param   string      $selected_input_value   Value of preselected input text (for use with ajax)
8222
     * @param   string      $objectfield            Object:Field that contains the definition of parent (in table $fields or $extrafields). Example: 'Object:xxx' or 'Object@module:xxx' (old syntax 'Module_Object:xxx') or 'Object:options_xxx' or 'Object@module:options_xxx' (old syntax 'Module_Object:options_xxx')
8223
     * @return  string                              Return HTML string
8224
     * @see selectForFormsList(), select_thirdparty_list()
8225
     */
8226
    public function selectForForms($objectdesc, $htmlname, $preSelectedValue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '', $objectfield = '')
8227
    {
8228
        global $conf, $extrafields, $user, $db;
8229
8230
        //var_dump($objectdesc); debug_print_backtrace();
8231
8232
        $objectdescorig = $objectdesc;
8233
        $objecttmp = null;
8234
        $InfoFieldList = array();
8235
        $classname = '';
8236
        $filter = '';  // Ensure filter has value (for static analysis)
8237
        $sortfield = '';  // Ensure filter has value (for static analysis)
8238
8239
        if ($objectfield) { // We must retrieve the objectdesc from the field or extrafield
8240
            // Example: $objectfield = 'product:options_package' or 'myobject@mymodule:options_myfield'
8241
            $tmparray = explode(':', $objectfield);
8242
8243
            // Get instance of object from $element
8244
            $objectforfieldstmp = fetchObjectByElement(0, strtolower($tmparray[0]));
8245
8246
            if (is_object($objectforfieldstmp)) {
8247
                $objectdesc = '';
8248
8249
                $reg = array();
8250
                if (preg_match('/^options_(.*)$/', $tmparray[1], $reg)) {
8251
                    // For a property in extrafields
8252
                    $key = $reg[1];
8253
                    // fetch optionals attributes and labels
8254
                    $extrafields->fetch_name_optionals_label($objectforfieldstmp->table_element);
8255
8256
                    if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key]) && $extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key] == 'link') {
8257
                        if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options'])) {
8258
                            $tmpextrafields = array_keys($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options']);
8259
                            $objectdesc = $tmpextrafields[0];
8260
                        }
8261
                    }
8262
                } else {
8263
                    // For a property in ->fields
8264
                    if (array_key_exists($tmparray[1], $objectforfieldstmp->fields)) {
8265
                        $objectdesc = $objectforfieldstmp->fields[$tmparray[1]]['type'];
8266
                        $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
8267
                    }
8268
                }
8269
            }
8270
        }
8271
8272
        if ($objectdesc) {
8273
            // Example of value for $objectdesc:
8274
            // Bom:bom/class/bom.class.php:0:t.status=1
8275
            // Bom:bom/class/bom.class.php:0:t.status=1:ref
8276
            // Bom:bom/class/bom.class.php:0:(t.status:=:1) OR (t.field2:=:2):ref
8277
            $InfoFieldList = explode(":", $objectdesc, 4);
8278
            $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
8279
            $reg = array();
8280
            if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
8281
                $InfoFieldList[4] = $reg[1];    // take the sort field
8282
            }
8283
            $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp);    // take the filter field
8284
8285
            $classname = $InfoFieldList[0];
8286
            $classpath = empty($InfoFieldList[1]) ? '' : $InfoFieldList[1];
8287
            //$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
8288
            $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
8289
            $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
8290
8291
8292
            // Load object according to $id and $element
8293
            // $objecttmp = fetchObjectByElement(0, strtolower($InfoFieldList[0]));
8294
            $objecttmp = Misc::loadModel($InfoFieldList[0], $db);
8295
8296
            // Fallback to another solution to get $objecttmp
8297
            if (empty($objecttmp) && !empty($classpath)) {
8298
                dol_include_once($classpath);
8299
8300
                if ($classname && class_exists($classname)) {
8301
                    $objecttmp = new $classname($this->db);
8302
                }
8303
            }
8304
        }
8305
8306
        // Make some replacement in $filter. May not be used if we used the ajax mode with $objectfield. In such a case
8307
        // we propagate the $objectfield and not the filter and replacement is done by the ajax/selectobject.php component.
8308
        $sharedentities = (is_object($objecttmp) && property_exists($objecttmp, 'element')) ? getEntity($objecttmp->element) : strtolower($classname);
8309
        $filter = str_replace(
8310
            array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
8311
            array($conf->entity, $sharedentities, $user->id),
8312
            $filter
8313
        );
8314
8315
        if (!is_object($objecttmp)) {
8316
            dol_syslog('selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig . ', objectfield=' . $objectfield . ', objectdesc=' . $objectdesc, LOG_WARNING);
8317
            return 'selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig . ', objectfield=' . $objectfield . ', objectdesc=' . $objectdesc;
8318
        }
8319
        '@phan-var-force CommonObject $objecttmp';
8320
8321
        //var_dump($filter);
8322
        $prefixforautocompletemode = $objecttmp->element;
8323
        if ($prefixforautocompletemode == 'societe') {
8324
            $prefixforautocompletemode = 'company';
8325
        }
8326
        if ($prefixforautocompletemode == 'product') {
8327
            $prefixforautocompletemode = 'produit';
8328
        }
8329
        $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
8330
8331
        dol_syslog(get_only_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
8332
8333
        // Generate the combo HTML component
8334
        $out = '';
8335
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
8336
            // No immediate load of all database
8337
            $placeholder = '';
8338
8339
            if ($preSelectedValue && empty($selected_input_value)) {
8340
                $objecttmp->fetch($preSelectedValue);
8341
                $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
8342
8343
                $oldValueForShowOnCombobox = 0;
8344
                foreach ($objecttmp->fields as $fieldK => $fielV) {
8345
                    if (empty($fielV['showoncombobox']) || empty($objecttmp->$fieldK)) {
8346
                        continue;
8347
                    }
8348
8349
                    if (!$oldValueForShowOnCombobox) {
8350
                        $selected_input_value = '';
8351
                    }
8352
8353
                    $selected_input_value .= $oldValueForShowOnCombobox ? ' - ' : '';
8354
                    $selected_input_value .= $objecttmp->$fieldK;
8355
                    $oldValueForShowOnCombobox = empty($fielV['showoncombobox']) ? 0 : $fielV['showoncombobox'];
8356
                }
8357
            }
8358
8359
            // Set url and param to call to get json of the search results
8360
            $urlforajaxcall = constant('BASE_URL') . '/core/ajax/selectobject.php';
8361
            $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdescorig) . '&objectfield=' . urlencode($objectfield) . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
8362
8363
            // Activate the auto complete using ajax call.
8364
            $out .= ajax_autocompleter($preSelectedValue, $htmlname, $urlforajaxcall, $urloption, getDolGlobalString($confkeyforautocompletemode), 0);
8365
            $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
8366
            $out .= '<input type="text" class="' . $morecss . '"' . ($disabled ? ' disabled="disabled"' : '') . ' name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' />';
8367
        } else {
8368
            // Immediate load of table record.
8369
            $out .= $this->selectForFormsList($objecttmp, $htmlname, $preSelectedValue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
8370
        }
8371
8372
        return $out;
8373
    }
8374
8375
8376
    /**
8377
     * Output html form to select an object.
8378
     * Note, this function is called by selectForForms or by ajax selectobject.php
8379
     *
8380
     * @param Object        $objecttmp          Object to know the table to scan for combo.
8381
     * @param string        $htmlname           Name of HTML select component
8382
     * @param int           $preselectedvalue   Preselected value (ID of element)
8383
     * @param string|int<0,1>   $showempty      ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
8384
     * @param string        $searchkey          Search value
8385
     * @param string        $placeholder        Place holder
8386
     * @param string        $morecss            More CSS
8387
     * @param string        $moreparams         More params provided to ajax call
8388
     * @param int           $forcecombo         Force to load all values and output a standard combobox (with no beautification)
8389
     * @param int           $outputmode         0=HTML select string, 1=Array
8390
     * @param int           $disabled           1=Html component is disabled
8391
     * @param string        $sortfield          Sort field
8392
     * @param string        $filter             Add more filter (Universal Search Filter)
8393
     * @return string|array                     Return HTML string
8394
     * @see selectForForms()
8395
     */
8396
    public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '')
8397
    {
8398
        global $langs, $user, $hookmanager;
8399
8400
        //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
8401
8402
        $prefixforautocompletemode = $objecttmp->element;
8403
        if ($prefixforautocompletemode == 'societe') {
8404
            $prefixforautocompletemode = 'company';
8405
        }
8406
        $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
8407
8408
        if (!empty($objecttmp->fields)) {    // For object that declare it, it is better to use declared fields (like societe, contact, ...)
8409
            $tmpfieldstoshow = '';
8410
            foreach ($objecttmp->fields as $key => $val) {
8411
                if (! (int) dol_eval($val['enabled'], 1, 1, '1')) {
8412
                    continue;
8413
                }
8414
                if (!empty($val['showoncombobox'])) {
8415
                    $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
8416
                }
8417
            }
8418
            if ($tmpfieldstoshow) {
8419
                $fieldstoshow = $tmpfieldstoshow;
8420
            }
8421
        } else {
8422
            // For backward compatibility
8423
            $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'showoncombobox' => 1);
8424
        }
8425
8426
        if (empty($fieldstoshow)) {
8427
            if (isset($objecttmp->fields['ref'])) {
8428
                $fieldstoshow = 't.ref';
8429
            } else {
8430
                $langs->load("errors");
8431
                $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
8432
                return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
8433
            }
8434
        }
8435
8436
        $out = '';
8437
        $outarray = array();
8438
        $tmparray = array();
8439
8440
        $num = 0;
8441
8442
        // Search data
8443
        $sql = "SELECT t.rowid, " . $fieldstoshow . " FROM " . $this->db->prefix() . $objecttmp->table_element . " as t";
8444
        if (!empty($objecttmp->isextrafieldmanaged)) {
8445
            $sql .= " LEFT JOIN " . $this->db->prefix() . $objecttmp->table_element . "_extrafields as e ON t.rowid=e.fk_object";
8446
        }
8447
        if (isset($objecttmp->ismultientitymanaged)) {
8448
            if (!is_numeric($objecttmp->ismultientitymanaged)) {
8449
                $tmparray = explode('@', $objecttmp->ismultientitymanaged);
8450
                $sql .= " INNER JOIN " . $this->db->prefix() . $tmparray[1] . " as parenttable ON parenttable.rowid = t." . $tmparray[0];
8451
            }
8452
            if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
8453
                if (!$user->hasRight('societe', 'client', 'voir')) {
8454
                    $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
8455
                }
8456
            }
8457
        }
8458
8459
        // Add where from hooks
8460
        $parameters = array(
8461
            'object' => $objecttmp,
8462
            'htmlname' => $htmlname,
8463
            'filter' => $filter,
8464
            'searchkey' => $searchkey
8465
        );
8466
8467
        $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
8468
        if (!empty($hookmanager->resPrint)) {
8469
            $sql .= $hookmanager->resPrint;
8470
        } else {
8471
            $sql .= " WHERE 1=1";
8472
            if (isset($objecttmp->ismultientitymanaged)) {
8473
                if ($objecttmp->ismultientitymanaged == 1) {
8474
                    $sql .= " AND t.entity IN (" . getEntity($objecttmp->table_element) . ")";
8475
                }
8476
                if (!is_numeric($objecttmp->ismultientitymanaged)) {
8477
                    $sql .= " AND parenttable.entity = t." . $tmparray[0];
8478
                }
8479
                if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
8480
                    if ($objecttmp->element == 'societe') {
8481
                        $sql .= " AND t.rowid = " . ((int) $user->socid);
8482
                    } else {
8483
                        $sql .= " AND t.fk_soc = " . ((int) $user->socid);
8484
                    }
8485
                }
8486
                if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
8487
                    if (!$user->hasRight('societe', 'client', 'voir')) {
8488
                        $sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
8489
                    }
8490
                }
8491
            }
8492
            if ($searchkey != '') {
8493
                $sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
8494
            }
8495
8496
            if ($filter) {     // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
8497
                $errormessage = '';
8498
                $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
8499
                if ($errormessage) {
8500
                    return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
8501
                }
8502
            }
8503
        }
8504
        $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, "ASC");
8505
        //$sql.=$this->db->plimit($limit, 0);
8506
        //print $sql;
8507
8508
        // Build output string
8509
        $resql = $this->db->query($sql);
8510
        if ($resql) {
8511
            // Construct $out and $outarray
8512
            $out .= '<select id="' . $htmlname . '" class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
8513
8514
            // 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
8515
            $textifempty = '&nbsp;';
8516
8517
            //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
8518
            if (getDolGlobalInt($confkeyforautocompletemode)) {
8519
                if ($showempty && !is_numeric($showempty)) {
8520
                    $textifempty = $langs->trans($showempty);
8521
                } else {
8522
                    $textifempty .= $langs->trans("All");
8523
                }
8524
            }
8525
            if ($showempty) {
8526
                $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
8527
            }
8528
8529
            $num = $this->db->num_rows($resql);
8530
            $i = 0;
8531
            if ($num) {
8532
                while ($i < $num) {
8533
                    $obj = $this->db->fetch_object($resql);
8534
                    $label = '';
8535
                    $labelhtml = '';
8536
                    $tmparray = explode(',', $fieldstoshow);
8537
                    $oldvalueforshowoncombobox = 0;
8538
                    foreach ($tmparray as $key => $val) {
8539
                        $val = preg_replace('/t\./', '', $val);
8540
                        $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
8541
                        $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
8542
                        $label .= $obj->$val;
8543
                        $labelhtml .= $obj->$val;
8544
8545
                        $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
8546
                    }
8547
                    if (empty($outputmode)) {
8548
                        if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
8549
                            $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
8550
                        } else {
8551
                            $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
8552
                        }
8553
                    } else {
8554
                        array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
8555
                    }
8556
8557
                    $i++;
8558
                    if (($i % 10) == 0) {
8559
                        $out .= "\n";
8560
                    }
8561
                }
8562
            }
8563
8564
            $out .= '</select>' . "\n";
8565
8566
            if (!$forcecombo) {
8567
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
8568
                $out .= ajax_combobox($htmlname, array(), getDolGlobalInt($confkeyforautocompletemode, 0));
8569
            }
8570
        } else {
8571
            dol_print_error($this->db);
8572
        }
8573
8574
        $this->result = array('nbofelement' => $num);
8575
8576
        if ($outputmode) {
8577
            return $outarray;
8578
        }
8579
        return $out;
8580
    }
8581
8582
8583
    /**
8584
     *  Return a HTML select string, built from an array of key+value.
8585
     *  Note: Do not apply langs->trans function on returned content, content may be entity encoded twice.
8586
     *
8587
     * @param string        $htmlname           Name of html select area. Try to start name with "multi" or "search_multi" if this is a multiselect
8588
     * @param array{label:string,data-html:string,disable?:int<0,1>,css?:string}    $array  Array like array(key => value) or array(key=>array('label'=>..., 'data-...'=>..., 'disabled'=>..., 'css'=>...))
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{label:string,data-...?:int<0,1>,css?:string} at position 12 could not be parsed: Expected '}' at position 12, but found 'int'.
Loading history...
8589
     * @param string|string[] $id               Preselected key or array of preselected keys for multiselect. Use 'ifone' to autoselect record if there is only one record.
8590
     * @param int<0,1>|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.
8591
     * @param int<0,1>      $key_in_label       1 to show key into label with format "[key] value"
8592
     * @param int<0,1>      $value_as_key       1 to use value as key
8593
     * @param string        $moreparam          Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container
8594
     * @param int<0,1>      $translate          1=Translate and encode value
8595
     * @param int           $maxlen             Length maximum for labels
8596
     * @param int<0,1>      $disabled           Html select box is disabled
8597
     * @param string        $sort               'ASC' or 'DESC' = Sort on label, '' or 'NONE' or 'POS' = Do not sort, we keep original order
8598
     * @param string        $morecss            Add more class to css styles
8599
     * @param int           $addjscombo         Add js combo
8600
     * @param string        $moreparamonempty   Add more param on the empty option line. Not used if show_empty not set
8601
     * @param int           $disablebademail    1=Check if a not valid email, 2=Check string '---', and if found into value, disable and colorize entry
8602
     * @param int           $nohtmlescape       No html escaping (not recommended, use 'data-html' if you need to use label with HTML content).
8603
     * @return string                           HTML select string.
8604
     * @see multiselectarray(), selectArrayAjax(), selectArrayFilter()
8605
     */
8606
    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 = 'minwidth75', $addjscombo = 1, $moreparamonempty = '', $disablebademail = 0, $nohtmlescape = 0)
8607
    {
8608
        global $conf, $langs;
8609
8610
        // Do we want a multiselect ?
8611
        //$jsbeautify = 0;
8612
        //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
8613
        $jsbeautify = 1;
8614
8615
        if ($value_as_key) {
8616
            $array = array_combine($array, $array);
8617
        }
8618
8619
        '@phan-var-force array{label:string,data-html:string,disable?:int<0,1>,css?:string}	$array'; // Array combine breaks information
8620
8621
        $out = '';
8622
8623
        if ($addjscombo < 0) {
8624
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8625
                $addjscombo = 1;
8626
            } else {
8627
                $addjscombo = 0;
8628
            }
8629
        }
8630
        $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
8631
        $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . ' selectformat"';
8632
        $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
8633
        $out .= '>' . "\n";
8634
8635
        if ($show_empty) {
8636
            $textforempty = ' ';
8637
            if (!empty($conf->use_javascript_ajax)) {
8638
                $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
8639
            }
8640
            if (!is_numeric($show_empty)) {
8641
                $textforempty = $show_empty;
8642
            }
8643
            $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . (((int) $show_empty) < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
8644
        }
8645
        if (is_array($array)) {
8646
            // Translate
8647
            if ($translate) {
8648
                foreach ($array as $key => $value) {
8649
                    if (!is_array($value)) {
8650
                        $array[$key] = $langs->trans($value);
8651
                    } else {
8652
                        $array[$key]['label'] = $langs->trans($value['label']);
8653
                    }
8654
                }
8655
            }
8656
            // Sort
8657
            if ($sort == 'ASC') {
8658
                asort($array);
8659
            } elseif ($sort == 'DESC') {
8660
                arsort($array);
8661
            }
8662
8663
            foreach ($array as $key => $tmpvalue) {
8664
                if (is_array($tmpvalue)) {
8665
                    $value = $tmpvalue['label'];
8666
                    //$valuehtml = empty($tmpvalue['data-html']) ? $value : $tmpvalue['data-html'];
8667
                    $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
8668
                    $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
8669
                } else {
8670
                    $value = $tmpvalue;
8671
                    //$valuehtml = $tmpvalue;
8672
                    $disabled = '';
8673
                    $style = '';
8674
                }
8675
                if (!empty($disablebademail)) {
8676
                    if (
8677
                        ($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
8678
                        || ($disablebademail == 2 && preg_match('/---/', $value))
8679
                    ) {
8680
                        $disabled = ' disabled';
8681
                        $style = ' class="warning"';
8682
                    }
8683
                }
8684
                if ($key_in_label) {
8685
                    if (empty($nohtmlescape)) {
8686
                        $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
8687
                    } else {
8688
                        $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
8689
                    }
8690
                } else {
8691
                    if (empty($nohtmlescape)) {
8692
                        $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
8693
                    } else {
8694
                        $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
8695
                    }
8696
                    if ($value == '' || $value == '-') {
8697
                        $selectOptionValue = '&nbsp;';
8698
                    }
8699
                }
8700
                $out .= '<option value="' . $key . '"';
8701
                $out .= $style . $disabled;
8702
                if (is_array($id)) {
8703
                    if (in_array($key, $id) && !$disabled) {
8704
                        $out .= ' selected'; // To preselect a value
8705
                    }
8706
                } else {
8707
                    $id = (string) $id; // if $id = 0, then $id = '0'
8708
                    if ($id != '' && ($id == $key || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
8709
                        $out .= ' selected'; // To preselect a value
8710
                    }
8711
                }
8712
                if (!empty($nohtmlescape)) {    // deprecated. Use instead the key 'data-html' into input $array, managed at next step to use HTML content.
8713
                    $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
8714
                }
8715
8716
                if (is_array($tmpvalue)) {
8717
                    foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
8718
                        if (preg_match('/^data-/', $keyforvalue)) { // The best solution if you want to use HTML values into the list is to use data-html.
8719
                            $out .= ' ' . dol_escape_htmltag($keyforvalue) . '="' . dol_escape_htmltag($valueforvalue) . '"';
8720
                        }
8721
                    }
8722
                }
8723
                $out .= '>';
8724
                $out .= $selectOptionValue;
8725
                $out .= "</option>\n";
8726
            }
8727
        }
8728
        $out .= "</select>";
8729
8730
        // Add code for jquery to use multiselect
8731
        if ($addjscombo && $jsbeautify) {
8732
            // Enhance with select2
8733
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
8734
            $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', (((int) $show_empty) < 0 ? (string) $show_empty : '-1'), $morecss);
8735
        }
8736
8737
        return $out;
8738
    }
8739
8740
    /**
8741
     *    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.
8742
     *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
8743
     *
8744
     * @param string $htmlname Name of html select area
8745
     * @param string $url Url. Must return a json_encode of array(key=>array('text'=>'A text', 'url'=>'An url'), ...)
8746
     * @param string $id Preselected key
8747
     * @param string $moreparam Add more parameters onto the select tag
8748
     * @param string $moreparamtourl Add more parameters onto the Ajax called URL
8749
     * @param int $disabled Html select box is disabled
8750
     * @param int $minimumInputLength Minimum Input Length
8751
     * @param string $morecss Add more class to css styles
8752
     * @param int $callurlonselect If set to 1, some code is added so an url return by the ajax is called when value is selected.
8753
     * @param string $placeholder String to use as placeholder
8754
     * @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)
8755
     * @return    string                        HTML select string
8756
     * @see selectArrayFilter(), ajax_combobox() in ajax.lib.php
8757
     */
8758
    public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
8759
    {
8760
        global $conf, $langs;
8761
        global $delayedhtmlcontent;    // Will be used later outside of this function
8762
8763
        // TODO Use an internal dolibarr component instead of select2
8764
        if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
8765
            return '';
8766
        }
8767
8768
        $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
8769
8770
        $outdelayed = '';
8771
        if (!empty($conf->use_javascript_ajax)) {
8772
            $tmpplugin = 'select2';
8773
            $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
8774
		    	<script nonce="' . getNonce() . '">
8775
		    	$(document).ready(function () {
8776
8777
	    	        ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
8778
8779
	                $(".' . $htmlname . '").select2({
8780
				    	ajax: {
8781
					    	dir: "ltr",
8782
					    	url: "' . $url . '",
8783
					    	dataType: \'json\',
8784
					    	delay: 250,
8785
					    	data: function (params) {
8786
					    		return {
8787
							    	q: params.term, 	// search term
8788
					    			page: params.page
8789
					    		}
8790
				    		},
8791
				    		processResults: function (data) {
8792
				    			// parse the results into the format expected by Select2.
8793
				    			// since we are using custom formatting functions we do not need to alter the remote JSON data
8794
				    			//console.log(data);
8795
								saveRemoteData = data;
8796
					    	    /* format json result for select2 */
8797
					    	    result = []
8798
					    	    $.each( data, function( key, value ) {
8799
					    	       result.push({id: key, text: value.text});
8800
	                            });
8801
				    			//return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
8802
				    			//console.log(result);
8803
				    			return {results: result, more: false}
8804
				    		},
8805
				    		cache: true
8806
				    	},
8807
		 				language: select2arrayoflanguage,
8808
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
8809
					    placeholder: "' . dol_escape_js($placeholder) . '",
8810
				    	escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
8811
				    	minimumInputLength: ' . ((int) $minimumInputLength) . ',
8812
				        formatResult: function (result, container, query, escapeMarkup) {
8813
	                        return escapeMarkup(result.text);
8814
	                    },
8815
				    });
8816
8817
	                ' . ($callurlonselect ? '
8818
	                /* Code to execute a GET when we select a value */
8819
	                $(".' . $htmlname . '").change(function() {
8820
				    	var selected = $(".' . $htmlname . '").val();
8821
	                	console.log("We select in selectArrayAjax the entry "+selected)
8822
				        $(".' . $htmlname . '").val("");  /* reset visible combo value */
8823
	    			    $.each( saveRemoteData, function( key, value ) {
8824
	    				        if (key == selected)
8825
	    			            {
8826
	    			                 console.log("selectArrayAjax - Do a redirect to "+value.url)
8827
	    			                 location.assign(value.url);
8828
	    			            }
8829
	                    });
8830
	    			});' : '') . '
8831
8832
	    	   });
8833
		       </script>';
8834
        }
8835
8836
        if ($acceptdelayedhtml) {
8837
            $delayedhtmlcontent .= $outdelayed;
8838
        } else {
8839
            $out .= $outdelayed;
8840
        }
8841
        return $out;
8842
    }
8843
8844
    /**
8845
     *  Return a HTML select string, built from an array of key+value, but content returned into select is defined into $array parameter.
8846
     *  Note: Do not apply langs->trans function on returned content of Ajax service, content may be entity encoded twice.
8847
     *
8848
     * @param string    $htmlname               Name of html select area
8849
     * @param array<string,array{text:string,url:string}>   $array  Array (key=>array('text'=>'A text', 'url'=>'An url'), ...)
8850
     * @param string    $id                     Preselected key
8851
     * @param string    $moreparam              Add more parameters onto the select tag
8852
     * @param int<0,1>  $disableFiltering       If set to 1, results are not filtered with searched string
8853
     * @param int<0,1>  $disabled               Html select box is disabled
8854
     * @param int       $minimumInputLength     Minimum Input Length
8855
     * @param string    $morecss                Add more class to css styles
8856
     * @param int<0,1>  $callurlonselect        If set to 1, some code is added so an url return by the ajax is called when value is selected.
8857
     * @param string    $placeholder            String to use as placeholder
8858
     * @param int<0,1>  $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)
8859
     * @param string    $textfortitle           Text to show on title.
8860
     * @return  string                          HTML select string
8861
     * @see selectArrayAjax(), ajax_combobox() in ajax.lib.php
8862
     */
8863
    public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
8864
    {
8865
        global $conf, $langs;
8866
        global $delayedhtmlcontent;    // Will be used later outside of this function
8867
8868
        // TODO Use an internal dolibarr component instead of select2
8869
        if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
8870
            return '';
8871
        }
8872
8873
        $out = '<select type="text"' . ($textfortitle ? ' title="' . dol_escape_htmltag($textfortitle) . '"' : '') . ' id="' . $htmlname . '" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . '"><option></option></select>';
8874
8875
        $formattedarrayresult = array();
8876
8877
        foreach ($array as $key => $value) {
8878
            $o = new stdClass();
8879
            $o->id = $key;
8880
            $o->text = $value['text'];
8881
            $o->url = $value['url'];
8882
            $formattedarrayresult[] = $o;
8883
        }
8884
8885
        $outdelayed = '';
8886
        if (!empty($conf->use_javascript_ajax)) {
8887
            $tmpplugin = 'select2';
8888
            $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
8889
				<script nonce="' . getNonce() . '">
8890
				$(document).ready(function () {
8891
					var data = ' . json_encode($formattedarrayresult) . ';
8892
8893
					' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
8894
8895
					$(".' . $htmlname . '").select2({
8896
						data: data,
8897
						language: select2arrayoflanguage,
8898
						containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
8899
						placeholder: "' . dol_escape_js($placeholder) . '",
8900
						escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
8901
						minimumInputLength: ' . $minimumInputLength . ',
8902
						formatResult: function (result, container, query, escapeMarkup) {
8903
							return escapeMarkup(result.text);
8904
						},
8905
						matcher: function (params, data) {
8906
8907
							if(! data.id) return null;';
8908
8909
            if ($callurlonselect) {
8910
                // We forge the url with 'sall='
8911
                $outdelayed .= '
8912
8913
							var urlBase = data.url;
8914
							var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
8915
							/* console.log("params.term="+params.term); */
8916
							/* console.log("params.term encoded="+encodeURIComponent(params.term)); */
8917
							saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
8918
            }
8919
8920
            if (!$disableFiltering) {
8921
                $outdelayed .= '
8922
8923
							if(data.text.match(new RegExp(params.term))) {
8924
								return data;
8925
							}
8926
8927
							return null;';
8928
            } else {
8929
                $outdelayed .= '
8930
8931
							return data;';
8932
            }
8933
8934
            $outdelayed .= '
8935
						}
8936
					});
8937
8938
					' . ($callurlonselect ? '
8939
					/* Code to execute a GET when we select a value */
8940
					$(".' . $htmlname . '").change(function() {
8941
						var selected = $(".' . $htmlname . '").val();
8942
						console.log("We select "+selected)
8943
8944
						$(".' . $htmlname . '").val("");  /* reset visible combo value */
8945
						$.each( saveRemoteData, function( key, value ) {
8946
							if (key == selected)
8947
							{
8948
								console.log("selectArrayFilter - Do a redirect to "+value.url)
8949
								location.assign(value.url);
8950
							}
8951
						});
8952
					});' : '') . '
8953
8954
				});
8955
				</script>';
8956
        }
8957
8958
        if ($acceptdelayedhtml) {
8959
            $delayedhtmlcontent .= $outdelayed;
8960
        } else {
8961
            $out .= $outdelayed;
8962
        }
8963
        return $out;
8964
    }
8965
8966
    /**
8967
     * Show a multiselect form from an array. WARNING: Use this only for short lists.
8968
     *
8969
     * @param   string      $htmlname       Name of select
8970
     * @param   array<string,string|array{id:string,label:string,color:string,picto:string,labelhtml:string}>   $array          Array(key=>value) or Array(key=>array('id'=>key, 'label'=>value, 'color'=> , 'picto'=> , 'labelhtml'=> ))
8971
     * @param   string[]    $selected       Array of keys preselected
8972
     * @param   int<0,1>    $key_in_label   1 to show key like in "[key] value"
8973
     * @param   int<0,1>    $value_as_key   1 to use value as key
8974
     * @param   string      $morecss        Add more css style
8975
     * @param   int<0,1>    $translate      Translate and encode value
8976
     * @param   int|string  $width          Force width of select box. May be used only when using jquery couch. Example: 250, '95%'
8977
     * @param   string      $moreattrib     Add more options on select component. Example: 'disabled'
8978
     * @param   string      $elemtype       Type of element we show ('category', ...). Will execute a formatting function on it. To use in readonly mode if js component support HTML formatting.
8979
     * @param   string      $placeholder    String to use as placeholder
8980
     * @param   int<-1,1>   $addjscombo     Add js combo
8981
     * @return  string                      HTML multiselect string
8982
     * @see selectarray(), selectArrayAjax(), selectArrayFilter()
8983
     */
8984
    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)
8985
    {
8986
        global $conf, $langs;
8987
8988
        $out = '';
8989
8990
        if ($addjscombo < 0) {
8991
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8992
                $addjscombo = 1;
8993
            } else {
8994
                $addjscombo = 0;
8995
            }
8996
        }
8997
8998
        $useenhancedmultiselect = 0;
8999
        if (!empty($conf->use_javascript_ajax) && !defined('MAIN_DO_NOT_USE_JQUERY_MULTISELECT') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
9000
            if ($addjscombo) {
9001
                $useenhancedmultiselect = 1;    // Use the js multiselect in one line. Possible only if $addjscombo not 0.
9002
            }
9003
        }
9004
9005
        // We need a hidden field because when using the multiselect, if we unselect all, there is no
9006
        // variable submitted at all, so no way to make a difference between variable not submitted and variable
9007
        // submitted to nothing.
9008
        $out .= '<input type="hidden" name="' . $htmlname . '_multiselect" value="1">';
9009
        // Output select component
9010
        $out .= '<select id="' . $htmlname . '" class="multiselect' . ($useenhancedmultiselect ? ' multiselectononeline' : '') . ($morecss ? ' ' . $morecss : '') . '" multiple name="' . $htmlname . '[]"' . ($moreattrib ? ' ' . $moreattrib : '') . ($width ? ' style="width: ' . (preg_match('/%/', (string) $width) ? $width : $width . 'px') . '"' : '') . '>' . "\n";
9011
        if (is_array($array) && !empty($array)) {
9012
            if ($value_as_key) {
9013
                $array = array_combine($array, $array);
9014
            }
9015
9016
            if (!empty($array)) {
9017
                foreach ($array as $key => $value) {
9018
                    $tmpkey = $key;
9019
                    $tmpvalue = $value;
9020
                    $tmpcolor = '';
9021
                    $tmppicto = '';
9022
                    $tmplabelhtml = '';
9023
                    if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
9024
                        $tmpkey = $value['id'];
9025
                        $tmpvalue = empty($value['label']) ? '' : $value['label'];
9026
                        $tmpcolor = empty($value['color']) ? '' : $value['color'];
9027
                        $tmppicto = empty($value['picto']) ? '' : $value['picto'];
9028
                        $tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
9029
                    }
9030
                    $newval = ($translate ? $langs->trans($tmpvalue) : $tmpvalue);
9031
                    $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
9032
9033
                    $out .= '<option value="' . $tmpkey . '"';
9034
                    if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
9035
                        $out .= ' selected';
9036
                    }
9037
                    if (!empty($tmplabelhtml)) {
9038
                        $out .= ' data-html="' . dol_escape_htmltag($tmplabelhtml, 0, 0, '', 0, 1) . '"';
9039
                    } else {
9040
                        $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
9041
                        $out .= ' data-html="' . dol_escape_htmltag($tmplabelhtml, 0, 0, '', 0, 1) . '"';
9042
                    }
9043
                    $out .= '>';
9044
                    $out .= dol_htmlentitiesbr($newval);
9045
                    $out .= '</option>' . "\n";
9046
                }
9047
            }
9048
        }
9049
        $out .= '</select>' . "\n";
9050
9051
        // Add code for jquery to use multiselect
9052
        if (!empty($conf->use_javascript_ajax) && getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT')) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($conf->use_java...RE_JQUERY_MULTISELECT'), Probably Intended Meaning: ! empty($conf->use_javas...E_JQUERY_MULTISELECT'))
Loading history...
9053
            $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
9054
            $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
9055
            if ($addjscombo == 1) {
9056
                $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
9057
                $out .= 'function formatResult(record, container) {' . "\n";
9058
                // If property data-html set, we decode html entities and use this.
9059
                // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
9060
                $out .= '	if ($(record.element).attr("data-html") != undefined && typeof htmlEntityDecodeJs === "function") {';
9061
                //$out .= '     console.log("aaa");';
9062
                $out .= '		return htmlEntityDecodeJs($(record.element).attr("data-html"));';
9063
                $out .= '	}' . "\n";
9064
                $out .= '	return record.text;';
9065
                $out .= '}' . "\n";
9066
                $out .= 'function formatSelection(record) {' . "\n";
9067
                if ($elemtype == 'category') {
9068
                    $out .= 'return \'<span><img src="' . constant('DOL_URL_ROOT') . '/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
9069
                } else {
9070
                    $out .= 'return record.text;';
9071
                }
9072
                $out .= '}' . "\n";
9073
                $out .= '$(document).ready(function () {
9074
							$(\'#' . $htmlname . '\').' . $tmpplugin . '({';
9075
                if ($placeholder) {
9076
                    $out .= '
9077
								placeholder: {
9078
								    id: \'-1\',
9079
								    text: \'' . dol_escape_js($placeholder) . '\'
9080
								  },';
9081
                }
9082
                $out .= '		dir: \'ltr\',
9083
								containerCssClass: \':all:\',					/* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
9084
								dropdownCssClass: \'' . $morecss . '\',				/* Line to add class on the new <span class="select2-selection...> tag (ok with multiselect). Need full version of select2. */
9085
								// Specify format function for dropdown item
9086
								formatResult: formatResult,
9087
							 	templateResult: formatResult,		/* For 4.0 */
9088
								escapeMarkup: function (markup) { return markup; }, 	// let our custom formatter work
9089
								// Specify format function for selected item
9090
								formatSelection: formatSelection,
9091
							 	templateSelection: formatSelection		/* For 4.0 */
9092
							});
9093
9094
							/* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
9095
								 the size only if component is not hidden by default on load */
9096
							$(\'#' . $htmlname . ' + .select2\').addClass(\'' . $morecss . '\');
9097
						});' . "\n";
9098
            } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
9099
                // Add other js lib
9100
                // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
9101
                // ...
9102
                $out .= 'console.log(\'addjscombo=2 for htmlname=' . $htmlname . '\');';
9103
                $out .= '$(document).ready(function () {
9104
							$(\'#' . $htmlname . '\').multiSelect({
9105
								containerHTML: \'<div class="multi-select-container">\',
9106
								menuHTML: \'<div class="multi-select-menu">\',
9107
								buttonHTML: \'<span class="multi-select-button ' . $morecss . '">\',
9108
								menuItemHTML: \'<label class="multi-select-menuitem">\',
9109
								activeClass: \'multi-select-container--open\',
9110
								noneText: \'' . $placeholder . '\'
9111
							});
9112
						})';
9113
            }
9114
            $out .= '</script>';
9115
        }
9116
9117
        return $out;
9118
    }
9119
9120
9121
    /**
9122
     * Show a multiselect dropbox from an array.
9123
     * If a saved selection of fields exists for user (into $user->conf->MAIN_SELECTEDFIELDS_contextofpage), we use this one instead of default.
9124
     *
9125
     * @param string    $htmlname   Name of HTML field
9126
     * @param array<string,array{label:string,checked:string,enabled?:string,type?:string,langfile?:string}>    $array  Array with array of fields we could show. This array may be modified according to setup of user.
9127
     * @param string    $varpage    Id of context for page. Can be set by caller with $varpage=(empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage);
9128
     * @param string    $pos        Position colon on liste value 'left' or '' (meaning 'right').
9129
     * @return string               HTML multiselect string
9130
     * @see selectarray()
9131
     */
9132
    public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '')
9133
    {
9134
        global $langs, $user;
9135
9136
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
9137
            return '';
9138
        }
9139
        if (empty($array)) {
9140
            return '';
9141
        }
9142
9143
        $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
9144
9145
        if (!empty($user->conf->$tmpvar)) {        // A list of fields was already customized for user
9146
            $tmparray = explode(',', $user->conf->$tmpvar);
9147
            foreach ($array as $key => $val) {
9148
                //var_dump($key);
9149
                //var_dump($tmparray);
9150
                if (in_array($key, $tmparray)) {
9151
                    $array[$key]['checked'] = 1;
9152
                } else {
9153
                    $array[$key]['checked'] = 0;
9154
                }
9155
            }
9156
        } else {                                // There is no list of fields already customized for user
9157
            foreach ($array as $key => $val) {
9158
                if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
9159
                    $array[$key]['checked'] = 0;
9160
                }
9161
            }
9162
        }
9163
9164
        $listoffieldsforselection = '';
9165
        $listcheckedstring = '';
9166
9167
        foreach ($array as $key => $val) {
9168
            // var_dump($val);
9169
            // var_dump(array_key_exists('enabled', $val));
9170
            // var_dump(!$val['enabled']);
9171
            if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
9172
                unset($array[$key]); // We don't want this field
9173
                continue;
9174
            }
9175
            if (!empty($val['type']) && $val['type'] == 'separate') {
9176
                // Field remains in array but we don't add it into $listoffieldsforselection
9177
                //$listoffieldsforselection .= '<li>-----</li>';
9178
                continue;
9179
            }
9180
            if (!empty($val['label']) && $val['label']) {
9181
                if (!empty($val['langfile']) && is_object($langs)) {
9182
                    $langs->load($val['langfile']);
9183
                }
9184
9185
                // Note: $val['checked'] <> 0 means we must show the field into the combo list  @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
9186
                $listoffieldsforselection .= '<li><input type="checkbox" id="checkbox' . $key . '" value="' . $key . '"' . ((!array_key_exists('checked', $val) || empty($val['checked']) || $val['checked'] == '-1') ? '' : ' checked="checked"') . '/><label for="checkbox' . $key . '">' . dol_escape_htmltag($langs->trans($val['label'])) . '</label></li>';
9187
                $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
9188
            }
9189
        }
9190
9191
        $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
9192
9193
        <dl class="dropdown">
9194
            <dt>
9195
            <a href="#' . $htmlname . '">
9196
              ' . img_picto('', 'list') . '
9197
            </a>
9198
            <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
9199
            </dt>
9200
            <dd class="dropdowndd">
9201
                <div class="multiselectcheckbox' . $htmlname . '">
9202
                    <ul class="' . $htmlname . ($pos == '1' ? 'left' : '') . '">
9203
                    <li><input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="' . $langs->trans('Search') . '"></li>
9204
                    ' . $listoffieldsforselection . '
9205
                    </ul>
9206
                </div>
9207
            </dd>
9208
        </dl>
9209
9210
        <script nonce="' . getNonce() . '" type="text/javascript">
9211
          jQuery(document).ready(function () {
9212
              $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on(\'click\', function () {
9213
                  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
9214
9215
                  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\');	// Update field so we know we changed something on selected fields after POST
9216
9217
                  var title = $(this).val() + ",";
9218
                  if ($(this).is(\':checked\')) {
9219
                      $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
9220
                  }
9221
                  else {
9222
                      $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
9223
                  }
9224
                  // Now, we submit page
9225
                  //$(this).parents(\'form:first\').submit();
9226
              });
9227
              $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
9228
			    var value = $(this).val().toLowerCase();
9229
			    $(\'.multiselectcheckbox' . $htmlname . ' li > label\').filter(function() {
9230
			      $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
9231
			    });
9232
			  });
9233
9234
9235
           });
9236
        </script>
9237
9238
        ';
9239
        return $out;
9240
    }
9241
9242
    /**
9243
     * Render list of categories linked to object with id $id and type $type
9244
     *
9245
     * @param int       $id         Id of object
9246
     * @param string    $type       Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
9247
     * @param int<0,1>  $rendermode 0=Default, use multiselect. 1=Emulate multiselect (recommended)
9248
     * @param int<0,1>  $nolink     1=Do not add html links
9249
     * @return string               String with categories
9250
     */
9251
    public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
9252
    {
9253
        $cat = new Categorie($this->db);
9254
        $categories = $cat->containing($id, $type);
9255
9256
        if ($rendermode == 1) {
9257
            $toprint = array();
9258
            foreach ($categories as $c) {
9259
                $ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
9260
                foreach ($ways as $way) {
9261
                    $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . $way . '</li>';
9262
                }
9263
            }
9264
            return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
9265
        }
9266
9267
        if ($rendermode == 0) {
9268
            $arrayselected = array();
9269
            $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 3);
9270
            foreach ($categories as $c) {
9271
                $arrayselected[] = $c->id;
9272
            }
9273
9274
            return $this->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, '', 0, '100%', 'disabled', 'category');
9275
        }
9276
9277
        return 'ErrorBadValueForParameterRenderMode'; // Should not happened
9278
    }
9279
9280
    /**
9281
     *  Show linked object block.
9282
     *
9283
     * @param   CommonObject    $object                         Object we want to show links to
9284
     * @param   string          $morehtmlright                  More html to show on right of title
9285
     * @param   array<int,string>   $compatibleImportElementsList   Array of compatibles elements object for "import from" action
9286
     * @param   string          $title                          Title
9287
     * @return  int                                             Return Number of different types
9288
     */
9289
    public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = array(), $title = 'RelatedObjects')
9290
    {
9291
        global $conf, $langs, $hookmanager;
9292
        global $bc, $action;
9293
9294
        $object->fetchObjectLinked();
9295
9296
        // Bypass the default method
9297
        $hookmanager->initHooks(array('commonobject'));
9298
        $parameters = array(
9299
            'morehtmlright' => $morehtmlright,
9300
            'compatibleImportElementsList' => &$compatibleImportElementsList,
9301
        );
9302
        $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
9303
9304
        $nbofdifferenttypes = count($object->linkedObjects);
9305
9306
        if (empty($reshook)) {
9307
            print '<!-- showLinkedObjectBlock -->';
9308
            print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
9309
9310
9311
            print '<div class="div-table-responsive-no-min">';
9312
            print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '"  data-elementid="' . $object->id . '"   >';
9313
9314
            print '<tr class="liste_titre">';
9315
            print '<td>' . $langs->trans("Type") . '</td>';
9316
            print '<td>' . $langs->trans("Ref") . '</td>';
9317
            print '<td class="center"></td>';
9318
            print '<td class="center">' . $langs->trans("Date") . '</td>';
9319
            print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
9320
            print '<td class="right">' . $langs->trans("Status") . '</td>';
9321
            print '<td></td>';
9322
            print '</tr>';
9323
9324
            $nboftypesoutput = 0;
9325
9326
            foreach ($object->linkedObjects as $objecttype => $objects) {
9327
                $tplpath = $element = $subelement = $objecttype;
9328
9329
                // to display import button on tpl
9330
                $showImportButton = false;
9331
                if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
9332
                    $showImportButton = true;
9333
                }
9334
9335
                $regs = array();
9336
                if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
9337
                    $element = $regs[1];
9338
                    $subelement = $regs[2];
9339
                    $tplpath = $element . '/' . $subelement;
9340
                }
9341
                $tplname = 'linkedobjectblock';
9342
9343
                // To work with non standard path
9344
                if ($objecttype == 'facture') {
9345
                    $tplpath = 'compta/' . $element;
9346
                    if (!isModEnabled('invoice')) {
9347
                        continue; // Do not show if module disabled
9348
                    }
9349
                } elseif ($objecttype == 'facturerec') {
9350
                    $tplpath = 'compta/facture';
9351
                    $tplname = 'linkedobjectblockForRec';
9352
                    if (!isModEnabled('invoice')) {
9353
                        continue; // Do not show if module disabled
9354
                    }
9355
                } elseif ($objecttype == 'propal') {
9356
                    $tplpath = 'comm/' . $element;
9357
                    if (!isModEnabled('propal')) {
9358
                        continue; // Do not show if module disabled
9359
                    }
9360
                } elseif ($objecttype == 'supplier_proposal') {
9361
                    if (!isModEnabled('supplier_proposal')) {
9362
                        continue; // Do not show if module disabled
9363
                    }
9364
                } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
9365
                    $tplpath = 'expedition';
9366
                    if (!isModEnabled('shipping')) {
9367
                        continue; // Do not show if module disabled
9368
                    }
9369
                } elseif ($objecttype == 'reception') {
9370
                    $tplpath = 'reception';
9371
                    if (!isModEnabled('reception')) {
9372
                        continue; // Do not show if module disabled
9373
                    }
9374
                } elseif ($objecttype == 'delivery') {
9375
                    $tplpath = 'delivery';
9376
                    if (!getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) {
9377
                        continue; // Do not show if sub module disabled
9378
                    }
9379
                } elseif ($objecttype == 'ficheinter') {
9380
                    $tplpath = 'fichinter';
9381
                    if (!isModEnabled('intervention')) {
9382
                        continue; // Do not show if module disabled
9383
                    }
9384
                } elseif ($objecttype == 'invoice_supplier') {
9385
                    $tplpath = 'fourn/facture';
9386
                } elseif ($objecttype == 'order_supplier') {
9387
                    $tplpath = 'fourn/commande';
9388
                } elseif ($objecttype == 'expensereport') {
9389
                    $tplpath = 'expensereport';
9390
                } elseif ($objecttype == 'subscription') {
9391
                    $tplpath = 'adherents';
9392
                } elseif ($objecttype == 'conferenceorbooth') {
9393
                    $tplpath = 'eventorganization';
9394
                } elseif ($objecttype == 'conferenceorboothattendee') {
9395
                    $tplpath = 'eventorganization';
9396
                } elseif ($objecttype == 'mo') {
9397
                    $tplpath = 'mrp';
9398
                    if (!isModEnabled('mrp')) {
9399
                        continue; // Do not show if module disabled
9400
                    }
9401
                }
9402
9403
                global $linkedObjectBlock;
9404
                $linkedObjectBlock = $objects;
9405
9406
                // Output template part (modules that overwrite templates must declare this into descriptor)
9407
                $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
9408
                foreach ($dirtpls as $reldir) {
9409
                    $reldir = rtrim($reldir, '/');
9410
                    if ($nboftypesoutput == ($nbofdifferenttypes - 1)) {    // No more type to show after
9411
                        global $noMoreLinkedObjectBlockAfter;
9412
                        $noMoreLinkedObjectBlockAfter = 1;
9413
                    }
9414
9415
                    $res = @include dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
9416
                    if ($res) {
9417
                        $nboftypesoutput++;
9418
                        break;
9419
                    }
9420
                }
9421
            }
9422
9423
            if (!$nboftypesoutput) {
9424
                print '<tr><td class="impair" colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
9425
            }
9426
9427
            print '</table>';
9428
9429
            if (!empty($compatibleImportElementsList)) {
9430
                $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
9431
            }
9432
9433
            print '</div>';
9434
        }
9435
9436
        return $nbofdifferenttypes;
9437
    }
9438
9439
    /**
9440
     *  Show block with links to link to other objects.
9441
     *
9442
     * @param   CommonObject    $object             Object we want to show links to
9443
     * @param   string[]            $restrictlinksto    Restrict links to some elements, for example array('order') or array('supplier_order'). null or array() if no restriction.
9444
     * @param   string[]            $excludelinksto     Do not show links of this type, for example array('order') or array('supplier_order'). null or array() if no exclusion.
9445
     * @return  string                              HTML block
9446
     */
9447
    public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
9448
    {
9449
        global $conf, $langs, $hookmanager;
9450
        global $action;
9451
9452
        $linktoelem = '';
9453
        $linktoelemlist = '';
9454
        $listofidcompanytoscan = '';
9455
9456
        if (!is_object($object->thirdparty)) {
9457
            $object->fetch_thirdparty();
9458
        }
9459
9460
        $possiblelinks = array();
9461
        if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
9462
            $listofidcompanytoscan = $object->thirdparty->id;
9463
            if (($object->thirdparty->parent > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PARENT_IN_LINKTO')) {
9464
                $listofidcompanytoscan .= ',' . $object->thirdparty->parent;
9465
            }
9466
            if (($object->fk_project > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO')) {
9467
                $tmpproject = new Project($this->db);
9468
                $tmpproject->fetch($object->fk_project);
9469
                if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) {
9470
                    $listofidcompanytoscan .= ',' . $tmpproject->socid;
9471
                }
9472
                unset($tmpproject);
9473
            }
9474
9475
            $possiblelinks = array(
9476
                'propal' => array(
9477
                    'enabled' => isModEnabled('propal'),
9478
                    'perms' => 1,
9479
                    'label' => 'LinkToProposal',
9480
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "propal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('propal') . ')'),
9481
                'shipping' => array(
9482
                    'enabled' => isModEnabled('shipping'),
9483
                    'perms' => 1,
9484
                    'label' => 'LinkToExpedition',
9485
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "expedition as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('shipping') . ')'),
9486
                'order' => array(
9487
                    'enabled' => isModEnabled('order'),
9488
                    'perms' => 1,
9489
                    'label' => 'LinkToOrder',
9490
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "commande as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('commande') . ')'),
9491
                'invoice' => array(
9492
                    'enabled' => isModEnabled('invoice'),
9493
                    'perms' => 1,
9494
                    'label' => 'LinkToInvoice',
9495
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('invoice') . ')'),
9496
                'invoice_template' => array(
9497
                    'enabled' => isModEnabled('invoice'),
9498
                    'perms' => 1,
9499
                    'label' => 'LinkToTemplateInvoice',
9500
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.titre as ref, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture_rec as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('invoice') . ')'),
9501
                'contrat' => array(
9502
                    'enabled' => isModEnabled('contract'),
9503
                    'perms' => 1,
9504
                    'label' => 'LinkToContract',
9505
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_customer as ref_client, t.ref_supplier, SUM(td.total_ht) as total_ht
9506
							FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "contrat as t, " . $this->db->prefix() . "contratdet as td WHERE t.fk_soc = s.rowid AND td.fk_contrat = t.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('contract') . ') GROUP BY s.rowid, s.nom, s.client, t.rowid, t.ref, t.ref_customer, t.ref_supplier'
9507
                ),
9508
                'fichinter' => array(
9509
                    'enabled' => isModEnabled('intervention'),
9510
                    'perms' => 1,
9511
                    'label' => 'LinkToIntervention',
9512
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "fichinter as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('intervention') . ')'),
9513
                'supplier_proposal' => array(
9514
                    'enabled' => isModEnabled('supplier_proposal'),
9515
                    'perms' => 1,
9516
                    'label' => 'LinkToSupplierProposal',
9517
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, '' as ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "supplier_proposal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('supplier_proposal') . ')'),
9518
                'order_supplier' => array(
9519
                    'enabled' => isModEnabled("supplier_order"),
9520
                    'perms' => 1,
9521
                    'label' => 'LinkToSupplierOrder',
9522
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('commande_fournisseur') . ')'),
9523
                'invoice_supplier' => array(
9524
                    'enabled' => isModEnabled("supplier_invoice"),
9525
                    'perms' => 1, 'label' => 'LinkToSupplierInvoice',
9526
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('facture_fourn') . ')'),
9527
                'ticket' => array(
9528
                    'enabled' => isModEnabled('ticket'),
9529
                    'perms' => 1,
9530
                    'label' => 'LinkToTicket',
9531
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.track_id, '0' as total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "ticket as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('ticket') . ')'),
9532
                'mo' => array(
9533
                    'enabled' => isModEnabled('mrp'),
9534
                    'perms' => 1,
9535
                    'label' => 'LinkToMo',
9536
                    'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM " . $this->db->prefix() . "societe as s INNER JOIN " . $this->db->prefix() . "mrp_mo as t ON t.fk_soc = s.rowid  WHERE  t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('mo') . ')')
9537
            );
9538
        }
9539
9540
        if ($object->table_element == 'commande_fournisseur') {
9541
            $possiblelinks['mo']['sql'] = "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM " . $this->db->prefix() . "societe as s INNER JOIN " . $this->db->prefix() . 'mrp_mo as t ON t.fk_soc = s.rowid  WHERE t.entity IN (' . getEntity('mo') . ')';
9542
        } elseif ($object->table_element == 'mrp_mo') {
9543
            $possiblelinks['order_supplier']['sql'] = "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . 'commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.entity IN (' . getEntity('commande_fournisseur') . ')';
9544
        }
9545
9546
        $reshook = 0; // Ensure $reshook is defined for static analysis
9547
        if (!empty($listofidcompanytoscan)) {  // If empty, we don't have criteria to scan the object we can link to
9548
            // Can complete the possiblelink array
9549
            $hookmanager->initHooks(array('commonobject'));
9550
            $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
9551
            $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
9552
        }
9553
9554
        if (empty($reshook)) {
9555
            if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
9556
                $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
9557
            }
9558
        } elseif ($reshook > 0) {
9559
            if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
9560
                $possiblelinks = $hookmanager->resArray;
9561
            }
9562
        }
9563
9564
        foreach ($possiblelinks as $key => $possiblelink) {
9565
            $num = 0;
9566
9567
            if (empty($possiblelink['enabled'])) {
9568
                continue;
9569
            }
9570
9571
            if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
9572
                print '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
9573
9574
                if (getDolGlobalString('MAIN_LINK_BY_REF_IN_LINKTO')) {
9575
                    print '<br>' . "\n";
9576
                    print '<!-- form to add a link from anywhere -->' . "\n";
9577
                    print '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
9578
                    print '<input type="hidden" name="id" value="' . $object->id . '">';
9579
                    print '<input type="hidden" name="action" value="addlinkbyref">';
9580
                    print '<input type="hidden" name="token" value="' . newToken() . '">';
9581
                    print '<input type="hidden" name="addlink" value="' . $key . '">';
9582
                    print '<table class="noborder">';
9583
                    print '<tr>';
9584
                    //print '<td>' . $langs->trans("Ref") . '</td>';
9585
                    print '<td class="center"><input type="text" placeholder="' . dol_escape_htmltag($langs->trans("Ref")) . '" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">&nbsp;';
9586
                    print '<input type="submit" class="button small valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
9587
                    print '<input type="submit" class="button small" name="cancel" value="' . $langs->trans('Cancel') . '"></td>';
9588
                    print '</tr>';
9589
                    print '</table>';
9590
                    print '</form>';
9591
                }
9592
9593
                $sql = $possiblelink['sql'];
9594
9595
                $resqllist = $this->db->query($sql);
9596
                if ($resqllist) {
9597
                    $num = $this->db->num_rows($resqllist);
9598
                    $i = 0;
9599
9600
                    print '<br>';
9601
                    print '<!-- form to add a link from object to same thirdparty -->' . "\n";
9602
                    print '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
9603
                    print '<input type="hidden" name="action" value="addlink">';
9604
                    print '<input type="hidden" name="token" value="' . newToken() . '">';
9605
                    print '<input type="hidden" name="id" value="' . $object->id . '">';
9606
                    print '<input type="hidden" name="addlink" value="' . $key . '">';
9607
                    print '<table class="noborder">';
9608
                    print '<tr class="liste_titre">';
9609
                    print '<td class="nowrap"></td>';
9610
                    print '<td class="center">' . $langs->trans("Ref") . '</td>';
9611
                    print '<td class="left">' . $langs->trans("RefCustomer") . '</td>';
9612
                    print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
9613
                    print '<td class="left">' . $langs->trans("Company") . '</td>';
9614
                    print '</tr>';
9615
                    while ($i < $num) {
9616
                        $objp = $this->db->fetch_object($resqllist);
9617
9618
                        print '<tr class="oddeven">';
9619
                        print '<td class="left">';
9620
                        print '<input type="radio" name="idtolinkto" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
9621
                        print '</td>';
9622
                        print '<td class="center"><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
9623
                        print '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
9624
                        print '<td class="right">';
9625
                        if ($possiblelink['label'] == 'LinkToContract') {
9626
                            $form = new Form($this->db);
9627
                            print $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
9628
                        }
9629
                        print '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
9630
                        print '</td>';
9631
                        print '<td>' . $objp->name . '</td>';
9632
                        print '</tr>';
9633
                        $i++;
9634
                    }
9635
                    print '</table>';
9636
                    print '<div class="center">';
9637
                    if ($num) {
9638
                        print '<input type="submit" class="button valignmiddle marginleftonly marginrightonly small" value="' . $langs->trans('ToLink') . '">';
9639
                    }
9640
                    if (empty($conf->use_javascript_ajax)) {
9641
                        print '<input type="submit" class="button button-cancel marginleftonly marginrightonly small" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
9642
                    } else {
9643
                        print '<input type="submit" onclick="jQuery(\'#' . $key . 'list\').toggle(); return false;" class="button button-cancel marginleftonly marginrightonly small" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
9644
                    }
9645
                    print '</form>';
9646
                    $this->db->free($resqllist);
9647
                } else {
9648
                    dol_print_error($this->db);
9649
                }
9650
                print '</div>';
9651
9652
                //$linktoelem.=($linktoelem?' &nbsp; ':'');
9653
                if ($num > 0 || getDolGlobalString('MAIN_LINK_BY_REF_IN_LINKTO')) {
9654
                    $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
9655
                    // } else $linktoelem.=$langs->trans($possiblelink['label']);
9656
                } else {
9657
                    $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
9658
                }
9659
            }
9660
        }
9661
9662
        if ($linktoelemlist) {
9663
            $linktoelem = '
9664
    		<dl class="dropdown" id="linktoobjectname">
9665
    		';
9666
            if (!empty($conf->use_javascript_ajax)) {
9667
                $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
9668
            }
9669
            $linktoelem .= '<dd>
9670
    		<div class="multiselectlinkto">
9671
    		<ul class="ulselectedfields">' . $linktoelemlist . '
9672
    		</ul>
9673
    		</div>
9674
    		</dd>
9675
    		</dl>';
9676
        } else {
9677
            $linktoelem = '';
9678
        }
9679
9680
        if (!empty($conf->use_javascript_ajax)) {
9681
            print '<!-- Add js to show linkto box -->
9682
				<script nonce="' . getNonce() . '">
9683
				jQuery(document).ready(function() {
9684
					jQuery(".linkto").click(function() {
9685
						console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
9686
					    jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
9687
					});
9688
				});
9689
				</script>
9690
		    ';
9691
        }
9692
9693
        return $linktoelem;
9694
    }
9695
9696
    /**
9697
     *    Return an html string with a select combo box to choose yes or no
9698
     *
9699
     * @param string    $htmlname       Name of html select field
9700
     * @param string    $value          Pre-selected value
9701
     * @param int       $option         0 return yes/no, 1 return 1/0
9702
     * @param bool      $disabled       true or false
9703
     * @param int       $useempty       1=Add empty line
9704
     * @param int       $addjscombo     1=Add js beautifier on combo box
9705
     * @param string    $morecss        More CSS
9706
     * @param string    $labelyes       Label for Yes
9707
     * @param string    $labelno        Label for No
9708
     * @return    string                See option
9709
     */
9710
    public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = '', $labelyes = 'Yes', $labelno = 'No')
9711
    {
9712
        global $langs;
9713
9714
        $yes = "yes";
9715
        $no = "no";
9716
        if ($option) {
9717
            $yes = "1";
9718
            $no = "0";
9719
        }
9720
9721
        $disabled = ($disabled ? ' disabled' : '');
9722
9723
        $resultyesno = '<select class="flat width75' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
9724
        if ($useempty) {
9725
            $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
9726
        }
9727
        if (("$value" == 'yes') || ($value == 1)) {
9728
            $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
9729
            $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
9730
        } else {
9731
            $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
9732
            $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
9733
            $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
9734
        }
9735
        $resultyesno .= '</select>' . "\n";
9736
9737
        if ($addjscombo) {
9738
            $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
9739
        }
9740
9741
        return $resultyesno;
9742
    }
9743
9744
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9745
9746
    /**
9747
     *  Return list of export templates
9748
     *
9749
     * @param string $selected Id modele pre-selectionne
9750
     * @param string $htmlname Name of HTML select
9751
     * @param string $type Type of searched templates
9752
     * @param int $useempty Affiche valeur vide dans liste
9753
     * @return    void
9754
     */
9755
    public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
9756
    {
9757
		// phpcs:enable
9758
        $sql = "SELECT rowid, label";
9759
        $sql .= " FROM " . $this->db->prefix() . "export_model";
9760
        $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
9761
        $sql .= " ORDER BY rowid";
9762
        $result = $this->db->query($sql);
9763
        if ($result) {
9764
            print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
9765
            if ($useempty) {
9766
                print '<option value="-1">&nbsp;</option>';
9767
            }
9768
9769
            $num = $this->db->num_rows($result);
9770
            $i = 0;
9771
            while ($i < $num) {
9772
                $obj = $this->db->fetch_object($result);
9773
                if ($selected == $obj->rowid) {
9774
                    print '<option value="' . $obj->rowid . '" selected>';
9775
                } else {
9776
                    print '<option value="' . $obj->rowid . '">';
9777
                }
9778
                print $obj->label;
9779
                print '</option>';
9780
                $i++;
9781
            }
9782
            print "</select>";
9783
        } else {
9784
            dol_print_error($this->db);
9785
        }
9786
    }
9787
9788
    /**
9789
     * Return a HTML area with the reference of object and a navigation bar for a business object
9790
     * Note: To complete search with a particular filter on select, you can set $object->next_prev_filter set to define SQL criteria.
9791
     *
9792
     * @param CommonObject  $object         Object to show.
9793
     * @param string    $paramid        Name of parameter to use to name the id into the URL next/previous link.
9794
     * @param string    $morehtml       More html content to output just before the nav bar.
9795
     * @param int<0,1>  $shownav        Show Condition (navigation is shown if value is 1).
9796
     * @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.
9797
     * @param string    $fieldref       Name of field ref of object (object->ref) to show or 'none' to not show ref.
9798
     * @param string    $morehtmlref    More html to show after ref.
9799
     * @param string    $moreparam      More param to add in nav link url. Must start with '&...'.
9800
     * @param int<0,1>  $nodbprefix     Do not include DB prefix to forge table name.
9801
     * @param string    $morehtmlleft   More html code to show before ref.
9802
     * @param string    $morehtmlstatus More html code to show under navigation arrows (status place).
9803
     * @param string    $morehtmlright  More html code to show after ref.
9804
     * @return string                   Portion HTML with ref + navigation buttons
9805
     */
9806
    public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
9807
    {
9808
        global $conf, $langs, $hookmanager, $extralanguages;
9809
9810
        $ret = '';
9811
        if (empty($fieldid)) {
9812
            $fieldid = 'rowid';
9813
        }
9814
        if (empty($fieldref)) {
9815
            $fieldref = 'ref';
9816
        }
9817
9818
        // Preparing gender's display if there is one
9819
        $addgendertxt = '';
9820
        if (property_exists($object, 'gender') && !empty($object->gender)) {
0 ignored issues
show
Bug Best Practice introduced by
The property gender does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
9821
            $addgendertxt = ' ';
9822
            switch ($object->gender) {
9823
                case 'man':
9824
                    $addgendertxt .= '<i class="fas fa-mars"></i>';
9825
                    break;
9826
                case 'woman':
9827
                    $addgendertxt .= '<i class="fas fa-venus"></i>';
9828
                    break;
9829
                case 'other':
9830
                    $addgendertxt .= '<i class="fas fa-transgender"></i>';
9831
                    break;
9832
            }
9833
        }
9834
9835
        // Add where from hooks
9836
        if (is_object($hookmanager)) {
9837
            $parameters = array('showrefnav' => true);
9838
            $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
9839
            $object->next_prev_filter .= $hookmanager->resPrint;
9840
        }
9841
9842
        $previous_ref = $next_ref = '';
9843
        if ($shownav) {
9844
            //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
9845
            $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
9846
9847
            $navurl = $_SERVER["PHP_SELF"];
9848
            // Special case for project/task page
9849
            if ($paramid == 'project_ref') {
9850
                if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) {     // TODO Remove this when nav with project_ref on task pages are ok
9851
                    $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
9852
                    $paramid = 'ref';
9853
                }
9854
            }
9855
9856
            // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
9857
            // accesskey is for Mac:               CTRL + key for all browsers
9858
            $stringforfirstkey = $langs->trans("KeyboardShortcut");
9859
            if ($conf->browser->name == 'chrome') {
9860
                $stringforfirstkey .= ' ALT +';
9861
            } elseif ($conf->browser->name == 'firefox') {
9862
                $stringforfirstkey .= ' ALT + SHIFT +';
9863
            } else {
9864
                $stringforfirstkey .= ' CTL +';
9865
            }
9866
9867
            $previous_ref = $object->ref_previous ? '<a accesskey="p" alt="' . dol_escape_htmltag($langs->trans("Previous")) . '" 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>';
9868
            $next_ref = $object->ref_next ? '<a accesskey="n" alt="' . dol_escape_htmltag($langs->trans("Next")) . '" 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>';
9869
        }
9870
9871
        //print "xx".$previous_ref."x".$next_ref;
9872
        $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
9873
9874
        // Right part of banner
9875
        if ($morehtmlright) {
9876
            $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
9877
        }
9878
9879
        if ($previous_ref || $next_ref || $morehtml) {
9880
            $ret .= '<div class="pagination paginationref"><ul class="right">';
9881
        }
9882
        if ($morehtml && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
9883
            $ret .= '<!-- morehtml --><li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
9884
        }
9885
        if ($shownav && ($previous_ref || $next_ref)) {
9886
            $ret .= '<li class="pagination">' . $previous_ref . '</li>';
9887
            $ret .= '<li class="pagination">' . $next_ref . '</li>';
9888
        }
9889
        if ($previous_ref || $next_ref || $morehtml) {
9890
            $ret .= '</ul></div>';
9891
        }
9892
9893
        // Status
9894
        $parameters = array('morehtmlstatus' => $morehtmlstatus);
9895
        $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
9896
        if (empty($reshook)) {
9897
            $morehtmlstatus .= $hookmanager->resPrint;
9898
        } else {
9899
            $morehtmlstatus = $hookmanager->resPrint;
9900
        }
9901
        if ($morehtmlstatus) {
9902
            $ret .= '<div class="statusref">' . $morehtmlstatus . '</div>';
9903
        }
9904
9905
        $parameters = array();
9906
        $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
9907
        if (empty($reshook)) {
9908
            $morehtmlref .= $hookmanager->resPrint;
9909
        } elseif ($reshook > 0) {
9910
            $morehtmlref = $hookmanager->resPrint;
9911
        }
9912
9913
        // Left part of banner
9914
        if ($morehtmlleft) {
9915
            if ($conf->browser->layout == 'phone') {
9916
                $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
9917
            } else {
9918
                $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
9919
            }
9920
        }
9921
9922
        //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
9923
        $ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
9924
9925
        // For thirdparty, contact, user, member, the ref is the id, so we show something else
9926
        if ($object->element == 'societe') {
9927
            $ret .= dol_htmlentities($object->name);
9928
9929
            // List of extra languages
9930
            $arrayoflangcode = array();
9931
            if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
9932
                $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
9933
            }
9934
9935
            if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
9936
                if (!is_object($extralanguages)) {
9937
                    include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
9938
                    $extralanguages = new ExtraLanguages($this->db);
9939
                }
9940
                $extralanguages->fetch_name_extralanguages('societe');
9941
9942
                if (!empty($extralanguages->attributes['societe']['name'])) {
9943
                    $object->fetchValuesForExtraLanguages();
9944
9945
                    $htmltext = '';
9946
                    // If there is extra languages
9947
                    foreach ($arrayoflangcode as $extralangcode) {
9948
                        $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
9949
                        if ($object->array_languages['name'][$extralangcode]) {
9950
                            $htmltext .= $object->array_languages['name'][$extralangcode];
9951
                        } else {
9952
                            $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
9953
                        }
9954
                    }
9955
                    $ret .= '<!-- Show translations of name -->' . "\n";
9956
                    $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
9957
                }
9958
            }
9959
        } elseif ($object->element == 'member') {
9960
            '@phan-var-force Adherent $object';
9961
            $ret .= $object->ref . '<br>';
9962
            $fullname = $object->getFullName($langs);
0 ignored issues
show
Bug introduced by
The method getFullName() does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

9962
            /** @scrutinizer ignore-call */ 
9963
            $fullname = $object->getFullName($langs);
Loading history...
9963
            if ($object->morphy == 'mor' && $object->societe) {
0 ignored issues
show
Bug Best Practice introduced by
The property morphy does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property societe does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
9964
                $ret .= dol_htmlentities($object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '');
9965
            } else {
9966
                $ret .= dol_htmlentities($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities($object->societe) . ')' : '');
9967
            }
9968
        } elseif (in_array($object->element, array('contact', 'user'))) {
9969
            $ret .= dol_htmlentities($object->getFullName($langs)) . $addgendertxt;
9970
        } elseif ($object->element == 'usergroup') {
9971
            $ret .= dol_htmlentities($object->name);
9972
        } elseif (in_array($object->element, array('action', 'agenda'))) {
9973
            '@phan-var-force ActionComm $object';
9974
            $ret .= $object->ref . '<br>' . $object->label;
9975
        } elseif (in_array($object->element, array('adherent_type'))) {
9976
            $ret .= $object->label;
9977
        } elseif ($object->element == 'ecm_directories') {
9978
            $ret .= '';
9979
        } elseif ($fieldref != 'none') {
9980
            $ret .= dol_htmlentities(!empty($object->$fieldref) ? $object->$fieldref : "");
9981
        }
9982
        if ($morehtmlref) {
9983
            // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
9984
            if (substr($morehtmlref, 0, 4) != '<div') {
9985
                $ret .= ' ';
9986
            }
9987
9988
            $ret .= $morehtmlref;
9989
        }
9990
9991
        $ret .= '</div>';
9992
9993
        $ret .= '</div><!-- End banner content -->';
9994
9995
        return $ret;
9996
    }
9997
9998
9999
    /**
10000
     *  Return HTML code to output a barcode
10001
     *
10002
     * @param CommonObject $object Object containing data to retrieve file name
10003
     * @param int $width Width of photo
10004
     * @param string $morecss More CSS on img of barcode
10005
     * @return string                    HTML code to output barcode
10006
     */
10007
    public function showbarcode(&$object, $width = 100, $morecss = '')
10008
    {
10009
        global $conf;
10010
10011
        //Check if barcode is filled in the card
10012
        if (empty($object->barcode)) {
0 ignored issues
show
Bug Best Practice introduced by
The property barcode does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
10013
            return '';
10014
        }
10015
10016
        // Complete object if not complete
10017
        if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
10018
            // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10019
            $result = $object->fetch_barcode();
10020
            //Check if fetch_barcode() failed
10021
            if ($result < 1) {
10022
                return '<!-- ErrorFetchBarcode -->';
10023
            }
10024
        }
10025
10026
        // Barcode image  @phan-suppress-next-line PhanUndeclaredProperty
10027
        $url = constant('BASE_URL') . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
10028
        $out = '<!-- url barcode = ' . $url . ' -->';
10029
        $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
10030
10031
        return $out;
10032
    }
10033
10034
    /**
10035
     * Return HTML code to output a photo
10036
     *
10037
     * @param string    $modulepart                 Key to define module concerned ('societe', 'userphoto', 'memberphoto')
10038
     * @param Societe|Adherent|Contact|User|CommonObject    $object Object containing data to retrieve file name
10039
     * @param int       $width                      Width of photo
10040
     * @param int       $height                     Height of photo (auto if 0)
10041
     * @param int<0,1>  $caneditfield               Add edit fields
10042
     * @param string    $cssclass                   CSS name to use on img for photo
10043
     * @param string    $imagesize                  'mini', 'small' or '' (original)
10044
     * @param int<0,1>  $addlinktofullsize          Add link to fullsize image
10045
     * @param int<0,1>  $cache                      1=Accept to use image in cache
10046
     * @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 ''.
10047
     * @param int<0,1>  $noexternsourceoverwrite    No overwrite image with extern source (like 'gravatar' or other module)
10048
     * @return string                               HTML code to output photo
10049
     * @see getImagePublicURLOfObject()
10050
     */
10051
    public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
10052
    {
10053
        global $conf, $langs;
10054
10055
        $entity = (empty($object->entity) ? $conf->entity : $object->entity);
10056
        $id = (empty($object->id) ? $object->rowid : $object->id);  // @phan-suppress-current-line PhanUndeclaredProperty (->rowid)
0 ignored issues
show
Bug Best Practice introduced by
The property rowid does not exist on Dolibarr\Code\Contact\Classes\Contact. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property rowid does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property rowid does not exist on Dolibarr\Code\User\Classes\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property rowid does not exist on Dolibarr\Code\Adherents\Classes\Adherent. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property rowid does not exist on Dolibarr\Code\Societe\Classes\Societe. Since you implemented __get, consider adding a @property annotation.
Loading history...
10057
10058
        $dir = '';
10059
        $file = '';
10060
        $originalfile = '';
10061
        $altfile = '';
10062
        $email = '';
10063
        $capture = '';
10064
        if ($modulepart == 'societe') {
10065
            $dir = $conf->societe->multidir_output[$entity];
10066
            if (!empty($object->logo)) {
0 ignored issues
show
Bug Best Practice introduced by
The property logo does not exist on Dolibarr\Code\Contact\Classes\Contact. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property logo does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property logo does not exist on Dolibarr\Code\User\Classes\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property logo does not exist on Dolibarr\Code\Adherents\Classes\Adherent. Since you implemented __get, consider adding a @property annotation.
Loading history...
10067
                if (dolIsAllowedForPreview($object->logo)) {
10068
                    if ((string) $imagesize == 'mini') {
10069
                        $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
10070
                    } elseif ((string) $imagesize == 'small') {
10071
                        $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
10072
                    } else {
10073
                        $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
10074
                    }
10075
                    $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
10076
                }
10077
            }
10078
            $email = $object->email;
0 ignored issues
show
Bug Best Practice introduced by
The property email does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
10079
        } elseif ($modulepart == 'contact') {
10080
            $dir = $conf->societe->multidir_output[$entity] . '/contact';
10081
            if (!empty($object->photo)) {
0 ignored issues
show
Bug Best Practice introduced by
The property photo does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property photo does not exist on Dolibarr\Code\Societe\Classes\Societe. Since you implemented __get, consider adding a @property annotation.
Loading history...
10082
                if (dolIsAllowedForPreview($object->photo)) {
10083
                    if ((string) $imagesize == 'mini') {
10084
                        $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
10085
                    } elseif ((string) $imagesize == 'small') {
10086
                        $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
10087
                    } else {
10088
                        $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $object->photo;
10089
                    }
10090
                    $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $object->photo;
10091
                }
10092
            }
10093
            $email = $object->email;
10094
            $capture = 'user';
10095
        } elseif ($modulepart == 'userphoto') {
10096
            $dir = $conf->user->dir_output;
10097
            if (!empty($object->photo)) {
10098
                if (dolIsAllowedForPreview($object->photo)) {
10099
                    if ((string) $imagesize == 'mini') {
10100
                        $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
10101
                    } elseif ((string) $imagesize == 'small') {
10102
                        $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
10103
                    } else {
10104
                        $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $object->photo;
10105
                    }
10106
                    $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $object->photo;
10107
                }
10108
            }
10109
            if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
10110
                $altfile = $object->id . ".jpg"; // For backward compatibility
10111
            }
10112
            $email = $object->email;
10113
            $capture = 'user';
10114
        } elseif ($modulepart == 'memberphoto') {
10115
            $dir = $conf->adherent->dir_output;
10116
            if (!empty($object->photo)) {
10117
                if (dolIsAllowedForPreview($object->photo)) {
10118
                    if ((string) $imagesize == 'mini') {
10119
                        $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
10120
                    } elseif ((string) $imagesize == 'small') {
10121
                        $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
10122
                    } else {
10123
                        $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $object->photo;
10124
                    }
10125
                    $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $object->photo;
10126
                }
10127
            }
10128
            if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
10129
                $altfile = $object->id . ".jpg"; // For backward compatibility
10130
            }
10131
            $email = $object->email;
10132
            $capture = 'user';
10133
        } else {
10134
            // Generic case to show photos
10135
            // TODO Implement this method in previous objects so we can always use this generic method.
10136
            if ($modulepart != "unknown" && method_exists($object, 'getDataToShowPhoto')) {
10137
                $tmpdata = $object->getDataToShowPhoto($modulepart, $imagesize);
10138
10139
                $dir = $tmpdata['dir'];
10140
                $file = $tmpdata['file'];
10141
                $originalfile = $tmpdata['originalfile'];
10142
                $altfile = $tmpdata['altfile'];
10143
                $email = $tmpdata['email'];
10144
                $capture = $tmpdata['capture'];
10145
            }
10146
        }
10147
10148
        if ($forcecapture) {
10149
            $capture = $forcecapture;
10150
        }
10151
10152
        $ret = '';
10153
10154
        if ($dir) {
10155
            if ($file && file_exists($dir . "/" . $file)) {
10156
                if ($addlinktofullsize) {
10157
                    $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
10158
                    if ($urladvanced) {
10159
                        $ret .= '<a href="' . $urladvanced . '">';
10160
                    } else {
10161
                        $ret .= '<a href="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
10162
                    }
10163
                }
10164
                $ret .= '<img alt="" class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . ' photologo' . (preg_replace('/[^a-z]/i', '_', $file)) . '" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($file) . '&cache=' . $cache . '">';
10165
                if ($addlinktofullsize) {
10166
                    $ret .= '</a>';
10167
                }
10168
            } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
10169
                if ($addlinktofullsize) {
10170
                    $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
10171
                    if ($urladvanced) {
10172
                        $ret .= '<a href="' . $urladvanced . '">';
10173
                    } else {
10174
                        $ret .= '<a href="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
10175
                    }
10176
                }
10177
                $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="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($altfile) . '&cache=' . $cache . '">';
10178
                if ($addlinktofullsize) {
10179
                    $ret .= '</a>';
10180
                }
10181
            } else {
10182
                $nophoto = '/public/theme/common/nophoto.png';
10183
                $defaultimg = 'identicon';        // For gravatar
10184
                if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) {    // For modules that need a special image when photo not found
10185
                    if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor') !== false)) {
0 ignored issues
show
Bug Best Practice introduced by
The property morphy does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property morphy does not exist on Dolibarr\Code\Societe\Classes\Societe. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property morphy does not exist on Dolibarr\Code\Contact\Classes\Contact. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property morphy does not exist on Dolibarr\Code\User\Classes\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
10186
                        $nophoto = 'company';
10187
                    } else {
10188
                        $nophoto = '/public/theme/common/user_anonymous.png';
10189
                        if (!empty($object->gender) && $object->gender == 'man') {
0 ignored issues
show
Bug Best Practice introduced by
The property gender does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property gender does not exist on Dolibarr\Code\Societe\Classes\Societe. Since you implemented __get, consider adding a @property annotation.
Loading history...
10190
                            $nophoto = '/public/theme/common/user_man.png';
10191
                        }
10192
                        if (!empty($object->gender) && $object->gender == 'woman') {
10193
                            $nophoto = '/public/theme/common/user_woman.png';
10194
                        }
10195
                    }
10196
                }
10197
10198
                if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
10199
                    // see https://gravatar.com/site/implement/images/php/
10200
                    $ret .= '<!-- Put link to gravatar -->';
10201
                    $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" title="' . $email . ' Gravatar avatar" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="https://www.gravatar.com/avatar/' . dol_hash(strtolower(trim($email)), 'sha256', 1) . '?s=' . $width . '&d=' . $defaultimg . '">'; // gravatar need md5 hash
10202
                } else {
10203
                    if ($nophoto == 'company') {
10204
                        $ret .= '<div class="divforspanimg valignmiddle center photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
10205
                        //$ret .= '<div class="difforspanimgright"></div>';
10206
                    } else {
10207
                        $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . constant('DOL_URL_ROOT') . $nophoto . '">';
10208
                    }
10209
                }
10210
            }
10211
10212
            if ($caneditfield) {
10213
                if ($object->photo) {
10214
                    $ret .= "<br>\n";
10215
                }
10216
                $ret .= '<table class="nobordernopadding centpercent">';
10217
                if ($object->photo) {
10218
                    $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
10219
                }
10220
                $ret .= '<tr><td class="tdoverflow">';
10221
                $maxfilesizearray = getMaxFileSizeArray();
10222
                $maxmin = $maxfilesizearray['maxmin'];
10223
                if ($maxmin > 0) {
10224
                    $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">';    // MAX_FILE_SIZE must precede the field type=file
10225
                }
10226
                $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . $capture . '"' : '') . '>';
10227
                $ret .= '</td></tr>';
10228
                $ret .= '</table>';
10229
            }
10230
        }
10231
10232
        return $ret;
10233
    }
10234
10235
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
10236
10237
    /**
10238
     * Return select list of groups
10239
     *
10240
     * @param int|object|object[]   $selected       Id group or group(s) preselected
10241
     * @param string                $htmlname       Field name in form
10242
     * @param int<0,1>              $show_empty     0=liste sans valeur nulle, 1=ajoute valeur inconnue
10243
     * @param string|int[]          $exclude        Array list of groups id to exclude
10244
     * @param int<0,1>              $disabled       If select list must be disabled
10245
     * @param string|int[]          $include        Array list of groups id to include
10246
     * @param int[]                 $enableonly     Array list of groups id to be enabled. All other must be disabled
10247
     * @param string                $force_entity   '0' or Ids of environment to force
10248
     * @param bool                  $multiple       add [] in the name of element and add 'multiple' attribute (not working with ajax_autocompleter)
10249
     * @param string                $morecss        More css to add to html component
10250
     * @return  string                              HTML Componont to select a group
10251
     * @see select_dolusers()
10252
     */
10253
    public function select_dolgroups($selected = 0, $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0', $multiple = false, $morecss = 'minwidth200')
10254
    {
10255
		// phpcs:enable
10256
        global $conf, $user, $langs;
10257
10258
        // Allow excluding groups
10259
        $excludeGroups = null;
10260
        if (is_array($exclude)) {
10261
            $excludeGroups = implode(",", $exclude);
10262
        }
10263
        // Allow including groups
10264
        $includeGroups = null;
10265
        if (is_array($include)) {
10266
            $includeGroups = implode(",", $include);
10267
        }
10268
10269
        if (!is_array($selected)) {
10270
            $selected = array($selected);
10271
        }
10272
10273
        $out = '';
10274
10275
        // Build sql to search groups
10276
        $sql = "SELECT ug.rowid, ug.nom as name";
10277
        if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
10278
            $sql .= ", e.label";
10279
        }
10280
        $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
10281
        if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
10282
            $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
10283
            if ($force_entity) {
10284
                $sql .= " WHERE ug.entity IN (0, " . $force_entity . ")";
10285
            } else {
10286
                $sql .= " WHERE ug.entity IS NOT NULL";
10287
            }
10288
        } else {
10289
            $sql .= " WHERE ug.entity IN (0, " . $conf->entity . ")";
10290
        }
10291
        if (is_array($exclude) && $excludeGroups) {
10292
            $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
10293
        }
10294
        if (is_array($include) && $includeGroups) {
10295
            $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
10296
        }
10297
        $sql .= " ORDER BY ug.nom ASC";
10298
10299
        dol_syslog(get_only_class($this) . "::select_dolgroups", LOG_DEBUG);
10300
        $resql = $this->db->query($sql);
10301
        if ($resql) {
10302
            // Enhance with select2
10303
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10304
10305
            $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
10306
10307
            $num = $this->db->num_rows($resql);
10308
            $i = 0;
10309
            if ($num) {
10310
                if ($show_empty && !$multiple) {
10311
                    $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
10312
                }
10313
10314
                while ($i < $num) {
10315
                    $obj = $this->db->fetch_object($resql);
10316
                    $disableline = 0;
10317
                    if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
10318
                        $disableline = 1;
10319
                    }
10320
10321
                    $label = $obj->name;
10322
                    $labelhtml = $obj->name;
10323
                    if (isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1) {
10324
                        $label .= " (" . $obj->label . ")";
10325
                        $labelhtml .= ' <span class="opacitymedium">(' . $obj->label . ')</span>';
10326
                    }
10327
10328
                    $out .= '<option value="' . $obj->rowid . '"';
10329
                    if ($disableline) {
10330
                        $out .= ' disabled';
10331
                    }
10332
                    if (
10333
                        (isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid)
10334
                        || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))
10335
                    ) {
10336
                        $out .= ' selected';
10337
                    }
10338
                    $out .= ' data-html="' . dol_escape_htmltag($labelhtml) . '"';
10339
                    $out .= '>';
10340
                    $out .= $label;
10341
                    $out .= '</option>';
10342
                    $i++;
10343
                }
10344
            } else {
10345
                if ($show_empty) {
10346
                    $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
10347
                }
10348
                $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
10349
            }
10350
            $out .= '</select>';
10351
10352
            $out .= ajax_combobox($htmlname);
10353
        } else {
10354
            dol_print_error($this->db);
10355
        }
10356
10357
        return $out;
10358
    }
10359
10360
10361
    /**
10362
     *    Return HTML to show the search and clear search button
10363
     *
10364
     * @param string $pos Position of colon on the list. Value 'left' or 'right'
10365
     * @return    string
10366
     */
10367
    public function showFilterButtons($pos = '')
10368
    {
10369
        $out = '<div class="nowraponall">';
10370
        $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fas fa-search"></span></button>';
10371
        $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fas fa-times"></span></button>';
10372
        $out .= '</div>';
10373
10374
        return $out;
10375
    }
10376
10377
    /**
10378
     *    Return HTML to show the search and clear search button
10379
     *
10380
     * @param string $cssclass CSS class
10381
     * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes
10382
     * @param string $massactionname Mass action button name that will launch an action on the selected items
10383
     * @return    string
10384
     */
10385
    public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
10386
    {
10387
        global $conf;
10388
10389
        $out = '';
10390
10391
        if (!empty($conf->use_javascript_ajax)) {
10392
            $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
10393
        }
10394
        $out .= '<script nonce="' . getNonce() . '">
10395
            $(document).ready(function() {
10396
                $("#' . $cssclass . 's").click(function() {
10397
                    if($(this).is(\':checked\')){
10398
                        console.log("We check all ' . $cssclass . ' and trigger the change method");
10399
                		$(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
10400
                    }
10401
                    else
10402
                    {
10403
                        console.log("We uncheck all");
10404
                		$(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
10405
                    }' . "\n";
10406
        if ($calljsfunction) {
10407
            $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
10408
        }
10409
        $out .= '         });
10410
        	        $(".' . $cssclass . '").change(function() {
10411
					$(this).closest("tr").toggleClass("highlight", this.checked);
10412
				});
10413
		 	});
10414
    	</script>';
10415
10416
        return $out;
10417
    }
10418
10419
    /**
10420
     *    Return HTML to show the search and clear search button
10421
     *
10422
     * @param int $addcheckuncheckall Add the check all/uncheck all checkbox (use javascript) and code to manage this
10423
     * @param string $cssclass CSS class
10424
     * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes
10425
     * @param string $massactionname Mass action name
10426
     * @return    string
10427
     */
10428
    public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
10429
    {
10430
        $out = $this->showFilterButtons();
10431
        if ($addcheckuncheckall) {
10432
            $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
10433
        }
10434
        return $out;
10435
    }
10436
10437
    /**
10438
     * Return HTML to show the select of expense categories
10439
     *
10440
     * @param string    $selected       preselected category
10441
     * @param string    $htmlname       name of HTML select list
10442
     * @param int<0,1>  $useempty       1=Add empty line
10443
     * @param int[]     $excludeid      id to exclude
10444
     * @param string    $target         htmlname of target select to bind event
10445
     * @param int       $default_selected default category to select if fk_c_type_fees change = EX_KME
10446
     * @param array<string,int|string>  $params param to give
10447
     * @param int<0,1>  $info_admin     Show the tooltip help picto to setup list
10448
     * @return    string
10449
     */
10450
    public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
10451
    {
10452
        global $langs, $user;
10453
10454
        $out = '';
10455
        $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
10456
        $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
10457
        if (!empty($excludeid)) {
10458
            $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
10459
        }
10460
        $sql .= " ORDER BY label";
10461
10462
        $resql = $this->db->query($sql);
10463
        if ($resql) {
10464
            $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
10465
            if ($useempty) {
10466
                $out .= '<option value="0">&nbsp;</option>';
10467
            }
10468
10469
            while ($obj = $this->db->fetch_object($resql)) {
10470
                $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
10471
            }
10472
            $out .= '</select>';
10473
            $out .= ajax_combobox('select_' . $htmlname);
10474
10475
            if (!empty($htmlname) && $user->admin && $info_admin) {
10476
                $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
10477
            }
10478
10479
            if (!empty($target)) {
10480
                $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
10481
                $resql = $this->db->query($sql);
10482
                if ($resql) {
10483
                    if ($this->db->num_rows($resql) > 0) {
10484
                        $obj = $this->db->fetch_object($resql);
10485
                        $out .= '<script nonce="' . getNonce() . '">
10486
							$(function() {
10487
								$("select[name=' . $target . ']").on("change", function() {
10488
									var current_val = $(this).val();
10489
									if (current_val == ' . $obj->id . ') {';
10490
                        if (!empty($default_selected) || !empty($selected)) {
10491
                            $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
10492
                        }
10493
10494
                        $out .= '
10495
										$("select[name=' . $htmlname . ']").change();
10496
									}
10497
								});
10498
10499
								$("select[name=' . $htmlname . ']").change(function() {
10500
10501
									if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
10502
										// get price of kilometer to fill the unit price
10503
										$.ajax({
10504
											method: "POST",
10505
											dataType: "json",
10506
											data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
10507
											url: "' . (constant('BASE_URL') . '/expensereport/ajax/ajaxik.php?' . implode('&', $params)) . '",
10508
										}).done(function( data, textStatus, jqXHR ) {
10509
											console.log(data);
10510
											if (typeof data.up != "undefined") {
10511
												$("input[name=value_unit]").val(data.up);
10512
												$("select[name=' . $htmlname . ']").attr("title", data.title);
10513
											} else {
10514
												$("input[name=value_unit]").val("");
10515
												$("select[name=' . $htmlname . ']").attr("title", "");
10516
											}
10517
										});
10518
									}
10519
								});
10520
							});
10521
						</script>';
10522
                    }
10523
                }
10524
            }
10525
        } else {
10526
            dol_print_error($this->db);
10527
        }
10528
10529
        return $out;
10530
    }
10531
10532
    /**
10533
     * Return HTML to show the select ranges of expense range
10534
     *
10535
     * @param string $selected preselected category
10536
     * @param string $htmlname name of HTML select list
10537
     * @param integer $useempty 1=Add empty line
10538
     * @return    string
10539
     */
10540
    public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
10541
    {
10542
        global $conf, $langs;
10543
10544
        $out = '';
10545
        $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
10546
        $sql .= " WHERE entity = " . $conf->entity . " AND active = 1";
10547
10548
        $resql = $this->db->query($sql);
10549
        if ($resql) {
10550
            $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
10551
            if ($useempty) {
10552
                $out .= '<option value="0"></option>';
10553
            }
10554
10555
            while ($obj = $this->db->fetch_object($resql)) {
10556
                $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
10557
            }
10558
            $out .= '</select>';
10559
        } else {
10560
            dol_print_error($this->db);
10561
        }
10562
10563
        return $out;
10564
    }
10565
10566
    /**
10567
     * Return HTML to show a select of expense
10568
     *
10569
     * @param string $selected preselected category
10570
     * @param string $htmlname name of HTML select list
10571
     * @param integer $useempty 1=Add empty choice
10572
     * @param integer $allchoice 1=Add all choice
10573
     * @param integer $useid 0=use 'code' as key, 1=use 'id' as key
10574
     * @return    string
10575
     */
10576
    public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
10577
    {
10578
        global $langs;
10579
10580
        $out = '';
10581
        $sql = "SELECT id, code, label";
10582
        $sql .= " FROM " . $this->db->prefix() . "c_type_fees";
10583
        $sql .= " WHERE active = 1";
10584
10585
        $resql = $this->db->query($sql);
10586
        if ($resql) {
10587
            $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
10588
            if ($useempty) {
10589
                $out .= '<option value="0"></option>';
10590
            }
10591
            if ($allchoice) {
10592
                $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
10593
            }
10594
10595
            $field = 'code';
10596
            if ($useid) {
10597
                $field = 'id';
10598
            }
10599
10600
            while ($obj = $this->db->fetch_object($resql)) {
10601
                $key = $langs->trans($obj->code);
10602
                $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
10603
            }
10604
            $out .= '</select>';
10605
10606
            $out .= ajax_combobox('select_' . $htmlname);
10607
        } else {
10608
            dol_print_error($this->db);
10609
        }
10610
10611
        return $out;
10612
    }
10613
10614
    /**
10615
     *  Output a combo list with invoices qualified for a third party
10616
     *
10617
     * @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)
10618
     * @param string $selected Id invoice preselected
10619
     * @param string $htmlname Name of HTML select
10620
     * @param int $maxlength Maximum length of label
10621
     * @param int $option_only Return only html options lines without the select tag
10622
     * @param string $show_empty Add an empty line ('1' or string to show for empty line)
10623
     * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable)
10624
     * @param int $forcefocus Force focus on field (works with javascript only)
10625
     * @param int $disabled Disabled
10626
     * @param string $morecss More css added to the select component
10627
     * @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
10628
     * @param string $showproject 'all' = Show project info, ''=Hide project info
10629
     * @param User $usertofilter User object to use for filtering
10630
     * @return string            HTML Select Invoice
10631
     */
10632
    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)
10633
    {
10634
        global $user, $conf, $langs;
10635
10636
10637
        if (is_null($usertofilter)) {
10638
            $usertofilter = $user;
10639
        }
10640
10641
        $out = '';
10642
10643
        $hideunselectables = false;
10644
        if (getDolGlobalString('PROJECT_HIDE_UNSELECTABLES')) {
10645
            $hideunselectables = true;
10646
        }
10647
10648
        if (empty($projectsListId)) {
10649
            if (!$usertofilter->hasRight('projet', 'all', 'lire')) {
0 ignored issues
show
Bug introduced by
The method hasRight() does not exist on null. ( Ignorable by Annotation )

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

10649
            if (!$usertofilter->/** @scrutinizer ignore-call */ hasRight('projet', 'all', 'lire')) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
10650
                $projectstatic = new Project($this->db);
10651
                $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
10652
            }
10653
        }
10654
10655
        // Search all projects
10656
        $sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref,
10657
            p.title, p.fk_soc, p.fk_statut, p.public,";
10658
        $sql .= ' s.nom as name';
10659
        $sql .= ' FROM ' . $this->db->prefix() . 'projet as p';
10660
        $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'societe as s ON s.rowid = p.fk_soc,';
10661
        $sql .= ' ' . $this->db->prefix() . 'facture as f';
10662
        $sql .= " WHERE p.entity IN (" . getEntity('project') . ")";
10663
        $sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
10664
        //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
10665
        //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
10666
        //if ($socid > 0)  $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
10667
        $sql .= " ORDER BY p.ref, f.ref ASC";
10668
10669
        $resql = $this->db->query($sql);
10670
        if ($resql) {
10671
            // Use select2 selector
10672
            if (!empty($conf->use_javascript_ajax)) {
10673
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10674
                $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
10675
                $out .= $comboenhancement;
10676
                $morecss = 'minwidth200imp maxwidth500';
10677
            }
10678
10679
            if (empty($option_only)) {
10680
                $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
10681
            }
10682
            if (!empty($show_empty)) {
10683
                $out .= '<option value="0" class="optiongrey">';
10684
                if (!is_numeric($show_empty)) {
10685
                    $out .= $show_empty;
10686
                } else {
10687
                    $out .= '&nbsp;';
10688
                }
10689
                $out .= '</option>';
10690
            }
10691
            $num = $this->db->num_rows($resql);
10692
            $i = 0;
10693
            if ($num) {
10694
                while ($i < $num) {
10695
                    $obj = $this->db->fetch_object($resql);
10696
                    // 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.
10697
                    if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && !$usertofilter->hasRight('societe', 'lire')) {
10698
                        // Do nothing
10699
                    } else {
10700
                        if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
10701
                            $i++;
10702
                            continue;
10703
                        }
10704
10705
                        $labeltoshow = '';
10706
10707
                        if ($showproject == 'all') {
10708
                            $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
10709
                            if ($obj->name) {
10710
                                $labeltoshow .= ' - ' . $obj->name; // Soc name
10711
                            }
10712
10713
                            $disabled = 0;
10714
                            if ($obj->fk_statut == Project::STATUS_DRAFT) {
10715
                                $disabled = 1;
10716
                                $labeltoshow .= ' - ' . $langs->trans("Draft");
10717
                            } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
10718
                                if ($discard_closed == 2) {
10719
                                    $disabled = 1;
10720
                                }
10721
                                $labeltoshow .= ' - ' . $langs->trans("Closed");
10722
                            } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
10723
                                $disabled = 1;
10724
                                $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
10725
                            }
10726
                        }
10727
10728
                        if (!empty($selected) && $selected == $obj->rowid) {
10729
                            $out .= '<option value="' . $obj->rowid . '" selected';
10730
                            //if ($disabled) $out.=' disabled';                     // with select2, field can't be preselected if disabled
10731
                            $out .= '>' . $labeltoshow . '</option>';
10732
                        } else {
10733
                            if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
10734
                                $resultat = '';
10735
                            } else {
10736
                                $resultat = '<option value="' . $obj->rowid . '"';
10737
                                if ($disabled) {
10738
                                    $resultat .= ' disabled';
10739
                                }
10740
                                //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
10741
                                //else $labeltoshow.=' ('.$langs->trans("Private").')';
10742
                                $resultat .= '>';
10743
                                $resultat .= $labeltoshow;
10744
                                $resultat .= '</option>';
10745
                            }
10746
                            $out .= $resultat;
10747
                        }
10748
                    }
10749
                    $i++;
10750
                }
10751
            }
10752
            if (empty($option_only)) {
10753
                $out .= '</select>';
10754
            }
10755
10756
            $this->db->free($resql);
10757
10758
            return $out;
10759
        } else {
10760
            dol_print_error($this->db);
10761
            return '';
10762
        }
10763
    }
10764
10765
    /**
10766
     *  Output a combo list with invoices qualified for a third party
10767
     *
10768
     * @param string $selected Id invoice preselected
10769
     * @param string $htmlname Name of HTML select
10770
     * @param int $maxlength Maximum length of label
10771
     * @param int $option_only Return only html options lines without the select tag
10772
     * @param string $show_empty Add an empty line ('1' or string to show for empty line)
10773
     * @param int $forcefocus Force focus on field (works with javascript only)
10774
     * @param int $disabled Disabled
10775
     * @param string $morecss More css added to the select component
10776
     * @return int                    Nbr of project if OK, <0 if KO
10777
     */
10778
    public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
10779
    {
10780
        global $conf, $langs;
10781
10782
        $out = '';
10783
10784
        dol_syslog('FactureRec::fetch', LOG_DEBUG);
10785
10786
        $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
10787
        //$sql.= ', el.fk_source';
10788
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
10789
        $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
10790
        $sql .= " ORDER BY f.titre ASC";
10791
10792
        $resql = $this->db->query($sql);
10793
        if ($resql) {
10794
            // Use select2 selector
10795
            if (!empty($conf->use_javascript_ajax)) {
10796
                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10797
                $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
10798
                $out .= $comboenhancement;
10799
                $morecss = 'minwidth200imp maxwidth500';
10800
            }
10801
10802
            if (empty($option_only)) {
10803
                $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
10804
            }
10805
            if (!empty($show_empty)) {
10806
                $out .= '<option value="0" class="optiongrey">';
10807
                if (!is_numeric($show_empty)) {
10808
                    $out .= $show_empty;
10809
                } else {
10810
                    $out .= '&nbsp;';
10811
                }
10812
                $out .= '</option>';
10813
            }
10814
            $num = $this->db->num_rows($resql);
10815
            if ($num) {
10816
                while ($obj = $this->db->fetch_object($resql)) {
10817
                    $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
10818
10819
                    $disabled = 0;
10820
                    if (!empty($obj->suspended)) {
10821
                        $disabled = 1;
10822
                        $labeltoshow .= ' - ' . $langs->trans("Closed");
10823
                    }
10824
10825
10826
                    if (!empty($selected) && $selected == $obj->rowid) {
10827
                        $out .= '<option value="' . $obj->rowid . '" selected';
10828
                        //if ($disabled) $out.=' disabled';                     // with select2, field can't be preselected if disabled
10829
                        $out .= '>' . $labeltoshow . '</option>';
10830
                    } else {
10831
                        if ($disabled && ($selected != $obj->rowid)) {
10832
                            $resultat = '';
10833
                        } else {
10834
                            $resultat = '<option value="' . $obj->rowid . '"';
10835
                            if ($disabled) {
10836
                                $resultat .= ' disabled';
10837
                            }
10838
                            $resultat .= '>';
10839
                            $resultat .= $labeltoshow;
10840
                            $resultat .= '</option>';
10841
                        }
10842
                        $out .= $resultat;
10843
                    }
10844
                }
10845
            }
10846
            if (empty($option_only)) {
10847
                $out .= '</select>';
10848
            }
10849
10850
            print $out;
10851
10852
            $this->db->free($resql);
10853
            return $num;
10854
        } else {
10855
            $this->errors[] = $this->db->lasterror;
10856
            return -1;
10857
        }
10858
    }
10859
10860
    /**
10861
     * Output the component to make advanced search criteries
10862
     *
10863
     * @param   array<array<string,array{type:string}>> $arrayofcriterias                   Array of available search criteria. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...)
10864
     * @param   array<int,string>                       $search_component_params            Array of selected search criteria
10865
     * @param   string[]                                $arrayofinputfieldsalreadyoutput    Array of input fields already inform. The component will not generate a hidden input field if it is in this list.
10866
     * @param   string                                  $search_component_params_hidden     String with $search_component_params criteria
10867
     * @return  string                                                                      HTML component for advanced search
10868
     */
10869
    public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '')
10870
    {
10871
        global $langs;
10872
10873
        if ($search_component_params_hidden != '' && !preg_match('/^\(.*\)$/', $search_component_params_hidden)) {    // If $search_component_params_hidden does not start and end with ()
10874
            $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
10875
        }
10876
10877
        $ret = '';
10878
10879
        $ret .= '<div class="divadvancedsearchfieldcomp centpercent inline-block">';
10880
        $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
10881
        $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
10882
        $ret .= '</a>';
10883
10884
        $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
10885
10886
        // Show select fields as tags.
10887
        $ret .= '<div id="divsearch_component_params" name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
10888
10889
        if ($search_component_params_hidden) {
10890
            // Split the criteria on each AND
10891
            //var_dump($search_component_params_hidden);
10892
10893
            $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
10894
10895
            // $arrayofandtags is now array( '...' , '...', ...)
10896
            // Show each AND part
10897
            foreach ($arrayofandtags as $tmpkey => $tmpval) {
10898
                $errormessage = '';
10899
                $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
10900
                if ($errormessage) {
10901
                    $this->error = 'ERROR in parsing search string: ' . $errormessage;
10902
                }
10903
                // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
10904
                include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
10905
                $searchtags = removeGlobalParenthesis($searchtags);
10906
10907
                $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="' . ($tmpkey + 1) . '" data-ufilter="' . dol_escape_htmltag($tmpval) . '">';
10908
                $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="' . ($tmpkey + 1) . '">x</span> ';
10909
                $ret .= dol_escape_htmltag($searchtags);
10910
                $ret .= '</span>';
10911
            }
10912
        }
10913
10914
        //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
10915
10916
        //$ret .= search_component_params
10917
        //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
10918
        //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
10919
10920
        $show_search_component_params_hidden = 1;
10921
        if ($show_search_component_params_hidden) {
10922
            $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
10923
        }
10924
        $ret .= "<!-- We store the full Universal Search String into this field. For example: (t.ref:like:'SO-%') AND ((t.ref:like:'CO-%') OR (t.ref:like:'AA%')) -->";
10925
        $ret .= '<input type="hidden" id="search_component_params_hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
10926
        // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
10927
10928
        // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
10929
        foreach ($arrayofcriterias as $criteria) {
10930
            foreach ($criteria as $criteriafamilykey => $criteriafamilyval) {
10931
                if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
10932
                    continue;
10933
                }
10934
                if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
10935
                    continue;
10936
                }
10937
                if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
10938
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
10939
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
10940
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
10941
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
10942
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
10943
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
10944
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
10945
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
10946
                } else {
10947
                    $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
10948
                }
10949
            }
10950
        }
10951
10952
        $ret .= '</div>';
10953
10954
        $ret .= "<!-- Field to enter a generic filter string: t.ref:like:'SO-%', t.date_creation:<:'20160101', t.date_creation:<:'2016-01-01 12:30:00', t.nature:is:NULL, t.field2:isnot:NULL -->\n";
10955
        $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" id="search_component_params_input" name="search_component_params_input" class="noborderbottom search_component_input" value="">';
10956
10957
        $ret .= '</div>';
10958
        $ret .= '</div>';
10959
10960
        $ret .= '<script>
10961
		jQuery(".tagsearchdelete").click(function(e) {
10962
			var filterid = $(this).parents().attr("data-ufilterid");
10963
			console.log("We click to delete the criteria nb "+filterid);
10964
10965
			// Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
10966
			var newparamstring = \'\';
10967
			$(\'.tagsearch\').each(function(index, element) {
10968
				tmpfilterid = $(this).attr("data-ufilterid");
10969
				if (tmpfilterid != filterid) {
10970
					// We keep this criteria
10971
					if (newparamstring == \'\') {
10972
						newparamstring = $(this).attr("data-ufilter");
10973
					} else {
10974
						newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
10975
					}
10976
				}
10977
			});
10978
			console.log("newparamstring = "+newparamstring);
10979
10980
			jQuery("#search_component_params_hidden").val(newparamstring);
10981
10982
			// We repost the form
10983
			$(this).closest(\'form\').submit();
10984
		});
10985
10986
		jQuery("#search_component_params_input").keydown(function(e) {
10987
			console.log("We press a key on the filter field that is "+jQuery("#search_component_params_input").val());
10988
			console.log(e.which);
10989
			if (jQuery("#search_component_params_input").val() == "" && e.which == 8) {
10990
				/* We click on back when the input field is already empty */
10991
			   	event.preventDefault();
10992
				jQuery("#divsearch_component_params .tagsearch").last().remove();
10993
				/* Regenerate content of search_component_params_hidden from remaining .tagsearch */
10994
				var s = "";
10995
				jQuery("#divsearch_component_params .tagsearch").each(function( index ) {
10996
					if (s != "") {
10997
						s = s + " AND ";
10998
					}
10999
					s = s + $(this).attr("data-ufilter");
11000
				});
11001
				console.log("New value for search_component_params_hidden = "+s);
11002
				jQuery("#search_component_params_hidden").val(s);
11003
			}
11004
		});
11005
11006
		</script>
11007
		';
11008
11009
        return $ret;
11010
    }
11011
11012
    /**
11013
     * selectModelMail
11014
     *
11015
     * @param   string      $prefix         Prefix
11016
     * @param   string      $modelType      Model type
11017
     * @param   int<0,1>    $default        1=Show also Default mail template
11018
     * @param   int<0,1>    $addjscombo     Add js combobox
11019
     * @return  string                      HTML select string
11020
     */
11021
    public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
11022
    {
11023
        global $langs, $user;
11024
11025
        $retstring = '';
11026
11027
        $TModels = array();
11028
11029
        $formmail = new FormMail($this->db);
11030
        $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
11031
11032
        if ($default) {
11033
            $TModels[0] = $langs->trans('DefaultMailModel');
11034
        }
11035
        if ($result > 0) {
11036
            foreach ($formmail->lines_model as $model) {
11037
                $TModels[$model->id] = $model->label;
11038
            }
11039
        }
11040
11041
        $retstring .= '<select class="flat" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
11042
11043
        foreach ($TModels as $id_model => $label_model) {
11044
            $retstring .= '<option value="' . $id_model . '"';
11045
            $retstring .= ">" . $label_model . "</option>";
11046
        }
11047
11048
        $retstring .= "</select>";
11049
11050
        if ($addjscombo) {
11051
            $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
11052
        }
11053
11054
        return $retstring;
11055
    }
11056
11057
    /**
11058
     * Output the buttons to submit a creation/edit form
11059
     *
11060
     * @param   string  $save_label         Alternative label for save button
11061
     * @param   string  $cancel_label       Alternative label for cancel button
11062
     * @param   array<array{addclass?:string,name?:string,label_key?:string}> $morebuttons      Add additional buttons between save and cancel
11063
     * @param   bool    $withoutdiv         Option to remove enclosing centered div
11064
     * @param   string  $morecss            More CSS
11065
     * @param   string  $dol_openinpopup    If the button are shown in a context of a page shown inside a popup, we put here the string name of popup.
11066
     * @return  string                      Html code with the buttons
11067
     */
11068
    public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = false, $morecss = '', $dol_openinpopup = '')
11069
    {
11070
        global $langs;
11071
11072
        $buttons = array();
11073
11074
        $save = array(
11075
            'name' => 'save',
11076
            'label_key' => $save_label,
11077
        );
11078
11079
        if ($save_label == 'Create' || $save_label == 'Add') {
11080
            $save['name'] = 'add';
11081
        } elseif ($save_label == 'Modify') {
11082
            $save['name'] = 'edit';
11083
        }
11084
11085
        $cancel = array(
11086
            'name' => 'cancel',
11087
            'label_key' => 'Cancel',
11088
        );
11089
11090
        !empty($save_label) ? $buttons[] = $save : '';
11091
11092
        if (!empty($morebuttons)) {
11093
            $buttons[] = $morebuttons;
11094
        }
11095
11096
        !empty($cancel_label) ? $buttons[] = $cancel : '';
11097
11098
        $retstring = $withoutdiv ? '' : '<div class="center">';
11099
11100
        foreach ($buttons as $button) {
11101
            $addclass = empty($button['addclass']) ? '' : $button['addclass'];
11102
            $retstring .= '<input type="submit" class="button button-' . $button['name'] . ($morecss ? ' ' . $morecss : '') . ' ' . $addclass . '" name="' . $button['name'] . '" value="' . dol_escape_htmltag($langs->trans($button['label_key'])) . '">';
11103
        }
11104
        $retstring .= $withoutdiv ? '' : '</div>';
11105
11106
        if ($dol_openinpopup) {
11107
            $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . $dol_openinpopup . ' context, so we enable the close of dialog on cancel -->' . "\n";
11108
            $retstring .= '<script nonce="' . getNonce() . '">';
11109
            $retstring .= 'jQuery(".button-cancel").click(function(e) {
11110
				e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . $dol_openinpopup . '\');
11111
				window.parent.jQuery(\'#idfordialog' . $dol_openinpopup . '\').dialog(\'close\');
11112
				 });';
11113
            $retstring .= '</script>';
11114
        }
11115
11116
        return $retstring;
11117
    }
11118
11119
11120
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11121
11122
    /**
11123
    * Load into cache list of invoice subtypes
11124
    *
11125
    * @return int             Nb of lines loaded, <0 if KO
11126
    */
11127
    public function load_cache_invoice_subtype()
11128
    {
11129
		// phpcs:enable
11130
        global $langs;
11131
11132
        $num = count($this->cache_invoice_subtype);
11133
        if ($num > 0) {
11134
            return 0; // Cache already loaded
11135
        }
11136
11137
        dol_syslog(__METHOD__, LOG_DEBUG);
11138
11139
        $sql = "SELECT rowid, code, label as label";
11140
        $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
11141
        $sql .= " WHERE active = 1";
11142
11143
        $resql = $this->db->query($sql);
11144
        if ($resql) {
11145
            $num = $this->db->num_rows($resql);
11146
            $i = 0;
11147
            while ($i < $num) {
11148
                $obj = $this->db->fetch_object($resql);
11149
11150
                // If translation exists, we use it, otherwise we take the default wording
11151
                $label = ($langs->trans("InvoiceSubtype" . $obj->rowid) != "InvoiceSubtype" . $obj->rowid) ? $langs->trans("InvoiceSubtype" . $obj->rowid) : (($obj->label != '-') ? $obj->label : '');
11152
                $this->cache_invoice_subtype[$obj->rowid]['rowid'] = $obj->rowid;
11153
                $this->cache_invoice_subtype[$obj->rowid]['code'] = $obj->code;
11154
                $this->cache_invoice_subtype[$obj->rowid]['label'] = $label;
11155
                $i++;
11156
            }
11157
11158
            $this->cache_invoice_subtype = dol_sort_array($this->cache_invoice_subtype, 'code', 'asc', 0, 0, 1);
11159
11160
            return $num;
11161
        } else {
11162
            dol_print_error($this->db);
11163
            return -1;
11164
        }
11165
    }
11166
11167
11168
    /**
11169
    * Return list of invoice subtypes.
11170
    *
11171
    * @param int        $selected       Id of invoice subtype to preselect by default
11172
    * @param string     $htmlname       Select field name
11173
    * @param int<0,1>   $addempty       Add an empty entry
11174
    * @param int<0,1>   $noinfoadmin    0=Add admin info, 1=Disable admin info
11175
    * @param string $morecss        Add more CSS on select tag
11176
    * @return string                String for the HTML select component
11177
    */
11178
    public function getSelectInvoiceSubtype($selected = 0, $htmlname = 'subtypeid', $addempty = 0, $noinfoadmin = 0, $morecss = '')
11179
    {
11180
        global $langs, $user;
11181
11182
        $out = '';
11183
        dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
11184
11185
        $this->load_cache_invoice_subtype();
11186
11187
        $out .= '<select id="' . $htmlname . '" class="flat selectsubtype' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
11188
        if ($addempty) {
11189
            $out .= '<option value="0">&nbsp;</option>';
11190
        }
11191
11192
        foreach ($this->cache_invoice_subtype as $rowid => $subtype) {
11193
            $label = $subtype['label'];
11194
            $out .= '<option value="' . $subtype['rowid'] . '"';
11195
            if ($selected == $subtype['rowid']) {
11196
                $out .= ' selected="selected"';
11197
            }
11198
            $out .= '>';
11199
            $out .= $label;
11200
            $out .= '</option>';
11201
        }
11202
11203
        $out .= '</select>';
11204
        if ($user->admin && empty($noinfoadmin)) {
11205
            $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
11206
        }
11207
        $out .= ajax_combobox($htmlname);
11208
11209
        return $out;
11210
    }
11211
}
11212