Passed
Push — EXTRACT_CLASSES ( 0382f2...c25e41 )
by Rafael
52:18
created

Form::form_project()   A

Complexity

Conditions 4

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 26
nop 10
dl 0
loc 37
rs 9.504
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

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

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

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