FormProduct::selectWorkstations()   F
last analyzed

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\CProductNature;
25
use Dolibarr\Code\Core\Classes\CUnits;
26
use DoliDB;
27
use Exception;
28
29
/**
30
 *  \file       htdocs/product/class/html.formproduct.class.php
31
 *  \brief      File for class with methods for building product related HTML components
32
 */
33
34
35
/**
36
 *  Class with static methods for building HTML components related to products
37
 *  Only components common to products and services must be here.
38
 */
39
class FormProduct
40
{
41
    /**
42
     * @var DoliDB Database handler.
43
     */
44
    public $db;
45
46
    /**
47
     * @var string Error code (or message)
48
     */
49
    public $error = '';
50
51
    // Cache arrays
52
    public $cache_warehouses = array();
53
    public $cache_lot = array();
54
    public $cache_workstations = array();
55
56
57
    /**
58
     *  Constructor
59
     *
60
     *  @param  DoliDB  $db     Database handler
61
     */
62
    public function __construct($db)
63
    {
64
        $this->db = $db;
65
    }
66
67
68
    /**
69
     * Load in cache array list of warehouses
70
     * If fk_product is not 0, we do not use cache
71
     *
72
     * @param   int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
73
     * @param   string      $batch              Add quantity of batch stock in label for product with batch name batch, batch name precedes batch_id. Nothing if ''.
74
     * @param   string      $status             warehouse status filter, following comma separated filter options can be used
75
     *                                          'warehouseopen' = select products from open warehouses,
76
     *                                          'warehouseclosed' = select products from closed warehouses,
77
     *                                          'warehouseinternal' = select products from warehouses for internal correct/transfer only
78
     * @param   boolean     $sumStock           sum total stock of a warehouse, default true
79
     * @param   array       $exclude            warehouses ids to exclude
80
     * @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
81
     * @param   string      $orderBy            [='e.ref'] Order by
82
     * @return  int                             Nb of loaded lines, 0 if already loaded, <0 if KO
83
     * @throws  Exception
84
     */
85
    public function loadWarehouses($fk_product = 0, $batch = '', $status = '', $sumStock = true, $exclude = array(), $stockMin = false, $orderBy = 'e.ref')
86
    {
87
        global $conf, $langs;
88
89
        if (empty($fk_product) && count($this->cache_warehouses)) {
90
            return 0; // Cache already loaded and we do not want a list with information specific to a product
91
        }
92
93
        $warehouseStatus = array();
94
95
        if (preg_match('/warehouseclosed/', $status)) {
96
            $warehouseStatus[] = Entrepot::STATUS_CLOSED;
97
        }
98
        if (preg_match('/warehouseopen/', $status)) {
99
            $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
100
        }
101
        if (preg_match('/warehouseinternal/', $status)) {
102
            $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
103
        }
104
105
        $sql = "SELECT e.rowid, e.ref as label, e.description, e.fk_parent";
106
        if (!empty($fk_product) && $fk_product > 0) {
107
            if (!empty($batch)) {
108
                $sql .= ", pb.qty as stock";
109
            } else {
110
                $sql .= ", ps.reel as stock";
111
            }
112
        } elseif ($sumStock) {
113
            $sql .= ", sum(ps.reel) as stock";
114
        }
115
        $sql .= " FROM " . $this->db->prefix() . "entrepot as e";
116
        $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.fk_entrepot = e.rowid";
117
        if (!empty($fk_product) && $fk_product > 0) {
118
            $sql .= " AND ps.fk_product = " . ((int) $fk_product);
119
            if (!empty($batch)) {
120
                $sql .= " LEFT JOIN " . $this->db->prefix() . "product_batch as pb on pb.fk_product_stock = ps.rowid AND pb.batch = '" . $this->db->escape($batch) . "'";
121
            }
122
        }
123
        $sql .= " WHERE e.entity IN (" . getEntity('stock') . ")";
124
        if (count($warehouseStatus)) {
125
            $sql .= " AND e.statut IN (" . $this->db->sanitize(implode(',', $warehouseStatus)) . ")";
126
        } else {
127
            $sql .= " AND e.statut = 1";
128
        }
129
130
        if (is_array($exclude) && !empty($exclude)) {
131
            $sql .= ' AND e.rowid NOT IN(' . $this->db->sanitize(implode(',', $exclude)) . ')';
132
        }
133
134
        // minimum stock
135
        if ($stockMin !== false) {
136
            if (!empty($fk_product) && $fk_product > 0) {
137
                if (!empty($batch)) {
138
                    $sql .= " AND pb.qty > " . ((float) $stockMin);
139
                } else {
140
                    $sql .= " AND ps.reel > " . ((float) $stockMin);
141
                }
142
            }
143
        }
144
145
        if ($sumStock && empty($fk_product)) {
146
            $sql .= " GROUP BY e.rowid, e.ref, e.description, e.fk_parent";
147
148
            // minimum stock
149
            if ($stockMin !== false) {
150
                $sql .= " HAVING sum(ps.reel) > " . ((float) $stockMin);
151
            }
152
        }
153
        $sql .= " ORDER BY " . $orderBy;
154
155
        dol_syslog(get_only_class($this) . '::loadWarehouses', LOG_DEBUG);
156
        $resql = $this->db->query($sql);
157
        if ($resql) {
158
            $num = $this->db->num_rows($resql);
159
            $i = 0;
160
            while ($i < $num) {
161
                $obj = $this->db->fetch_object($resql);
162
                if ($sumStock) {
163
                    $obj->stock = price2num($obj->stock, 5);
164
                }
165
                $this->cache_warehouses[$obj->rowid]['id'] = $obj->rowid;
166
                $this->cache_warehouses[$obj->rowid]['label'] = $obj->label;
167
                $this->cache_warehouses[$obj->rowid]['parent_id'] = $obj->fk_parent;
168
                $this->cache_warehouses[$obj->rowid]['description'] = $obj->description;
169
                $this->cache_warehouses[$obj->rowid]['stock'] = $obj->stock;
170
                $i++;
171
            }
172
173
            // Full label init
174
            foreach ($this->cache_warehouses as $obj_rowid => $tab) {
175
                $this->cache_warehouses[$obj_rowid]['full_label'] = $this->get_parent_path($tab);
176
            }
177
178
            return $num;
179
        } else {
180
            dol_print_error($this->db);
181
            return -1;
182
        }
183
    }
184
185
    /**
186
     * Load in cache array list of workstations
187
     * If fk_product is not 0, we do not use cache
188
     *
189
     * @param   int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
190
     * @param   array       $exclude            warehouses ids to exclude
191
     * @param   string      $orderBy            [='e.ref'] Order by
192
     * @return  int                             Nb of loaded lines, 0 if already loaded, <0 if KO
193
     * @throws  Exception
194
     */
195
    public function loadWorkstations($fk_product = 0, $exclude = array(), $orderBy = 'w.ref')
196
    {
197
        global $conf, $langs;
198
199
        if (empty($fk_product) && count($this->cache_workstations)) {
200
            return 0; // Cache already loaded and we do not want a list with information specific to a product
201
        }
202
203
        $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";
204
        $sql .= " FROM " . $this->db->prefix() . "workstation_workstation as w";
205
        $sql .= " WHERE 1 = 1";
206
        if (!empty($fk_product) && $fk_product > 0) {
207
            $sql .= " AND w.fk_product = " . ((int) $fk_product);
208
        }
209
        $sql .= " AND w.entity IN (" . getEntity('workstation') . ")";
210
211
        if (is_array($exclude) && !empty($exclude)) {
212
            $sql .= ' AND w.rowid NOT IN(' . $this->db->sanitize(implode(',', $exclude)) . ')';
213
        }
214
215
        $sql .= " ORDER BY " . $orderBy;
216
217
        dol_syslog(get_only_class($this) . '::loadWorkstations', LOG_DEBUG);
218
        $resql = $this->db->query($sql);
219
        if ($resql) {
220
            $num = $this->db->num_rows($resql);
221
            $i = 0;
222
            while ($i < $num) {
223
                $obj = $this->db->fetch_object($resql);
224
225
                $this->cache_workstations[$obj->rowid]['id'] = $obj->rowid;
226
                $this->cache_workstations[$obj->rowid]['ref'] = $obj->ref;
227
                $this->cache_workstations[$obj->rowid]['label'] = $obj->label;
228
                $this->cache_workstations[$obj->rowid]['type'] = $obj->type;
229
                $this->cache_workstations[$obj->rowid]['nb_operators_required'] = $obj->nb_operators_required;
230
                $this->cache_workstations[$obj->rowid]['thm_operator_estimated'] = $obj->thm_operator_estimated;
231
                $this->cache_workstations[$obj->rowid]['thm_machine_estimated'] = $obj->thm_machine_estimated;
232
                $i++;
233
            }
234
235
            return $num;
236
        } else {
237
            dol_print_error($this->db);
238
            return -1;
239
        }
240
    }
241
242
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
243
    /**
244
     * Return full path to current warehouse in $tab (recursive function)
245
     *
246
     * @param   array   $tab            warehouse data in $this->cache_warehouses line
247
     * @param   string  $final_label    full label with all parents, separated by ' >> ' (completed on each call)
248
     * @return  string                  full label with all parents, separated by ' >> '
249
     */
250
    private function get_parent_path($tab, $final_label = '')
251
    {
252
		//phpcs:enable
253
        if (empty($final_label)) {
254
            $final_label = $tab['label'];
255
        }
256
257
        if (empty($tab['parent_id'])) {
258
            return $final_label;
259
        } else {
260
            if (!empty($this->cache_warehouses[$tab['parent_id']])) {
261
                $final_label = $this->cache_warehouses[$tab['parent_id']]['label'] . ' >> ' . $final_label;
262
                return $this->get_parent_path($this->cache_warehouses[$tab['parent_id']], $final_label);
263
            }
264
        }
265
266
        return $final_label;
267
    }
268
269
    /**
270
     *  Return list of warehouses
271
     *
272
     *  @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)
273
     *  @param  string      $htmlname           Name of html select html
274
     *  @param  string      $filterstatus       warehouse status filter, following comma separated filter options can be used
275
     *                                          'warehouseopen' = select products from open warehouses,
276
     *                                          'warehouseclosed' = select products from closed warehouses,
277
     *                                          'warehouseinternal' = select products from warehouses for internal correct/transfer only
278
     *  @param  int         $empty              1=Can be empty, 0 if not
279
     *  @param  int         $disabled           1=Select is disabled
280
     *  @param  int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
281
     *  @param  string      $empty_label        Empty label if needed (only if $empty=1)
282
     *  @param  int         $showstock          1=Show stock count
283
     *  @param  int         $forcecombo         1=Force combo iso ajax select2
284
     *  @param  array       $events             Events to add to select2
285
     *  @param  string      $morecss            Add more css classes to HTML select
286
     *  @param  array       $exclude            Warehouses ids to exclude
287
     *  @param  int         $showfullpath       1=Show full path of name (parent ref into label), 0=Show only ref of current warehouse
288
     *  @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
289
     *  @param  string      $orderBy            [='e.ref'] Order by
290
     *  @param  int         $multiselect        1=Allow multiselect
291
     *  @return string                          HTML select
292
     *
293
     *  @throws Exception
294
     */
295
    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)
