Passed
Push — EXTRACT_CLASSES ( ff35ec...a2ff75 )
by Rafael
48:13
created

FormProduct::selectWorkstations()   F

Complexity

Conditions 27
Paths 12960

Size

Total Lines 78
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 27
eloc 54
nc 12960
nop 12
dl 0
loc 78
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2008-2009  Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2015-2017  Francis Appels              <[email protected]>
5
 * Copyright (C) 2024		Frédéric France			    <[email protected]>
6
 * Copyright (C) 2024       Rafael San José             <[email protected]>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Dolibarr\Code\Product\Classes;
23
24
use DoliDB;
25
26
/**
27
 *  \file       htdocs/product/class/html.formproduct.class.php
28
 *  \brief      File for class with methods for building product related HTML components
29
 */
30
31
require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/entrepot.class.php';
32
33
/**
34
 *  Class with static methods for building HTML components related to products
35
 *  Only components common to products and services must be here.
36
 */
37
class FormProduct
38
{
39
    /**
40
     * @var DoliDB Database handler.
41
     */
42
    public $db;
43
44
    /**
45
     * @var string Error code (or message)
46
     */
47
    public $error = '';
48
49
    // Cache arrays
50
    public $cache_warehouses = array();
51
    public $cache_lot = array();
52
    public $cache_workstations = array();
53
54
55
    /**
56
     *  Constructor
57
     *
58
     *  @param  DoliDB  $db     Database handler
59
     */
60
    public function __construct($db)
61
    {
62
        $this->db = $db;
63
    }
64
65
66
    /**
67
     * Load in cache array list of warehouses
68
     * If fk_product is not 0, we do not use cache
69
     *
70
     * @param   int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
71
     * @param   string      $batch              Add quantity of batch stock in label for product with batch name batch, batch name precedes batch_id. Nothing if ''.
72
     * @param   string      $status             warehouse status filter, following comma separated filter options can be used
73
     *                                          'warehouseopen' = select products from open warehouses,
74
     *                                          'warehouseclosed' = select products from closed warehouses,
75
     *                                          'warehouseinternal' = select products from warehouses for internal correct/transfer only
76
     * @param   boolean     $sumStock           sum total stock of a warehouse, default true
77
     * @param   array       $exclude            warehouses ids to exclude
78
     * @param   bool|int    $stockMin           [=false] Value of minimum stock to filter (only warehouse with stock > stockMin are loaded) or false not not filter by minimum stock
79
     * @param   string      $orderBy            [='e.ref'] Order by
80
     * @return  int                             Nb of loaded lines, 0 if already loaded, <0 if KO
81
     * @throws  Exception
82
     */
83
    public function loadWarehouses($fk_product = 0, $batch = '', $status = '', $sumStock = true, $exclude = array(), $stockMin = false, $orderBy = 'e.ref')
84
    {
85
        global $conf, $langs;
86
87
        if (empty($fk_product) && count($this->cache_warehouses)) {
88
            return 0; // Cache already loaded and we do not want a list with information specific to a product
89
        }
90
91
        $warehouseStatus = array();
92
93
        if (preg_match('/warehouseclosed/', $status)) {
94
            $warehouseStatus[] = Entrepot::STATUS_CLOSED;
95
        }
96
        if (preg_match('/warehouseopen/', $status)) {
97
            $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
98
        }
99
        if (preg_match('/warehouseinternal/', $status)) {
100
            $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
101
        }
102
103
        $sql = "SELECT e.rowid, e.ref as label, e.description, e.fk_parent";
104
        if (!empty($fk_product) && $fk_product > 0) {
105
            if (!empty($batch)) {
106
                $sql .= ", pb.qty as stock";
107
            } else {
108
                $sql .= ", ps.reel as stock";
109
            }
110
        } elseif ($sumStock) {
111
            $sql .= ", sum(ps.reel) as stock";
112
        }
113
        $sql .= " FROM " . $this->db->prefix() . "entrepot as e";
114
        $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.fk_entrepot = e.rowid";
115
        if (!empty($fk_product) && $fk_product > 0) {
116
            $sql .= " AND ps.fk_product = " . ((int) $fk_product);
117
            if (!empty($batch)) {
118
                $sql .= " LEFT JOIN " . $this->db->prefix() . "product_batch as pb on pb.fk_product_stock = ps.rowid AND pb.batch = '" . $this->db->escape($batch) . "'";
119
            }
120
        }
121
        $sql .= " WHERE e.entity IN (" . getEntity('stock') . ")";
122
        if (count($warehouseStatus)) {
123
            $sql .= " AND e.statut IN (" . $this->db->sanitize(implode(',', $warehouseStatus)) . ")";
124
        } else {
125
            $sql .= " AND e.statut = 1";
126
        }
127
128
        if (is_array($exclude) && !empty($exclude)) {
129
            $sql .= ' AND e.rowid NOT IN(' . $this->db->sanitize(implode(',', $exclude)) . ')';
130
        }
131
132
        // minimum stock
133
        if ($stockMin !== false) {
134
            if (!empty($fk_product) && $fk_product > 0) {
135
                if (!empty($batch)) {
136
                    $sql .= " AND pb.qty > " . ((float) $stockMin);
137
                } else {
138
                    $sql .= " AND ps.reel > " . ((float) $stockMin);
139
                }
140
            }
141
        }
142
143
        if ($sumStock && empty($fk_product)) {
144
            $sql .= " GROUP BY e.rowid, e.ref, e.description, e.fk_parent";
145
146
            // minimum stock
147
            if ($stockMin !== false) {
148
                $sql .= " HAVING sum(ps.reel) > " . ((float) $stockMin);
149
            }
150
        }
151
        $sql .= " ORDER BY " . $orderBy;
152
153
        dol_syslog(get_class($this) . '::loadWarehouses', LOG_DEBUG);
154
        $resql = $this->db->query($sql);
155
        if ($resql) {
156
            $num = $this->db->num_rows($resql);
157
            $i = 0;
158
            while ($i < $num) {
159
                $obj = $this->db->fetch_object($resql);
160
                if ($sumStock) {
161
                    $obj->stock = price2num($obj->stock, 5);
162
                }
163
                $this->cache_warehouses[$obj->rowid]['id'] = $obj->rowid;
164
                $this->cache_warehouses[$obj->rowid]['label'] = $obj->label;
165
                $this->cache_warehouses[$obj->rowid]['parent_id'] = $obj->fk_parent;
166
                $this->cache_warehouses[$obj->rowid]['description'] = $obj->description;
167
                $this->cache_warehouses[$obj->rowid]['stock'] = $obj->stock;
168
                $i++;
169
            }
170
171
            // Full label init
172
            foreach ($this->cache_warehouses as $obj_rowid => $tab) {
173
                $this->cache_warehouses[$obj_rowid]['full_label'] = $this->get_parent_path($tab);
174
            }
175
176
            return $num;
177
        } else {
178
            dol_print_error($this->db);
179
            return -1;
180
        }
181
    }
182
183
    /**
184
     * Load in cache array list of workstations
185
     * If fk_product is not 0, we do not use cache
186
     *
187
     * @param   int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
188
     * @param   array       $exclude            warehouses ids to exclude
189
     * @param   string      $orderBy            [='e.ref'] Order by
190
     * @return  int                             Nb of loaded lines, 0 if already loaded, <0 if KO
191
     * @throws  Exception
192
     */
193
    public function loadWorkstations($fk_product = 0, $exclude = array(), $orderBy = 'w.ref')
194
    {
195
        global $conf, $langs;
196
197
        if (empty($fk_product) && count($this->cache_workstations)) {
198
            return 0; // Cache already loaded and we do not want a list with information specific to a product
199
        }
200
201
        $sql = "SELECT w.rowid, w.ref as ref, w.label as label, w.type, w.nb_operators_required,w.thm_operator_estimated,w.thm_machine_estimated";
202
        $sql .= " FROM " . $this->db->prefix() . "workstation_workstation as w";
203
        $sql .= " WHERE 1 = 1";
204
        if (!empty($fk_product) && $fk_product > 0) {
205
            $sql .= " AND w.fk_product = " . ((int) $fk_product);
206
        }
207
        $sql .= " AND w.entity IN (" . getEntity('workstation') . ")";
208
209
        if (is_array($exclude) && !empty($exclude)) {
210
            $sql .= ' AND w.rowid NOT IN(' . $this->db->sanitize(implode(',', $exclude)) . ')';
211
        }
212
213
        $sql .= " ORDER BY " . $orderBy;
214
215
        dol_syslog(get_class($this) . '::loadWorkstations', LOG_DEBUG);
216
        $resql = $this->db->query($sql);
217
        if ($resql) {
218
            $num = $this->db->num_rows($resql);
219
            $i = 0;
220
            while ($i < $num) {
221
                $obj = $this->db->fetch_object($resql);
222
223
                $this->cache_workstations[$obj->rowid]['id'] = $obj->rowid;
224
                $this->cache_workstations[$obj->rowid]['ref'] = $obj->ref;
225
                $this->cache_workstations[$obj->rowid]['label'] = $obj->label;
226
                $this->cache_workstations[$obj->rowid]['type'] = $obj->type;
227
                $this->cache_workstations[$obj->rowid]['nb_operators_required'] = $obj->nb_operators_required;
228
                $this->cache_workstations[$obj->rowid]['thm_operator_estimated'] = $obj->thm_operator_estimated;
229
                $this->cache_workstations[$obj->rowid]['thm_machine_estimated'] = $obj->thm_machine_estimated;
230
                $i++;
231
            }
232
233
            return $num;
234
        } else {
235
            dol_print_error($this->db);
236
            return -1;
237
        }
238
    }
239
240
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
241
    /**
242
     * Return full path to current warehouse in $tab (recursive function)
243
     *
244
     * @param   array   $tab            warehouse data in $this->cache_warehouses line
245
     * @param   string  $final_label    full label with all parents, separated by ' >> ' (completed on each call)
246
     * @return  string                  full label with all parents, separated by ' >> '
247
     */
248
    private function get_parent_path($tab, $final_label = '')
249
    {
250
		//phpcs:enable
251
        if (empty($final_label)) {
252
            $final_label = $tab['label'];
253
        }
254
255
        if (empty($tab['parent_id'])) {
256
            return $final_label;
257
        } else {
258
            if (!empty($this->cache_warehouses[$tab['parent_id']])) {
259
                $final_label = $this->cache_warehouses[$tab['parent_id']]['label'] . ' >> ' . $final_label;
260
                return $this->get_parent_path($this->cache_warehouses[$tab['parent_id']], $final_label);
261
            }
262
        }
263
264
        return $final_label;
265
    }
266
267
    /**
268
     *  Return list of warehouses
269
     *
270
     *  @param  string|int|array  $selected     Id of preselected warehouse ('' or '-1' for no value, 'ifone' and 'ifonenodefault' = select value if one value otherwise no value, '-2' to use the default value from setup)
271
     *  @param  string      $htmlname           Name of html select html
272
     *  @param  string      $filterstatus       warehouse status filter, following comma separated filter options can be used
273
     *                                          'warehouseopen' = select products from open warehouses,
274
     *                                          'warehouseclosed' = select products from closed warehouses,
275
     *                                          'warehouseinternal' = select products from warehouses for internal correct/transfer only
276
     *  @param  int         $empty              1=Can be empty, 0 if not
277
     *  @param  int         $disabled           1=Select is disabled
278
     *  @param  int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
279
     *  @param  string      $empty_label        Empty label if needed (only if $empty=1)
280
     *  @param  int         $showstock          1=Show stock count
281
     *  @param  int         $forcecombo         1=Force combo iso ajax select2
282
     *  @param  array       $events             Events to add to select2
283
     *  @param  string      $morecss            Add more css classes to HTML select
284
     *  @param  array       $exclude            Warehouses ids to exclude
285
     *  @param  int         $showfullpath       1=Show full path of name (parent ref into label), 0=Show only ref of current warehouse
286
     *  @param  bool|int    $stockMin           [=false] Value of minimum stock to filter (only warehouse with stock > stockMin are loaded) or false not not filter by minimum stock
287
     *  @param  string      $orderBy            [='e.ref'] Order by
288
     *  @param  int         $multiselect        1=Allow multiselect
289
     *  @return string                          HTML select
290
     *
291
     *  @throws Exception
292
     */
293
    public function selectWarehouses($selected = '', $htmlname = 'idwarehouse', $filterstatus = '', $empty = 0, $disabled = 0, $fk_product = 0, $empty_label = '', $showstock = 0, $forcecombo = 0, $events = array(), $morecss = 'minwidth200', $exclude = array(), $showfullpath = 1, $stockMin = false, $orderBy = 'e.ref', $multiselect = 0)
294
    {
295
        global $conf, $langs, $user, $hookmanager;
296
297
        dol_syslog(get_class($this) . "::selectWarehouses " . (is_array($selected) ? 'selected is array' : $selected) . ", $htmlname, $filterstatus, $empty, $disabled, $fk_product, $empty_label, $showstock, $forcecombo, $morecss", LOG_DEBUG);
298
299
        $out = '';
300
        if (!getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
301
            $filterstatus = '';
302
        }
303
        if (!empty($fk_product) && $fk_product > 0) {
304
            $this->cache_warehouses = array();
305
        }
306
307
        $this->loadWarehouses($fk_product, '', $filterstatus, true, $exclude, $stockMin, $orderBy);
308
        $nbofwarehouses = count($this->cache_warehouses);
309
310
        if ($conf->use_javascript_ajax && !$forcecombo) {
311
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
312
            $comboenhancement = ajax_combobox($htmlname, $events);
313
            $out .= $comboenhancement;
314
        }
315
316
        if (strpos($htmlname, 'search_') !== 0) {
317
            if (empty($user->fk_warehouse) || $user->fk_warehouse == -1) {
318
                if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
319
                    $selected = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
320
                }
321
            } else {
322
                if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER')) {
323
                    $selected = $user->fk_warehouse;
324
                }
325
            }
326
        }
