Passed
Pull Request — dev (#6)
by Rafael
79:24 queued 24:08
created

Form::selectForForms()   D

Complexity

Conditions 38

Size

Total Lines 145
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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