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