327
328
        $out .= '<select ' . ($multiselect ? 'multiple ' : '') . 'class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled' : '');
329
        $out .= ' id="' . $htmlname . '" name="' . ($htmlname . ($multiselect ? '[]' : '') . ($disabled ? '_disabled' : '')) . '"';
330
        //$out .= ' placeholder="todo"';    // placeholder for select2 must be added by setting the id+placeholder js param when calling select2
331
        $out .= '>';
332
        if ($empty) {
333
            $out .= '<option value="-1">' . ($empty_label ? $empty_label : '&nbsp;') . '</option>';
334
        }
335
        foreach ($this->cache_warehouses as $id => $arraytypes) {
336
            $label = '';
337
            if ($showfullpath) {
338
                $label .= $arraytypes['full_label'];
339
            } else {
340
                $label .= $arraytypes['label'];
341
            }
342
            if (($fk_product || ($showstock > 0)) && ($arraytypes['stock'] != 0 || ($showstock > 0))) {
343
                if ($arraytypes['stock'] <= 0) {
344
                    $label .= ' <span class="text-warning">(' . $langs->trans("Stock") . ':' . $arraytypes['stock'] . ')</span>';
345
                } else {
346
                    $label .= ' <span class="opacitymedium">(' . $langs->trans("Stock") . ':' . $arraytypes['stock'] . ')</span>';
347
                }
348
            }
349
350
            $out .= '<option value="' . $id . '"';
351
            if (is_array($selected)) {
352
                if (in_array($id, $selected)) {
353
                    $out .= ' selected';
354
                }
355
            } else {
356
                if ($selected == $id || (!empty($selected) && preg_match('/^ifone/', $selected) && $nbofwarehouses == 1)) {
357
                    $out .= ' selected';
358
                }
359
            }
360
            $out .= ' data-html="' . dol_escape_htmltag($label) . '"';
361
            $out .= '>';
362
            $out .= $label;
363
            $out .= '</option>';
364
        }