296
    {
297
        global $conf, $langs, $user, $hookmanager;
298
299
        dol_syslog(get_only_class($this) . "::selectWarehouses " . (is_array($selected) ? 'selected is array' : $selected) . ", $htmlname, $filterstatus, $empty, $disabled, $fk_product, $empty_label, $showstock, $forcecombo, $morecss", LOG_DEBUG);
300
301
        $out = '';
302
        if (!getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
303
            $filterstatus = '';
304
        }
305
        if (!empty($fk_product) && $fk_product > 0) {
306
            $this->cache_warehouses = array();
307
        }
308
309
        $this->loadWarehouses($fk_product, '', $filterstatus, true, $exclude, $stockMin, $orderBy);
310
        $nbofwarehouses = count($this->cache_warehouses);
311
312
        if ($conf->use_javascript_ajax && !$forcecombo) {
313
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
314
            $comboenhancement = ajax_combobox($htmlname, $events);
315
            $out .= $comboenhancement;
316
        }
317
318
        if (strpos($htmlname, 'search_') !== 0) {
319
            if (empty($user->fk_warehouse) || $user->fk_warehouse == -1) {
320
                if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
321
                    $selected = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
322
                }
323
            } else {
324
                if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER')) {
325
                    $selected = $user->fk_warehouse;
326
                }
327
            }
