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

Form::loadCacheInputReason()   B

Complexity

Conditions 7

Size

Total Lines 43
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 29
nop 0
dl 0
loc 43
rs 8.5226
c 0
b 0
f 0
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