365
        $out .= '</select>';
366
        if ($disabled) {
367
            $out .= '<input type="hidden" name="' . $htmlname . '" value="' . (($selected > 0) ? $selected : '') . '">';
368
        }
369
370
        $parameters = array(
371
            'selected' => $selected,
372
            'htmlname' => $htmlname,
373
            'filterstatus' => $filterstatus,
374
            'empty' => $empty,
375
            'disabled ' => $disabled,
376
            'fk_product' => $fk_product,
377
            'empty_label' => $empty_label,
378
            'showstock' => $showstock,
379
            'forcecombo' => $forcecombo,
380
            'events' => $events,
381
            'morecss' => $morecss,
382
            'exclude' => $exclude,
383
            'showfullpath' => $showfullpath,
384
            'stockMin' => $stockMin,
385
            'orderBy' => $orderBy
386
        );
387
388
        $reshook = $hookmanager->executeHooks('selectWarehouses', $parameters, $this);
389
        if ($reshook > 0) {
390
            $out = $hookmanager->resPrint;
391
        } elseif ($reshook == 0) {
392
            $out .= $hookmanager->resPrint;
393
        }
394
395
        return $out;
396
    }
397
398
    /**
399
     *  Return list of workstations
400
     *
401
     *  @param  string|int  $selected           Id of preselected warehouse ('' or '-1' for no value, 'ifone' and 'ifonenodefault' = select value if one value otherwise no value, '-2' to use the default value from setup)
402
     *  @param  string      $htmlname           Name of html select html
403
     *  @param  int         $empty              1=Can be empty, 0 if not
404
     *  @param  int         $disabled           1=Select is disabled
405
     *  @param  int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
406
     *  @param  string      $empty_label        Empty label if needed (only if $empty=1)
407
     *  @param  int         $forcecombo         1=Force combo iso ajax select2
408
     *  @param  array       $events                     Events to add to select2
409
     *  @param  string      $morecss                    Add more css classes to HTML select
410
     *  @param  array       $exclude            Warehouses ids to exclude
411
     *  @param  int         $showfullpath       1=Show full path of name (parent ref into label), 0=Show only ref of current warehouse
412
     *  @param  string      $orderBy            [='e.ref'] Order by
413
     *  @return string                          HTML select
414
     *
415
     *  @throws Exception
416
     */