328
        }
329
330
        $out .= '<select ' . ($multiselect ? 'multiple ' : '') . 'class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled' : '');
331
        $out .= ' id="' . $htmlname . '" name="' . ($htmlname . ($multiselect ? '[]' : '') . ($disabled ? '_disabled' : '')) . '"';
332
        //$out .= ' placeholder="todo"';    // placeholder for select2 must be added by setting the id+placeholder js param when calling select2
333
        $out .= '>';
334
        if ($empty) {
335
            $out .= '<option value="-1">' . ($empty_label ? $empty_label : '&nbsp;') . '</option>';
336
        }
337
        foreach ($this->cache_warehouses as $id => $arraytypes) {
338
            $label = '';
339
            if ($showfullpath) {
340
                $label .= $arraytypes['full_label'];
341
            } else {
342
                $label .= $arraytypes['label'];
343
            }
344
            if (($fk_product || ($showstock > 0)) && ($arraytypes['stock'] != 0 || ($showstock > 0))) {
345
                if ($arraytypes['stock'] <= 0) {
346
                    $label .= ' <span class="text-warning">(' . $langs->trans("Stock") . ':' . $arraytypes['stock'] . ')</span>';
347
                } else {
348
                    $label .= ' <span class="opacitymedium">(' . $langs->trans("Stock") . ':' . $arraytypes['stock'] . ')</span>';
349
                }
350
            }
351
352
            $out .= '<option value="' . $id . '"';
353
            if (is_array($selected)) {
354
                if (in_array($id, $selected)) {
355
                    $out .= ' selected';
356
                }
357
            } else {
358
                if ($selected == $id || (!empty($selected) && preg_match('/^ifone/', $selected) && $nbofwarehouses == 1)) {
359
                    $out .= ' selected';
360
                }
361
            }
362
            $out .= ' data-html="' . dol_escape_htmltag($label) . '"';
363
            $out .= '>';
364
            $out .= $label;
365
            $out .= '</option>';
366
        }
