Passed
Pull Request — dev (#8)
by Rafael
58:47
created

FormProduct::selectProductNature()   C

Complexity

Conditions 14
Paths 7

Size

Total Lines 57
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 36
nc 7
nop 4
dl 0
loc 57
rs 6.2666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

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