417
    public function selectWorkstations($selected = '', $htmlname = 'idworkstations', $empty = 0, $disabled = 0, $fk_product = 0, $empty_label = '', $forcecombo = 0, $events = array(), $morecss = 'minwidth200', $exclude = array(), $showfullpath = 1, $orderBy = 'e.ref')
418
    {
419
        global $conf, $langs, $user, $hookmanager;
420
421
        dol_syslog(get_class($this) . "::selectWorkstations $selected, $htmlname, $empty, $disabled, $fk_product, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
422
423
        $filterstatus = '';
424
        $out = '';
425
        if (!empty($fk_product) && $fk_product > 0) {
426
            $this->cache_workstations = array();
427
        }
428
429
        $this->loadWorkstations($fk_product);
430
        $nbofworkstations = count($this->cache_workstations);
431
432
        if ($conf->use_javascript_ajax && !$forcecombo) {
433
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
434
            $comboenhancement = ajax_combobox($htmlname, $events);
435
            $out .= $comboenhancement;
436
        }
437
438
        if (strpos($htmlname, 'search_') !== 0) {
439
            if (empty($user->fk_workstation) || $user->fk_workstation == -1) {
440
                if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
441
                    $selected = getDolGlobalString('MAIN_DEFAULT_WORKSTATION');
442
                }
443
            } else {
444
                if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
445
                    $selected = $user->fk_workstation;
446
                }
447
            }
448
        }
449
450
        $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled' : '') . ' id="' . $htmlname . '" name="' . ($htmlname . ($disabled ? '_disabled' : '')) . '">';
451
        if ($empty) {
452
            $out .= '<option value="-1">' . ($empty_label ? $empty_label : '&nbsp;') . '</option>';
453
        }