367
        $out .= '</select>';
368
        if ($disabled) {
369
            $out .= '<input type="hidden" name="' . $htmlname . '" value="' . (($selected > 0) ? $selected : '') . '">';
370
        }
371
372
        $parameters = array(
373
            'selected' => $selected,
374
            'htmlname' => $htmlname,
375
            'filterstatus' => $filterstatus,
376
            'empty' => $empty,
377
            'disabled ' => $disabled,
378
            'fk_product' => $fk_product,
379
            'empty_label' => $empty_label,
380
            'showstock' => $showstock,
381
            'forcecombo' => $forcecombo,
382
            'events' => $events,
383
            'morecss' => $morecss,
384
            'exclude' => $exclude,
385
            'showfullpath' => $showfullpath,
386
            'stockMin' => $stockMin,
387
            'orderBy' => $orderBy
388
        );
389
390
        $reshook = $hookmanager->executeHooks('selectWarehouses', $parameters, $this);
391
        if ($reshook > 0) {
392
            $out = $hookmanager->resPrint;
393
        } elseif ($reshook == 0) {
394
            $out .= $hookmanager->resPrint;
395
        }
396
397
        return $out;
398
    }
399
400
    /**
401
     *  Return list of workstations
402
     *
403
     *  @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)
404
     *  @param  string      $htmlname           Name of html select html
405
     *  @param  int         $empty              1=Can be empty, 0 if not
406
     *  @param  int         $disabled           1=Select is disabled
407
     *  @param  int         $fk_product         Add quantity of stock in label for product with id fk_product. Nothing if 0.
408
     *  @param  string      $empty_label        Empty label if needed (only if $empty=1)
409
     *  @param  int         $forcecombo         1=Force combo iso ajax select2
410
     *  @param  array       $events                     Events to add to select2
411
     *  @param  string      $morecss                    Add more css classes to HTML select
412
     *  @param  array       $exclude            Warehouses ids to exclude
413
     *  @param  int         $showfullpath       1=Show full path of name (parent ref into label), 0=Show only ref of current warehouse
414
     *  @param  string      $orderBy            [='e.ref'] Order by
415
     *  @return string                          HTML select
416
     *
417
     *  @throws Exception
418
     */
419
    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')
420
    {
421
        global $conf, $langs, $user, $hookmanager;
422
423
        dol_syslog(get_only_class($this) . "::selectWorkstations $selected, $htmlname, $empty, $disabled, $fk_product, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
424
425
        $filterstatus = '';
426
        $out = '';
427
        if (!empty($fk_product) && $fk_product > 0) {
428
            $this->cache_workstations = array();
429
        }
430
431
        $this->loadWorkstations($fk_product);
432
        $nbofworkstations = count($this->cache_workstations);
433
434
        if ($conf->use_javascript_ajax && !$forcecombo) {
435
            include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
436
            $comboenhancement = ajax_combobox($htmlname, $events);
437
            $out .= $comboenhancement;
438
        }
439
440
        if (strpos($htmlname, 'search_') !== 0) {
441
            if (empty($user->fk_workstation) || $user->fk_workstation == -1) {
442
                if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
443
                    $selected = getDolGlobalString('MAIN_DEFAULT_WORKSTATION');
444
                }
445
            } else {
446
                if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
447
                    $selected = $user->fk_workstation;
448
                }
449
            }
450
        }
451
452
        $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled' : '') . ' id="' . $htmlname . '" name="' . ($htmlname . ($disabled ? '_disabled' : '')) . '">';
