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

Form   F

Complexity

Total Complexity 2459

Size/Duplication

Total Lines 11132
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 5773
dl 0
loc 11132
rs 0.8
c 0
b 0
f 0
wmc 2459

112 Methods

Rating   Name   Duplication   Size   Complexity  
A constructProjectListOption() 0 24 4
A form_project() 0 36 4
A form_multicurrency_code() 0 14 3
A showbarcode() 0 25 6
B select_produits_fournisseurs() 0 26 8
A formSelectAccount() 0 24 5
C selectyesno() 0 32 13
A formSelectShippingMethod() 0 19 3
A showFilterButtons() 0 8 1
A select_users() 0 4 1
F constructProductListOption() 0 320 89
F selectcontacts() 0 246 84
A constructTicketListOption() 0 21 4
A select_date() 0 11 2
B load_cache_conditions_paiements() 0 39 6
D selectMembersList() 0 107 21
B load_cache_invoice_subtype() 0 37 6
F select_dolusers() 0 259 98
C getSelectConditionsPaiements() 0 73 16
F editfieldkey() 0 82 34
A __construct() 0 3 1
F textwithpicto() 0 64 29
A showCheckAddButtons() 0 32 3
F select_country() 0 116 40
F select_bom() 0 59 13
A selectTypeDuration() 0 31 4
F selectDate() 0 429 123
A select_currency() 0 4 1
F editfieldval() 0 184 92
B load_cache_types_paiements() 0 41 6
D select_dolresources_forevent() 0 70 14
C selectArrayFilter() 0 101 13
F load_tva() 0 200 57
A form_modes_reglement() 0 31 5
D select_dolgroups() 0 105 43
F select_produits_fournisseurs_list() 0 433 102
C selectProjects() 0 46 12
F textwithtooltip() 0 94 40
D selectTicketsList() 0 104 19
A showFilterAndCheckAddButtons() 0 7 2
B form_conditions_reglement() 0 42 8
F select_produits() 0 138 28
C selectShippingMethod() 0 47 14
D selectProjectsList() 0 104 19
B loadCacheInputReason() 0 43 7
A load_cache_types_fees() 0 38 5
D select_all_categories() 0 88 19
A constructMemberListOption() 0 20 4
D select_contact() 0 55 14
D load_cache_vatrates() 0 74 21
A select_export_model() 0 30 5
A form_confirm() 0 5 1
B selectExpense() 0 36 8
F editInPlace() 0 125 37
F formconfirm() 0 396 112
A selectPriceBaseType() 0 23 4
B showCategories() 0 27 8
A selectDateToDate() 0 10 2
B selectSituationInvoices() 0 44 9
F select_thirdparty_list() 0 226 71
C select_remises() 0 72 16
F showLinkToObjectBlock() 0 247 42
A formSelectTransportMode() 0 16 3
D selectForForms() 0 147 38
D select_duration() 0 76 25
B selectUnits() 0 36 9
C selectExpenseCategories() 0 80 15
F showphoto() 0 182 72
D selectTransportMode() 0 55 24
B getSelectInvoiceSubtype() 0 32 7
A form_contacts() 0 27 5
C buttonsSaveCancel() 0 49 13
A form_multicurrency_rate() 0 24 6
C selectEstablishments() 0 56 14
C select_incoterms() 0 72 15
A form_users() 0 19 3
C selectTickets() 0 46 12
C selectMultiCurrency() 0 52 13
B load_cache_availability() 0 39 6
F showrefnav() 0 190 65
D multiSelectArrayWithCheckbox() 0 108 24
A form_availability() 0 18 3
D select_product_fourn_price() 0 95 20
F selectMassAction() 0 96 17
C searchComponent() 0 141 12
A selectAvailabilityDelay() 0 26 6
F select_company() 0 49 16
F selectForFormsList() 0 184 52
D selectarray() 0 132 52
A select_type_fees() 0 31 6
F select_type_of_lines() 0 54 28
D select_comptes() 0 73 22
D form_remise_dispo() 0 80 26
B selectCurrency() 0 42 9
F select_produits_list() 0 346 78
D multiselectarray() 0 134 43
F showLinkedObjectBlock() 0 148 38
D select_types_paiements() 0 89 34
A formInputReason() 0 21 5
A form_thirdparty() 0 30 4
A selectExpenseRanges() 0 24 5
C selectMembers() 0 49 12
A selectModelMail() 0 34 6
F select_dolusers_forevent() 0 73 18
C selectInvoiceRec() 0 79 17
A select_conditions_paiements() 0 8 2
A form_date() 0 32 5
B load_cache_transport_mode() 0 40 6
B selectInputReason() 0 29 11
D selectInvoice() 0 130 35
C widgetForTranslation() 0 59 12
B selectArrayAjax() 0 84 9

How to fix   Complexity   

Complex Class

Complex classes like Form often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Form, and based on these observations, apply Extract Interface, too.

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

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

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

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

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

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

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

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

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

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

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

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

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