454
        foreach ($this->cache_workstations as $id => $arraytypes) {
455
            $label = $arraytypes['label'];
456
457
            $out .= '<option value="' . $id . '"';
458
            if ($selected == $id || (preg_match('/^ifone/', $selected) && $nbofworkstations == 1)) {
459
                $out .= ' selected';
460
            }
461
            $out .= ' data-html="' . dol_escape_htmltag($label) . '"';
462
            $out .= '>';
463
            $out .= $label;
464
            $out .= '</option>';
465
        }
466
        $out .= '</select>';
467
        if ($disabled) {
468
            $out .= '<input type="hidden" name="' . $htmlname . '" value="' . (($selected > 0) ? $selected : '') . '">';
469
        }
470
471
        $parameters = array(
472
            'selected' => $selected,
473
            'htmlname' => $htmlname,
474
            'filterstatus' => $filterstatus,
475
            'empty' => $empty,
476
            'disabled ' => $disabled,
477
            'fk_product' => $fk_product,
478
            'empty_label' => $empty_label,
479
            'forcecombo' => $forcecombo,
480
            'events' => $events,
481
            'morecss' => $morecss,
482
            'exclude' => $exclude,
483
            'showfullpath' => $showfullpath,
484
            'orderBy' => $orderBy
485
        );
486
487
        $reshook = $hookmanager->executeHooks('selectWorkstations', $parameters, $this);
488
        if ($reshook > 0) {
489
            $out = $hookmanager->resPrint;
490
        } elseif ($reshook == 0) {
491
            $out .= $hookmanager->resPrint;
492
        }
493
494
        return $out;
495
    }
496
497
    /**
498
     *    Display form to select warehouse
499
     *
500
     *    @param    string      $page        Page
501
     *    @param    string|int  $selected    Id of warehouse
502
     *    @param    string      $htmlname    Name of select html field
503
     *    @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.
504
     *    @return   void
505
     */
506
    public function formSelectWarehouses($page, $selected = '', $htmlname = 'warehouse_id', $addempty = 0)
507
    {
508
        global $langs;
509
        if ($htmlname != "none") {
510
            print '<form method="POST" action="' . $page . '">';
511
            print '<input type="hidden" name="action" value="setwarehouse">';
512
            print '<input type="hidden" name="token" value="' . newToken() . '">';
513
            print '<table class="nobordernopadding">';
514
            print '<tr><td>';
515
            print $this->selectWarehouses($selected, $htmlname, '', $addempty);
516
            print '</td>';
517
            print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
518
            print '</tr></table></form>';
519
        } else {
520
            if ($selected) {
521
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/entrepot.class.php';
522
                $warehousestatic = new Entrepot($this->db);
523
                $warehousestatic->fetch($selected);
524
                print $warehousestatic->getNomUrl();
525
            } else {
526
                print "&nbsp;";
527
            }
528
        }
529
    }
530
531
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
532
    /**
533
     *  Output a combo box with list of units
534
     *  Currently the units are not define in the DB
535
     *
536
     *  @param  string      $name               Name of HTML field
537
     *  @param  string      $measuring_style    Unit to show: weight, size, surface, volume, time
538
     *  @param  string      $selected            Preselected value
539
     *  @param  int         $adddefault         Add empty unit called "Default"
540
     *  @param  int         $mode               1=Use short label as value, 0=Use rowid
541
     *  @return void
542
     *  @deprecated
543
     */
544
    public function select_measuring_units($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0)
545
    {
546
		//phpcs:enable
547
        print $this->selectMeasuringUnits($name, $measuring_style, $selected, $adddefault, $mode);
548
    }
549
550
    /**
551
     *  Return a combo box with list of units
552
     *  Units labels are defined in llx_c_units
553
     *
554
     *  @param  string      $name                Name of HTML field
555
     *  @param  string      $measuring_style     Unit to show: weight, size, surface, volume, time
556
     *  @param  string      $selected            Preselected value
557
     *  @param  int|string  $adddefault          1=Add empty unit called "Default", ''=Add empty value
558
     *  @param  int         $mode                1=Use short label as value, 0=Use rowid, 2=Use scale (power)
559
     *  @param  string      $morecss             More CSS
560
     *  @return string|-1
0 ignored issues
show
Documentation Bug introduced by
The doc comment string|-1 at position 2 could not be parsed: Unknown type name '-1' at position 2 in string|-1.
Loading history...
561
     */
562
    public function selectMeasuringUnits($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0, $morecss = 'maxwidth125')