453
        if ($empty) {
454
            $out .= '<option value="-1">' . ($empty_label ? $empty_label : '&nbsp;') . '</option>';
455
        }
456
        foreach ($this->cache_workstations as $id => $arraytypes) {
457
            $label = $arraytypes['label'];
458
459
            $out .= '<option value="' . $id . '"';
460
            if ($selected == $id || (preg_match('/^ifone/', $selected) && $nbofworkstations == 1)) {
461
                $out .= ' selected';
462
            }
463
            $out .= ' data-html="' . dol_escape_htmltag($label) . '"';
464
            $out .= '>';
465
            $out .= $label;
466
            $out .= '</option>';
467
        }
468
        $out .= '</select>';
469
        if ($disabled) {
470
            $out .= '<input type="hidden" name="' . $htmlname . '" value="' . (($selected > 0) ? $selected : '') . '">';
471
        }
472
473
        $parameters = array(
474
            'selected' => $selected,
475
            'htmlname' => $htmlname,
476
            'filterstatus' => $filterstatus,
477
            'empty' => $empty,
478
            'disabled ' => $disabled,
479
            'fk_product' => $fk_product,
480
            'empty_label' => $empty_label,
481
            'forcecombo' => $forcecombo,
482
            'events' => $events,
483
            'morecss' => $morecss,
484
            'exclude' => $exclude,
485
            'showfullpath' => $showfullpath,
486
            'orderBy' => $orderBy
487
        );
488
489
        $reshook = $hookmanager->executeHooks('selectWorkstations', $parameters, $this);
490
        if ($reshook > 0) {
491
            $out = $hookmanager->resPrint;
492
        } elseif ($reshook == 0) {
493
            $out .= $hookmanager->resPrint;
494
        }
495
496
        return $out;
497
    }
498
499
    /**
500
     *    Display form to select warehouse
501
     *
502
     *    @param    string      $page        Page
503
     *    @param    string|int  $selected    Id of warehouse
504
     *    @param    string      $htmlname    Name of select html field
505
     *    @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.
506
     *    @return   void
507
     */
508
    public function formSelectWarehouses($page, $selected = '', $htmlname = 'warehouse_id', $addempty = 0)
509
    {
510
        global $langs;
511
        if ($htmlname != "none") {
512
            print '<form method="POST" action="' . $page . '">';
513
            print '<input type="hidden" name="action" value="setwarehouse">';
514
            print '<input type="hidden" name="token" value="' . newToken() . '">';
515
            print '<table class="nobordernopadding">';
516
            print '<tr><td>';
517
            print $this->selectWarehouses($selected, $htmlname, '', $addempty);
518
            print '</td>';
519
            print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
520
            print '</tr></table></form>';
521
        } else {
522
            if ($selected) {
523
                $warehousestatic = new Entrepot($this->db);
524
                $warehousestatic->fetch($selected);
525
                print $warehousestatic->getNomUrl();
526
            } else {
527
                print "&nbsp;";
528
            }
529
        }
530
    }
531
532
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
533
    /**
534
     *  Output a combo box with list of units
535
     *  Currently the units are not define in the DB
536
     *
537
     *  @param  string      $name               Name of HTML field
538
     *  @param  string      $measuring_style    Unit to show: weight, size, surface, volume, time
539
     *  @param  string      $selected            Preselected value
540
     *  @param  int         $adddefault         Add empty unit called "Default"
541
     *  @param  int         $mode               1=Use short label as value, 0=Use rowid
542
     *  @return void
543
     *  @deprecated
544
     */
545
    public function select_measuring_units($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0)
546
    {
547
		//phpcs:enable
548
        print $this->selectMeasuringUnits($name, $measuring_style, $selected, $adddefault, $mode);
549
    }
550
551
    /**
552
     *  Return a combo box with list of units
553
     *  Units labels are defined in llx_c_units
554
     *
555
     *  @param  string      $name                Name of HTML field
556
     *  @param  string      $measuring_style     Unit to show: weight, size, surface, volume, time
557
     *  @param  string      $selected            Preselected value
558
     *  @param  int|string  $adddefault          1=Add empty unit called "Default", ''=Add empty value
559
     *  @param  int         $mode                1=Use short label as value, 0=Use rowid, 2=Use scale (power)
560
     *  @param  string      $morecss             More CSS
561
     *  @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...
562
     */
563
    public function selectMeasuringUnits($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0, $morecss = 'maxwidth125')
564
    {
565
        global $langs, $db;
566
567
        $langs->load("other");
568
569
        $return = '';
570
571
        // TODO Use a cache
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
        $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_only_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_only_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_only_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