563
    {
564
        global $langs, $db;
565
566
        $langs->load("other");
567
568
        $return = '';
569
570
        // TODO Use a cache
571
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/cunits.class.php';
572
        $measuringUnits = new CUnits($db);
573
574
        $filter = array();
575
        $filter['t.active'] = 1;
576
        if ($measuring_style) {
577
            $filter['t.unit_type'] = $measuring_style;
578
        }
579
580
        $result = $measuringUnits->fetchAll(
581
            '',
582
            '',
583
            0,
584
            0,
585
            $filter
586
        );
587
        if ($result < 0) {
588
            dol_print_error($db);
589
            return -1;
590
        } else {
591
            $return .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $name . '" id="' . $name . '">';
592
            if ($adddefault || $adddefault === '') {
593
                $return .= '<option value="0"' . ($selected === '0' ? ' selected' : '') . '>' . ($adddefault ? '(' . $langs->trans("Default") . ')' : '') . '</option>';
594
            }
595
596
            foreach ($measuringUnits->records as $lines) {
597
                $return .= '<option value="';
598
                if ($mode == 1) {
599
                    $return .= $lines->short_label;
600
                } elseif ($mode == 2) {
601
                    $return .= $lines->scale;
602
                } else {
603
                    $return .= $lines->id;
604
                }
605
                $return .= '"';
606
                if ($mode == 1 && $lines->short_label == $selected) {
607
                    $return .= ' selected';
608
                } elseif ($mode == 2 && $lines->scale == $selected) {
609
                    $return .= ' selected';
610
                } elseif ($mode == 0 && $lines->id == $selected) {
611
                    $return .= ' selected';
612
                }
613
                $return .= '>';
614
                if ($measuring_style == 'time') {
615
                    $return .= $langs->trans(ucfirst($lines->label));
616
                } else {
617
                    $return .= $langs->trans($lines->label);
618
                }
619
                $return .= '</option>';
620
            }
621
            $return .= '</select>';
622
        }
623
624
        $return .= ajax_combobox($name);
625
626
        return $return;
627
    }
628
629
    /**
630
     *  Return a combo box with list of units
631
     *  NAture of product labels are defined in llx_c_product_nature
632
     *
633
     *  @param  string      $name                Name of HTML field
634
     *  @param  string      $selected             Preselected value
635
     *  @param  int         $mode                1=Use label as value, 0=Use code
636
     *  @param  int         $showempty           1=show empty value, 0= no
637
     *  @return string|int
638
     */
639
    public function selectProductNature($name = 'finished', $selected = '', $mode = 0, $showempty = 1)
640
    {
641
        global $langs, $db;
642
643
        $langs->load('products');
644
645
        $return = '';
646
647
        // TODO Use a cache
648
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/cproductnature.class.php';
649
        $productNature = new CProductNature($db);
650
651
        $filter = array();
652
        $filter['t.active'] = 1;
653
654
        $result = $productNature->fetchAll('', '', 0, 0, $filter);
655
656
        if ($result < 0) {
657
            dol_print_error($db);
658
            return -1;
659
        } else {
660
            $return .= '<select class="flat" name="' . $name . '" id="' . $name . '">';
661
            if ($showempty || ($selected == '' || $selected == '-1')) {
662
                $return .= '<option value="-1"';
663
                if ($selected == '' || $selected == '-1') {
664
                    $return .= ' selected';
665
                }
666
                $return .= '></option>';
667
            }
668
            if (!empty($productNature->records) && is_array($productNature->records)) {
669
                foreach ($productNature->records as $lines) {
670
                    $return .= '<option value="';
671
                    if ($mode == 1) {
672
                        $return .= $lines->label;
673
                    } else {
674
                        $return .= $lines->code;
675
                    }
676
677
                    $return .= '"';
678
679
                    if ($mode == 1 && $lines->label == $selected) {
680
                        $return .= ' selected';
681
                    } elseif ($lines->code == $selected) {
682
                        $return .= ' selected';
683
                    }
684
685
                    $return .= '>';
686
                    $return .= $langs->trans($lines->label);
687
                    $return .= '</option>';
688
                }
689
            }
690
            $return .= '</select>';
691
        }
692
693
        $return .= ajax_combobox($name);
694
695
        return $return;
696
    }
697
698
    /**
699
     *  Return list of lot numbers (stock from product_batch) with stock location and stock qty
700
     *
701
     *  @param  string|int  $selected   Id of preselected lot stock id ('' for no value, 'ifone'=select value if one value otherwise no value)
702
     *  @param  string  $htmlname       Name of html select html
703
     *  @param  string  $filterstatus   lot status filter, following comma separated filter options can be used
704
     *  @param  int     $empty          1=Can be empty, 0 if not
705
     *  @param  int     $disabled       1=Select is disabled
706
     *  @param  int     $fk_product     show lot numbers of product with id fk_product. All from objectLines if 0.
707
     *  @param  int     $fk_entrepot    filter lot numbers for warehouse with id fk_entrepot. All if 0.
708
     *  @param  array   $objectLines    Only cache lot numbers for products in lines of object. If no lines only for fk_product. If no fk_product, all.
709
     *  @param  string  $empty_label    Empty label if needed (only if $empty=1)
710
     *  @param  int     $forcecombo     1=Force combo iso ajax select2
711
     *  @param  array   $events         Events to add to select2
712
     *  @param  string  $morecss        Add more css classes to HTML select
713
     *
714
     *  @return string                  HTML select
715
     */
716
    public function selectLotStock($selected = '', $htmlname = 'batch_id', $filterstatus = '', $empty = 0, $disabled = 0, $fk_product = 0, $fk_entrepot = 0, $objectLines = array(), $empty_label = '', $forcecombo = 0, $events = array(), $morecss = 'minwidth200')
717
    {
718
        global $conf, $langs;
719
720
        dol_syslog(get_class($this) . "::selectLotStock $selected, $htmlname, $filterstatus, $empty, $disabled, $fk_product, $fk_entrepot, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
721
722
        $out = '';
723
        $productIdArray = array();
724
        if (!is_array($objectLines) || !count($objectLines)) {
725
            if (!empty($fk_product) && $fk_product > 0) {
726
                $productIdArray[] = (int) $fk_product;
727
            }
728
        } else {
729
            foreach ($objectLines as $line) {
730
                if ($line->fk_product) {
731
                    $productIdArray[] = $line->fk_product;
732
                }
733
            }
734
        }
735
736
        $nboflot = $this->loadLotStock($productIdArray);
737
738
        if ($conf->use_javascript_ajax && !$forcecombo) {
739
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
740
            $comboenhancement = ajax_combobox($htmlname, $events);
741
            $out .= $comboenhancement;
742
        }
743
744
        $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled' : '') . ' id="' . $htmlname . '" name="' . ($htmlname . ($disabled ? '_disabled' : '')) . '">';
745
        if ($empty) {
746
            $out .= '<option value="-1">' . ($empty_label ? $empty_label : '&nbsp;') . '</option>';
747
        }
748
        if (!empty($fk_product) && $fk_product > 0) {
749
            $productIdArray = array((int) $fk_product); // only show lot stock for product
750
        } else {
751
            foreach ($this->cache_lot as $key => $value) {
752
                $productIdArray[] = $key;
753
            }
754
        }
755
756
        foreach ($productIdArray as $productId) {
757
            foreach ($this->cache_lot[$productId] as $id => $arraytypes) {
758
                if (empty($fk_entrepot) || $fk_entrepot == $arraytypes['entrepot_id']) {
759
                    $label = $arraytypes['entrepot_label'] . ' - ';
760
                    $label .= $arraytypes['batch'];
761
                    if ($arraytypes['qty'] <= 0) {
762
                        $label .= ' <span class=\'text-warning\'>(' . $langs->trans("Stock") . ' ' . $arraytypes['qty'] . ')</span>';
763
                    } else {
764
                        $label .= ' <span class=\'opacitymedium\'>(' . $langs->trans("Stock") . ' ' . $arraytypes['qty'] . ')</span>';
765
                    }
766
767
                    $out .= '<option value="' . $id . '"';
768
769
                    if ($selected == $id || ($selected == 'ifone' && $nboflot == 1)) {
770
                        $out .= ' selected';
771
                    }
772
                    $out .= ' data-html="' . dol_escape_htmltag($label) . '"';
773
                    $out .= '>';
774
                    $out .= $label;
775
                    $out .= '</option>';
776
                }
777
            }
778
        }
779
        $out .= '</select>';
780
        if ($disabled) {
781
            $out .= '<input type="hidden" name="' . $htmlname . '" value="' . (($selected > 0) ? $selected : '') . '">';
782
        }
783
784
        return $out;
785
    }
786
787
788
789
    /**
790
     *  Return list of lot numbers (stock from product_batch) for product and warehouse.
791
     *
792
     *  @param  string  $htmlname       Name of key that is inside attribute "list" of an input text field.
793
     *  @param  int     $empty          1=Can be empty, 0 if not
794
     *  @param  int     $fk_product     show lot numbers of product with id fk_product. All from objectLines if 0.
795
     *  @param  int     $fk_entrepot    filter lot numbers for warehouse with id fk_entrepot. All if 0.
796
     *  @param  array   $objectLines    Only cache lot numbers for products in lines of object. If no lines only for fk_product. If no fk_product, all.
797
     *  @return string                  HTML datalist
798
     */
799
    public function selectLotDataList($htmlname = 'batch_id', $empty = 0, $fk_product = 0, $fk_entrepot = 0, $objectLines = array())
800
    {
801
        global $langs, $hookmanager;
802
803
        dol_syslog(get_class($this) . "::selectLotDataList $htmlname, $empty, $fk_product, $fk_entrepot", LOG_DEBUG);
804
805
        $out = '';
806
        $productIdArray = array();
807
        if (!is_array($objectLines) || !count($objectLines)) {
808
            if (!empty($fk_product) && $fk_product > 0) {
809
                $productIdArray[] = (int) $fk_product;
810
            }
811
        } else {
812
            foreach ($objectLines as $line) {
813
                if ($line->fk_product) {
814
                    $productIdArray[] = $line->fk_product;
815
                }
816
            }
817
        }
818
819
        $nboflot = $this->loadLotStock($productIdArray);
820
821
        if (!empty($fk_product) && $fk_product > 0) {
822
            $productIdArray = array((int) $fk_product); // only show lot stock for product
823
        } else {
824
            foreach ($this->cache_lot as $key => $value) {
825
                $productIdArray[] = $key;
826
            }
827
        }
828
829
        if (empty($hookmanager)) {
830
            include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
831
            $hookmanager = new HookManager($this->db);
832
        }
833
        $hookmanager->initHooks(array('productdao'));
834
        $parameters = array('productIdArray' => $productIdArray, 'htmlname' => $htmlname);
835
        $reshook = $hookmanager->executeHooks('selectLotDataList', $parameters, $this);
836
        if ($reshook < 0) {
837
            return $hookmanager->error;
838
        } elseif ($reshook > 0) {
839
            return $hookmanager->resPrint;
840
        } else {
841
            $out .= $hookmanager->resPrint;
842
        }
843
844
        $out .= '<datalist id="' . $htmlname . '" >';
845
        foreach ($productIdArray as $productId) {
846
            if (array_key_exists($productId, $this->cache_lot)) {
847
                foreach ($this->cache_lot[$productId] as $id => $arraytypes) {
848
                    if (empty($fk_entrepot) || $fk_entrepot == $arraytypes['entrepot_id']) {
849
                        $label = $arraytypes['entrepot_label'] . ' - ';
850
                        $label .= $arraytypes['batch'];
851
                        $out .= '<option data-warehouse="' . dol_escape_htmltag($label) . '" value="' . $arraytypes['batch'] . '">(' . $langs->trans('Stock Total') . ': ' . $arraytypes['qty'] . ')</option>';
852
                    }
853
                }
854
            }
855
        }
856
        $out .= '</datalist>';
857
858
        return $out;
859
    }
860
861
862
    /**
863
     * Load in cache array list of lot available in stock from a given list of products
864
     *
865
     * @param   array   $productIdArray     array of product id's from who to get lot numbers. A
866
     *
867
     * @return  int                         Nb of loaded lines, 0 if nothing loaded, <0 if KO
868
     */
869
    private function loadLotStock($productIdArray = array())
870
    {
871
        global $conf, $langs;
872
873
        $cacheLoaded = false;
874
        if (empty($productIdArray)) {
875
            // only Load lot stock for given products
876
            $this->cache_lot = array();
877
            return 0;
878
        }
879
        if (count($productIdArray) && count($this->cache_lot)) {
880
            // check cache already loaded for product id's
881
            foreach ($productIdArray as $productId) {
882
                $cacheLoaded = !empty($this->cache_lot[$productId]) ? true : false;
883
            }
884
        }
885
        if ($cacheLoaded) {
886
            return count($this->cache_lot);
887
        } else {
888
            // clear cache
889
            $this->cache_lot = array();
890
            $productIdList = implode(',', $productIdArray);
891
892
            $batch_count = 0;
893
            global $hookmanager;
894
            if (empty($hookmanager)) {
895
                include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
896
                $hookmanager = new HookManager($this->db);
897
            }
898
            $hookmanager->initHooks(array('productdao'));
899
            $parameters = array('productIdList' => $productIdList);
900
            $reshook = $hookmanager->executeHooks('loadLotStock', $parameters, $this);
901
            if ($reshook < 0) {
902
                $this->error = $hookmanager->error;
903
                return -1;
904
            }
905
            if (!empty($hookmanager->resArray['batch_list']) && is_array($hookmanager->resArray['batch_list'])) {
906
                $this->cache_lot = $hookmanager->resArray['batch_list'];
907
                $batch_count = (int) $hookmanager->resArray['batch_count'];
908
            }
909
            if ($reshook > 0) {
910
                return $batch_count;
911
            }
912
913
            $sql = "SELECT pb.batch, pb.rowid, ps.fk_entrepot, pb.qty, e.ref as label, ps.fk_product";
914
            $sql .= " FROM " . $this->db->prefix() . "product_batch as pb";
915
            $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.rowid = pb.fk_product_stock";
916
            $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e on e.rowid = ps.fk_entrepot AND e.entity IN (" . getEntity('stock') . ")";
917
            if (!empty($productIdList)) {
918
                $sql .= " WHERE ps.fk_product IN (" . $this->db->sanitize($productIdList) . ")";
919
            }
920
            $sql .= " ORDER BY e.ref, pb.batch";
921
922
            dol_syslog(get_class($this) . '::loadLotStock', LOG_DEBUG);
923
            $resql = $this->db->query($sql);
924
            if ($resql) {
925
                $num = $this->db->num_rows($resql);
926
                $i = 0;
927
                while ($i < $num) {
928
                    $obj = $this->db->fetch_object($resql);
929
                    $this->cache_lot[$obj->fk_product][$obj->rowid]['id'] = $obj->rowid;
930
                    $this->cache_lot[$obj->fk_product][$obj->rowid]['batch'] = $obj->batch;
931
                    $this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_id'] = $obj->fk_entrepot;
932
                    $this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_label'] = $obj->label;
933
                    $this->cache_lot[$obj->fk_product][$obj->rowid]['qty'] = $obj->qty;
934
                    $i++;
935
                }
936
937
                return $batch_count + $num;
938
            } else {
939
                dol_print_error($this->db);
940
                return -1;
941
            }
942
        }
943
    }
944
}
945