Passed
Push — CHECK_API ( 78ffdd...fa4f39 )
by Rafael
41:29
created

Entrepot   F

Complexity

Total Complexity 129

Size/Duplication

Total Lines 1016
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 468
dl 0
loc 1016
rs 2
c 0
b 0
f 0
wmc 129

19 Methods

Rating   Name   Duplication   Size   Complexity  
A get_full_arbo() 0 33 5
F getNomUrl() 0 77 25
A get_children_warehouses() 0 17 3
A initAsSpecimen() 0 21 1
A generateDocument() 0 20 4
A setCategories() 0 3 1
B nb_products() 0 40 7
B getTooltipContentArray() 0 29 7
A __construct() 0 12 2
F update() 0 77 14
A info() 0 22 4
B fetch() 0 66 7
A LibStatut() 0 15 2
A nb_different_products() 0 23 2
B getKanbanView() 0 27 10
A list_array() 0 22 3
F delete() 0 82 17
A getLibStatut() 0 3 1
C create() 0 70 14

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/* Copyright (C) 2003-2006  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2004-2010  Laurent Destailleur         <[email protected]>
5
 * Copyright (C) 2005-2008  Regis Houssin               <[email protected]>
6
 * Copyright (C) 2011	    Juanjo Menent               <[email protected]>
7
 * Copyright (C) 2016	    Francis Appels              <[email protected]>
8
 * Copyright (C) 2019-2024  Frédéric France             <[email protected]>
9
 * Copyright (C) 2024		MDW							<[email protected]>
10
 * Copyright (C) 2024       Rafael San José             <[email protected]>
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 3 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24
 */
25
26
namespace Dolibarr\Code\Product\Classes;
27
28
use Dolibarr\Code\Categories\Classes\Categorie;
29
use Dolibarr\Core\Base\CommonObject;
30
31
/**
32
 *  \file       htdocs/product/stock/class/entrepot.class.php
33
 *  \ingroup    stock
34
 *  \brief      File for class to manage warehouses
35
 */
36
37
38
/**
39
 *  Class to manage warehouses
40
 */
41
class Entrepot extends CommonObject
42
{
43
    /**
44
     * @var string ID to identify managed object
45
     */
46
    public $element = 'stock';
47
48
    /**
49
     * @var string Name of table without prefix where object is stored
50
     */
51
    public $table_element = 'entrepot';
52
53
    /**
54
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
55
     */
56
    public $picto = 'stock';
57
58
    /**
59
     * @var string  Label
60
     * @deprecated
61
     * @see $label
62
     */
63
    public $libelle;
64
65
    /**
66
     * @var string  Label
67
     */
68
    public $label;
69
70
    /**
71
     * @var string description
72
     */
73
    public $description;
74
75
    public $statut;
76
77
    /**
78
     * @var string Place
79
     */
80
    public $lieu;
81
82
    /**
83
     * @var string Address
84
     */
85
    public $address;
86
87
    /**
88
     * @var string Zipcode
89
     */
90
    public $zip;
91
92
    /**
93
     * @var string Town
94
     */
95
    public $town;
96
97
    /**
98
     * @var string Phone
99
     */
100
    public $phone;
101
102
    /**
103
     * @var string Fax
104
     */
105
    public $fax;
106
107
    /**
108
     * @var int ID of parent
109
     */
110
    public $fk_parent;
111
112
    /**
113
     * @var int ID of project
114
     */
115
    public $fk_project;
116
117
    /**
118
     * @var int Warehouse usage ID
119
     */
120
    public $warehouse_usage;
121
122
    /**
123
     *  'type' field format:
124
     *      'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',
125
     *      'select' (list of values are in 'options'),
126
     *      'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:SortField]]]]]]',
127
     *      'chkbxlst:...',
128
     *      'varchar(x)',
129
     *      'text', 'text:none', 'html',
130
     *      'double(24,8)', 'real', 'price', 'stock',
131
     *      'date', 'datetime', 'timestamp', 'duration',
132
     *      'boolean', 'checkbox', 'radio', 'array',
133
     *      'mail', 'phone', 'url', 'password', 'ip'
134
     *      Note: Filter must be a Dolibarr Universal Filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)"
135
     *  'label' the translation key.
136
     *  'picto' is code of a picto to show before value in forms
137
     *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt("MY_SETUP_PARAM")' or 'isModEnabled("multicurrency")' ...)
138
     *  'position' is the sort order of field.
139
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
140
     *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
141
     *  'noteditable' says if field is not editable (1 or 0)
142
     *  'alwayseditable' says if field can be modified also when status is not draft ('1' or '0')
143
     *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
144
     *  'index' if we want an index in database.
145
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
146
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
147
     *  'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage)
148
     *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
149
     *  'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
150
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
151
     *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
152
     *  'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar'
153
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
154
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
155
     *  'validate' is 1 if you need to validate the field with $this->validateField(). Need MAIN_ACTIVATE_VALIDATION_RESULT.
156
     *  'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value)
157
     *
158
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
159
     */
160
161
    // BEGIN MODULEBUILDER PROPERTIES
162
    /**
163
     * @var array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}>  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string,array{type:...ring>,comment?:string}> at position 16 could not be parsed: Expected '}' at position 16, but found 'int'.
Loading history...
164
     */
165
    public $fields = array(
166
        'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
167
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'default' => '1', 'notnull' => 1, 'index' => 1, 'position' => 15),
168
        'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
169
        'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -2, 'position' => 35, 'searchall' => 1),
170
        'lieu' => array('type' => 'varchar(64)', 'label' => 'LocationSummary', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'showoncombobox' => 2, 'searchall' => 1),
171
        'fk_parent' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php:1:((statut:=:1) AND (entity:IN:__SHARED_ENTITIES__))', 'label' => 'ParentWarehouse', 'enabled' => 1, 'visible' => -2, 'position' => 41),
172
        'fk_project' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => '$conf->project->enabled', 'visible' => -1, 'position' => 42),
173
        'address' => array('type' => 'varchar(255)', 'label' => 'Address', 'enabled' => 1, 'visible' => -2, 'position' => 45, 'searchall' => 1),
174
        'zip' => array('type' => 'varchar(10)', 'label' => 'Zip', 'enabled' => 1, 'visible' => -2, 'position' => 50, 'searchall' => 1),
175
        'town' => array('type' => 'varchar(50)', 'label' => 'Town', 'enabled' => 1, 'visible' => -2, 'position' => 55, 'searchall' => 1),
176
        'fk_departement' => array('type' => 'integer', 'label' => 'State', 'enabled' => 1, 'visible' => 0, 'position' => 60),
177
        'fk_pays' => array('type' => 'integer:Ccountry:core/class/ccountry.class.php', 'label' => 'Country', 'enabled' => 1, 'visible' => -1, 'position' => 65),
178
        'phone' => array('type' => 'varchar(20)', 'label' => 'Phone', 'enabled' => 1, 'visible' => -2, 'position' => 70, 'searchall' => 1),
179
        'fax' => array('type' => 'varchar(20)', 'label' => 'Fax', 'enabled' => 1, 'visible' => -2, 'position' => 75, 'searchall' => 1),
180
        //'fk_user_author' =>array('type'=>'integer', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-2, 'position'=>82),
181
        'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 300),
182
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 301),
183
        'warehouse_usage' => array('type' => 'integer', 'label' => 'WarehouseUsage', 'enabled' => 'getDolGlobalInt("MAIN_FEATURES_LEVEL")', 'visible' => 1, 'position' => 400, 'default' => 1, 'arrayofkeyval' => array(1 => 'InternalWarehouse', 2 => 'ExternalWarehouse')),
184
        //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000),
185
        //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
186
        'statut' => array('type' => 'tinyint(4)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 500, 'css' => 'minwidth50'),
187
    );
188
    // END MODULEBUILDER PROPERTIES
189
190
191
    /**
192
     * Warehouse closed, inactive
193
     */
194
    const STATUS_CLOSED = 0;
195
196
    /**
197
     * Warehouse open and any operations are allowed (customer shipping, supplier dispatch, internal stock transfers/corrections).
198
     */
199
    const STATUS_OPEN_ALL = 1;
200
201
    /**
202
     * Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping and supplier dispatch).
203
     */
204
    const STATUS_OPEN_INTERNAL = 2;
205
206
    /**
207
     * Warehouse open and any operations are allowed, but warehouse is not included into calculation of stock.
208
     */
209
    const STATUS_OPENEXT_ALL = 3;   // TODO Implement this
210
211
212
213
    /**
214
     *  Constructor
215
     *
216
     *  @param      DoliDB      $db      Database handler
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Product\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
217
     */
218
    public function __construct($db)
219
    {
220
        $this->db = $db;
221
222
        $this->ismultientitymanaged = 1;
223
224
        $this->labelStatus[self::STATUS_CLOSED] = 'Closed2';
225
        if (getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
226
            $this->labelStatus[self::STATUS_OPEN_ALL] = 'OpenAnyMovement';
227
            $this->labelStatus[self::STATUS_OPEN_INTERNAL] = 'OpenInternal';
228
        } else {
229
            $this->labelStatus[self::STATUS_OPEN_ALL] = 'Opened';
230
        }
231
    }
232
233
    /**
234
     *  Creation d'un entrepot en base
235
     *
236
     *  @param      User    $user       Object user that create the warehouse
237
     *  @param      int     $notrigger  0=launch triggers after, 1=disable triggers
238
     *  @return     int                 Return >0 if OK, =<0 if KO
239
     */
240
    public function create($user, $notrigger = 0)
241
    {
242
        global $conf;
243
244
        $error = 0;
245
246
        $this->label = trim($this->label);
247
248
        // Error if label not defined
249
        if ($this->label == '') {
250
            $this->error = "ErrorFieldRequired";
251
            return 0;
252
        }
253
254
        $now = dol_now();
255
256
        $this->db->begin();
257
258
        $sql = "INSERT INTO " . $this->db->prefix() . "entrepot (ref, entity, datec, fk_user_author, fk_parent, fk_project)";
259
        $sql .= " VALUES ('" . $this->db->escape($this->label) . "', " . ((int) $conf->entity) . ", '" . $this->db->idate($now) . "', " . ((int) $user->id) . ", " . ($this->fk_parent > 0 ? ((int) $this->fk_parent) : "NULL") . ", " . ($this->fk_project > 0 ? ((int) $this->fk_project) : "NULL") . ")";
260
261
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
262
        $result = $this->db->query($sql);
263
        if ($result) {
264
            $id = $this->db->last_insert_id($this->db->prefix() . "entrepot");
265
            if ($id > 0) {
266
                $this->id = $id;
267
268
                if (!$error) {
269
                    $result = $this->update($id, $user);
270
                    if ($result <= 0) {
271
                        $error++;
272
                    }
273
                }
274
275
                // Actions on extra fields
276
                if (!$error) {
277
                    $result = $this->insertExtraFields();
278
                    if ($result < 0) {
279
                        $error++;
280
                    }
281
                }
282
283
                if (!$error && !$notrigger) {
284
                    // Call triggers
285
                    $result = $this->call_trigger('WAREHOUSE_CREATE', $user);
286
                    if ($result < 0) {
287
                        $error++;
288
                    }
289
                    // End call triggers
290
                }
291
292
                if (!$error) {
293
                    $this->db->commit();
294
                    return $id;
295
                } else {
296
                    dol_syslog(get_class($this) . "::create return -3");
297
                    $this->db->rollback();
298
                    return -3;
299
                }
300
            } else {
301
                $this->error = "Failed to get insert id";
302
                dol_syslog(get_class($this) . "::create return -2");
303
                return -2;
304
            }
305
        } else {
306
            $this->error = $this->db->error();
307
            dol_syslog(get_class($this) . "::create Error " . $this->db->error());
308
            $this->db->rollback();
309
            return -1;
310
        }
311
    }
312
313
    /**
314
     *  Update properties of a warehouse
315
     *
316
     *  @param      int     $id         id of warehouse to modify
317
     *  @param      User    $user       User object
318
     *  @param      int     $notrigger  0=launch triggers after, 1=disable trigge
319
     *  @return     int                 Return >0 if OK, <0 if KO
320
     */
321
    public function update($id, $user, $notrigger = 0)
322
    {
323
        $error = 0;
324
325
        if (empty($id)) {
326
            $id = $this->id;
327
        }
328
329
        // Check if new parent is already a child of current warehouse
330
        if (!empty($this->fk_parent)) {
331
            $TChildWarehouses = array($id);
332
            $TChildWarehouses = $this->get_children_warehouses($this->id, $TChildWarehouses);
333
            if (in_array($this->fk_parent, $TChildWarehouses)) {
334
                $this->error = 'ErrorCannotAddThisParentWarehouse';
335
                return -2;
336
            }
337
        }
338
339
        $this->label = trim($this->label);
340
341
        $this->description = trim($this->description);
342
343
        $this->lieu = trim($this->lieu);
344
345
        $this->address = trim($this->address);
346
        $this->zip = trim($this->zip);
347
        $this->town = trim($this->town);
348
        $this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
349
350
        $sql = "UPDATE " . $this->db->prefix() . "entrepot";
351
        $sql .= " SET ref = '" . $this->db->escape($this->label) . "'";
352
        $sql .= ", fk_parent = " . (($this->fk_parent > 0) ? $this->fk_parent : "NULL");
353
        $sql .= ", fk_project = " . (($this->fk_project > 0) ? $this->fk_project : "NULL");
354
        $sql .= ", description = '" . $this->db->escape($this->description) . "'";
355
        $sql .= ", statut = " . ((int) $this->statut);
356
        $sql .= ", lieu = '" . $this->db->escape($this->lieu) . "'";
357
        $sql .= ", address = '" . $this->db->escape($this->address) . "'";
358
        $sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
359
        $sql .= ", town = '" . $this->db->escape($this->town) . "'";
360
        $sql .= ", fk_pays = " . ((int) $this->country_id);
361
        $sql .= ", phone = '" . $this->db->escape($this->phone) . "'";
362
        $sql .= ", fax = '" . $this->db->escape($this->fax) . "'";
363
        $sql .= " WHERE rowid = " . ((int) $id);
364
365
        $this->db->begin();
366
367
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
368
        $resql = $this->db->query($sql);
369
370
        if (!$resql) {
371
            $error++;
372
            $this->errors[] = "Error " . $this->db->lasterror();
373
        }
374
375
        if (!$error) {
376
            $result = $this->insertExtraFields();
377
            if ($result < 0) {
378
                $error++;
379
            }
380
        }
381
382
        if (!$error && !$notrigger) {
383
            // Call triggers
384
            $result = $this->call_trigger('WAREHOUSE_MODIFY', $user);
385
            if ($result < 0) {
386
                $error++;
387
            }
388
            // End call triggers
389
        }
390
391
        if (!$error) {
392
            $this->db->commit();
393
            return 1;
394
        } else {
395
            $this->db->rollback();
396
            $this->error = $this->db->lasterror();
397
            return -1;
398
        }
399
    }
400
401
402
    /**
403
     *  Delete a warehouse
404
     *
405
     *  @param      User    $user          Object user that made deletion
406
     *  @param      int     $notrigger     1=No trigger
407
     *  @return     int                    Return integer <0 if KO, >0 if OK
408
     */
409
    public function delete($user, $notrigger = 0)
410
    {
411
        global $conf;
412
413
        $error = 0;
414
415
        dol_syslog(get_class($this) . "::delete id=" . $this->id, LOG_DEBUG);
416
417
        $this->db->begin();
418
419
        if (!$error && empty($notrigger)) {
420
            // Call trigger
421
            $result = $this->call_trigger('WAREHOUSE_DELETE', $user);
422
            if ($result < 0) {
423
                $error++;
424
            }
425
            // End call triggers
426
        }
427
428
        if (!$error) {
429
            $sql = "DELETE FROM " . $this->db->prefix() . "product_batch";
430
            $sql .= " WHERE fk_product_stock IN (SELECT rowid FROM " . $this->db->prefix() . "product_stock as ps WHERE ps.fk_entrepot = " . ((int) $this->id) . ")";
431
            $result = $this->db->query($sql);
432
            if (!$result) {
433
                $error++;
434
                $this->errors[] = $this->db->lasterror();
435
            }
436
        }
437
438
        if (!$error) {
439
            $elements = array('stock_mouvement', 'product_stock');
440
            foreach ($elements as $table) {
441
                if (!$error) {
442
                    $sql = "DELETE FROM " . $this->db->prefix() . $table;
443
                    $sql .= " WHERE fk_entrepot = " . ((int) $this->id);
444
445
                    $result = $this->db->query($sql);
446
                    if (!$result) {
447
                        $error++;
448
                        $this->errors[] = $this->db->lasterror();
449
                    }
450
                }
451
            }
452
        }
453
454
        // Removed extrafields
455
        if (!$error) {
456
            $result = $this->deleteExtraFields();
457
            if ($result < 0) {
458
                $error++;
459
                dol_syslog(get_class($this) . "::delete Error " . $this->error, LOG_ERR);
460
            }
461
        }
462
463
        if (!$error) {
464
            $sql = "DELETE FROM " . $this->db->prefix() . "entrepot";
465
            $sql .= " WHERE rowid = " . ((int) $this->id);
466
            $resql1 = $this->db->query($sql);
467
            if (!$resql1) {
468
                $error++;
469
                $this->errors[] = $this->db->lasterror();
470
                dol_syslog(get_class($this) . "::delete Error " . $this->db->lasterror(), LOG_ERR);
471
            }
472
        }
473
474
        if (!$error) {
475
            // Update denormalized fields because we change content of produt_stock. Warning: Do not use "SET p.stock", does not works with pgsql
476
            $sql = "UPDATE " . $this->db->prefix() . "product as p SET stock = (SELECT SUM(ps.reel) FROM " . $this->db->prefix() . "product_stock as ps WHERE ps.fk_product = p.rowid)";
477
            $resql2 = $this->db->query($sql);
478
            if (!$resql2) {
479
                $error++;
480
                $this->errors[] = $this->db->lasterror();
481
                dol_syslog(get_class($this) . "::delete Error " . $this->db->lasterror(), LOG_ERR);
482
            }
483
        }
484
485
        if (!$error) {
486
            $this->db->commit();
487
            return 1;
488
        } else {
489
            $this->db->rollback();
490
            return -1;
491
        }
492
    }
493
494
495
    /**
496
     *  Load warehouse data
497
     *
498
     *  @param      int     $id     Warehouse id
499
     *  @param      string  $ref    Warehouse label
500
     *  @return     int             >0 if OK, <0 if KO
501
     */
502
    public function fetch($id, $ref = '')
503
    {
504
        global $conf;
505
506
        dol_syslog(get_class($this) . "::fetch id=" . $id . " ref=" . $ref);
507
508
        // Check parameters
509
        if (!$id && !$ref) {
510
            $this->error = 'ErrorWrongParameters';
511
            dol_syslog(get_class($this) . "::fetch " . $this->error);
512
            return -1;
513
        }
514
515
        $sql  = "SELECT rowid, entity, fk_parent, fk_project, ref as label, description, statut, lieu, address, zip, town, fk_pays as country_id, phone, fax,";
516
        $sql .= " model_pdf, import_key";
517
        $sql .= " FROM " . $this->db->prefix() . "entrepot";
518
        if ($id) {
519
            $sql .= " WHERE rowid = " . ((int) $id);
520
        } else {
521
            $sql .= " WHERE entity IN (" . getEntity('stock') . ")";
522
            if ($ref) {
523
                $sql .= " AND ref = '" . $this->db->escape($ref) . "'";
524
            }
525
        }
526
527
        $result = $this->db->query($sql);
528
        if ($result) {
529
            if ($this->db->num_rows($result) > 0) {
530
                $obj = $this->db->fetch_object($result);
531
532
                $this->id             = $obj->rowid;
533
                $this->entity         = $obj->entity;
534
                $this->fk_parent      = $obj->fk_parent;
535
                $this->fk_project     = $obj->fk_project;
536
                $this->ref            = $obj->label;
537
                $this->label          = $obj->label;
538
                $this->description    = $obj->description;
539
                $this->statut         = $obj->statut;
540
                $this->lieu           = $obj->lieu;
541
                $this->address        = $obj->address;
542
                $this->zip            = $obj->zip;
543
                $this->town           = $obj->town;
544
                $this->country_id     = $obj->country_id;
545
                $this->phone          = $obj->phone;
546
                $this->fax            = $obj->fax;
547
548
                $this->model_pdf      = $obj->model_pdf;
549
                $this->import_key     = $obj->import_key;
550
551
                // Retrieve all extrafield
552
                // fetch optionals attributes and labels
553
                $this->fetch_optionals();
554
555
                include_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
556
                $tmp = getCountry($this->country_id, 'all');
557
                $this->country = $tmp['label'];
558
                $this->country_code = $tmp['code'];
559
560
                return 1;
561
            } else {
562
                $this->error = "Record Not Found";
563
                return 0;
564
            }
565
        } else {
566
            $this->error = $this->db->error();
567
            return -1;
568
        }
569
    }
570
571
572
    /**
573
     *  Load warehouse info data
574
     *
575
     *  @param  int     $id      warehouse id
576
     *  @return void
577
     */
578
    public function info($id)
579
    {
580
        $sql = "SELECT e.rowid, e.datec, e.tms as datem, e.fk_user_author";
581
        $sql .= " FROM " . $this->db->prefix() . "entrepot as e";
582
        $sql .= " WHERE e.rowid = " . ((int) $id);
583
584
        dol_syslog(get_class($this) . "::info", LOG_DEBUG);
585
        $result = $this->db->query($sql);
586
        if ($result) {
587
            if ($this->db->num_rows($result)) {
588
                $obj = $this->db->fetch_object($result);
589
590
                $this->id = $obj->rowid;
591
592
                $this->user_creation_id = $obj->fk_user_author;
593
                $this->date_creation     = $this->db->jdate($obj->datec);
594
                $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
595
            }
596
597
            $this->db->free($result);
598
        } else {
599
            dol_print_error($this->db);
600
        }
601
    }
602
603
604
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
605
    /**
606
     *  Return list of all warehouses
607
     *
608
     *  @param  int     $status     Status
609
     *  @return array               Array list of warehouses
610
     */
611
    public function list_array($status = 1)
612
    {
613
		// phpcs:enable
614
        $liste = array();
615
616
        $sql = "SELECT rowid, ref as label";
617
        $sql .= " FROM " . $this->db->prefix() . "entrepot";
618
        $sql .= " WHERE entity IN (" . getEntity('stock') . ")";
619
        $sql .= " AND statut = " . ((int) $status);
620
621
        $result = $this->db->query($sql);
622
        $i = 0;
623
        $num = $this->db->num_rows($result);
624
        if ($result) {
625
            while ($i < $num) {
626
                $row = $this->db->fetch_row($result);
627
                $liste[$row[0]] = $row[1];
628
                $i++;
629
            }
630
            $this->db->free($result);
631
        }
632
        return $liste;
633
    }
634
635
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
636
    /**
637
     *  Return number of unique different product into a warehouse
638
     *
639
     *  @return     array|int       Array('nb'=>Nb, 'value'=>Value)
640
     */
641
    public function nb_different_products()
642
    {
643
		// phpcs:enable
644
        $ret = array();
645
646
        $sql = "SELECT count(distinct p.rowid) as nb";
647
        $sql .= " FROM " . $this->db->prefix() . "product_stock as ps";
648
        $sql .= ", " . $this->db->prefix() . "product as p";
649
        $sql .= " WHERE ps.fk_entrepot = " . ((int) $this->id);
650
        $sql .= " AND ps.fk_product = p.rowid";
651
652
        //print $sql;
653
        $result = $this->db->query($sql);
654
        if ($result) {
655
            $obj = $this->db->fetch_object($result);
656
            $ret['nb'] = $obj->nb;
657
            $this->db->free($result);
658
        } else {
659
            $this->error = $this->db->lasterror();
660
            return -1;
661
        }
662
663
        return $ret;
664
    }
665
666
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
667
    /**
668
     *  Return stock and value of warehosue
669
     *
670
     *  @return     array|int       Array('nb'=>Nb, 'value'=>Value)
671
     */
672
    public function nb_products()
673
    {
674
        global $conf;
675
		// phpcs:enable
676
        $ret = array();
677
678
        //For MultiCompany PMP per entity
679
        $separatedPMP = false;
680
        if (getDolGlobalString('MULTICOMPANY_PRODUCT_SHARING_ENABLED') && getDolGlobalString('MULTICOMPANY_PMP_PER_ENTITY_ENABLED')) {
681
            $separatedPMP = true;
682
        }
683
684
        if ($separatedPMP) {
685
            $sql = "SELECT sum(ps.reel) as nb, sum(ps.reel * pa.pmp) as value";
686
        } else {
687
            $sql = "SELECT sum(ps.reel) as nb, sum(ps.reel * p.pmp) as value";
688
        }
689
        $sql .= " FROM " . $this->db->prefix() . "product_stock as ps";
690
        $sql .= ", " . $this->db->prefix() . "product as p";
691
        if ($separatedPMP) {
692
            $sql .= ", " . $this->db->prefix() . "product_perentity as pa";
693
        }
694
        $sql .= " WHERE ps.fk_entrepot = " . ((int) $this->id);
695
        if ($separatedPMP) {
696
            $sql .= " AND pa.fk_product = p.rowid AND pa.entity = " . (int) $conf->entity;
697
        }
698
        $sql .= " AND ps.fk_product = p.rowid";
699
        //print $sql;
700
        $result = $this->db->query($sql);
701
        if ($result) {
702
            $obj = $this->db->fetch_object($result);
703
            $ret['nb'] = $obj->nb;
704
            $ret['value'] = $obj->value;
705
            $this->db->free($result);
706
        } else {
707
            $this->error = $this->db->lasterror();
708
            return -1;
709
        }
710
711
        return $ret;
712
    }
713
714
    /**
715
     *  Return label of status of object
716
     *
717
     *  @param      int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
718
     *  @return     string              Label of status
719
     */
720
    public function getLibStatut($mode = 0)
721
    {
722
        return $this->LibStatut($this->statut, $mode);
723
    }
724
725
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
726
    /**
727
     *  Return label of a given status
728
     *
729
     *  @param  int     $status     Id status
730
     *  @param  int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
731
     *  @return string              Label of status
732
     */
733
    public function LibStatut($status, $mode = 0)
734
    {
735
		// phpcs:enable
736
        global $langs;
737
738
        $statusType = 'status5';
739
        if ($status > 0) {
740
            $statusType = 'status4';
741
        }
742
743
        $langs->load('stocks');
744
        $label = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
745
        $labelshort = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
746
747
        return dolGetStatus($label, $labelshort, '', $statusType, $mode);
748
    }
749
750
    /**
751
     * getTooltipContentArray
752
     *
753
     * @param   array   $params     Params to construct tooltip data
754
     * @since   v18
755
     * @return  array
756
     */
757
    public function getTooltipContentArray($params)
758
    {
759
        global $conf, $langs, $user;
760
761
        $langs->load('stocks');
762
763
        $datas = [];
764
765
        $option = $params['option'] ?? '';
766
        $nofetch = !empty($params['nofetch']);
767
768
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
769
            return ['optimize' => $langs->trans("Warehouse")];
770
        }
771
        $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("Warehouse") . '</u>';
772
        if (isset($this->statut)) {
773
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
774
        }
775
        $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . (empty($this->ref) ? $this->label : $this->ref);
776
        if (!empty($this->lieu)) {
777
            $datas['locationsummary'] = '<br><b>' . $langs->trans('LocationSummary') . ':</b> ' . $this->lieu;
778
        }
779
        // show categories for this record only in ajax to not overload lists
780
        if (!$nofetch && isModEnabled('category')) {
781
            $form = new Form($this->db);
782
            $datas['categories_warehouse'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_WAREHOUSE, 1, 1);
783
        }
784
785
        return $datas;
786
    }
787
788
    /**
789
     *  Return clickable name (possibility with the pictogram)
790
     *
791
     *  @param      int     $withpicto              with pictogram
792
     *  @param      string  $option                 Where the link point to
793
     *  @param      int     $showfullpath           0=Show ref only. 1=Show full path instead of Ref (this->fk_parent must be defined)
794
     *  @param      int     $notooltip              1=Disable tooltip
795
     *  @param      string  $morecss                Add more css on link
796
     *  @param      int     $save_lastsearch_value  -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
797
     *  @return     string                          String with URL
798
     */
799
    public function getNomUrl($withpicto = 0, $option = '', $showfullpath = 0, $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
800
    {
801
        global $conf, $langs, $hookmanager;
802
        $langs->load("stocks");
803
804
        if (!empty($conf->dol_no_mouse_hover)) {
805
            $notooltip = 1; // Force disable tooltips
806
        }
807
808
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
809
            $withpicto = 0;
810
        }
811
812
        $result = '';
813
        $params = [
814
            'id' => $this->id,
815
            'objecttype' => $this->element,
816
            'option' => $option,
817
            'nofetch' => 1,
818
        ];
819
        $classfortooltip = 'classfortooltip';
820
        $dataparams = '';
821
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
822
            $classfortooltip = 'classforajaxtooltip';
823
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
824
            $label = 'ToComplete';
825
        } else {
826
            $label = implode($this->getTooltipContentArray($params));
827
        }
828
829
        $url = constant('BASE_URL') . '/product/stock/card.php?id=' . $this->id;
830
831
        if ($option != 'nolink') {
832
            // Add param to save lastsearch_values or not
833
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
834
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
835
                $add_save_lastsearch_values = 1;
836
            }
837
            if ($add_save_lastsearch_values) {
838
                $url .= '&save_lastsearch_values=1';
839
            }
840
        }
841
842
        $linkclose = '';
843
        if (empty($notooltip)) {
844
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
845
                $label = $langs->trans("Warehouse");
846
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
847
            }
848
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
849
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
850
        }
851
852
        $linkstart = '<a href="' . $url . '"';
853
        $linkstart .= $linkclose . '>';
854
        $linkend = '</a>';
855
856
        $result .= $linkstart;
857
        if ($withpicto) {
858
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . '"'), 0, 0, $notooltip ? 0 : 1);
859
        }
860
        if ($withpicto != 2) {
861
            $result .= (($showfullpath || getDolGlobalString('STOCK_ALWAYS_SHOW_FULL_ARBO')) ? $this->get_full_arbo() : $this->label);
862
        }
863
        $result .= $linkend;
864
865
        global $action;
866
        $hookmanager->initHooks(array('warehousedao'));
867
        $parameters = array('id' => $this->id, 'getnomurl' => &$result, 'withpicto' => $withpicto, 'option' => $option, 'showfullpath' => $showfullpath, 'notooltip' => $notooltip);
868
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
869
        if ($reshook > 0) {
870
            $result = $hookmanager->resPrint;
871
        } else {
872
            $result .= $hookmanager->resPrint;
873
        }
874
875
        return $result;
876
    }
877
878
    /**
879
     *  Initialise an instance with random values.
880
     *  Used to build previews or test instances.
881
     *  id must be 0 if object instance is a specimen.
882
     *
883
     *  @return int
884
     */
885
    public function initAsSpecimen()
886
    {
887
        global $user, $langs, $conf, $mysoc;
888
889
        $now = dol_now();
890
891
        // Initialize parameters
892
        $this->id = 0;
893
        $this->label = 'WAREHOUSE SPECIMEN';
894
        $this->description = 'WAREHOUSE SPECIMEN ' . dol_print_date($now, 'dayhourlog');
895
        $this->statut = 1;
896
        $this->specimen = 1;
897
898
        $this->lieu = 'Location test';
899
        $this->address = '21 jump street';
900
        $this->zip = '99999';
901
        $this->town = 'MyTown';
902
        $this->country_id = 1;
903
        $this->country_code = 'FR';
904
905
        return 1;
906
    }
907
908
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
909
    /**
910
     *  Return full path to current warehouse
911
     *
912
     *  @return     string  String full path to current warehouse separated by " >> "
913
     */
914
    public function get_full_arbo()
915
    {
916
		// phpcs:enable
917
        $TArbo = array($this->label);
918
919
        $protection = 100; // We limit depth of warehouses to 100
920
921
        $warehousetmp = new Entrepot($this->db);
922
923
        $parentid = $this->fk_parent; // If parent_id not defined on current object, we do not start consecutive searches of parents
924
        $i = 0;
925
        while ($parentid > 0 && $i < $protection) {
926
            $sql = "SELECT fk_parent FROM " . $this->db->prefix() . "entrepot";
927
            $sql .= " WHERE rowid = " . ((int) $parentid);
928
929
            $resql = $this->db->query($sql);
930
            if ($resql) {
931
                $objarbo = $this->db->fetch_object($resql);
932
                if ($objarbo) {
933
                    $warehousetmp->fetch($parentid);
934
                    $TArbo[] = $warehousetmp->label;
935
                    $parentid = $objarbo->fk_parent;
936
                } else {
937
                    break;
938
                }
939
            } else {
940
                dol_print_error($this->db);
941
            }
942
943
            $i++;
944
        }
945
946
        return implode(' >> ', array_reverse($TArbo));
947
    }
948
949
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
950
    /**
951
     * Return array of children warehouses ids from $id warehouse (recursive function)
952
     *
953
     * @param   int         $id                 id parent warehouse
954
     * @param   integer[]   $TChildWarehouses   array which will contain all children (param by reference)
955
     * @return  integer[]   $TChildWarehouses   array which will contain all children
956
     */
957
    public function get_children_warehouses($id, &$TChildWarehouses)
958
    {
959
		// phpcs:enable
960
961
        $sql = "SELECT rowid
962
				FROM " . $this->db->prefix() . "entrepot
963
				WHERE fk_parent = " . ((int) $id);
964
965
        $resql = $this->db->query($sql);
966
        if ($resql) {
967
            while ($res = $this->db->fetch_object($resql)) {
968
                $TChildWarehouses[] = $res->rowid;
969
                $this->get_children_warehouses($res->rowid, $TChildWarehouses);
970
            }
971
        }
972
973
        return $TChildWarehouses;
974
    }
975
976
    /**
977
     *  Create object on disk
978
     *
979
     *  @param     string       $modele         force le modele a utiliser ('' to not force)
980
     *  @param     Translate    $outputlangs    Object langs to use for output
981
     *  @param     int          $hidedetails    Hide details of lines
982
     *  @param     int          $hidedesc       Hide description
983
     *  @param     int          $hideref        Hide ref
984
     *  @return    int                          0 if KO, 1 if OK
985
     */
986
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
987
    {
988
        global $conf, $user, $langs;
989
990
        $langs->load("stocks");
991
        $outputlangs->load("products");
992
993
        if (!dol_strlen($modele)) {
994
            $modele = 'standard';
995
996
            if ($this->model_pdf) {
997
                $modele = $this->model_pdf;
998
            } elseif (getDolGlobalString('STOCK_ADDON_PDF')) {
999
                $modele = getDolGlobalString('STOCK_ADDON_PDF');
1000
            }
1001
        }
1002
1003
        $modelpath = "core/modules/stock/doc/";
1004
1005
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1006
    }
1007
1008
    /**
1009
     * Sets object to supplied categories.
1010
     *
1011
     * Deletes object from existing categories not supplied.
1012
     * Adds it to non existing supplied categories.
1013
     * Existing categories are left untouch.
1014
     *
1015
     * @param   int[]|int   $categories     Category or categories IDs
1016
     * @return  int                         Return integer <0 if KO, >0 if OK
1017
     */
1018
    public function setCategories($categories)
1019
    {
1020
        return parent::setCategoriesCommon($categories, Categorie::TYPE_WAREHOUSE);
1021
    }
1022
1023
    /**
1024
     *  Return clicable link of object (with eventually picto)
1025
     *
1026
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
1027
     *  @param      array       $arraydata              Array of data
1028
     *  @return     string                              HTML Code for Kanban thumb.
1029
     */
1030
    public function getKanbanView($option = '', $arraydata = null)
1031
    {
1032
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1033
1034
        $return = '<div class="box-flex-item box-flex-grow-zero">';
1035
        $return .= '<div class="info-box info-box-sm">';
1036
        $return .= '<div class="info-box-icon bg-infobox-action" >';
1037
        $return .= img_picto('', $this->picto);
1038
        $return .= '</div>';
1039
        $return .= '<div class="info-box-content" >';
1040
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
1041
        if ($selected >= 0) {
1042
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
1043
        }
1044
        if (property_exists($this, 'lieu') && (!empty($this->lieu))) {
1045
            $return .= '<br><span class="info-box-label opacitymedium">' . $this->lieu . '</span>';
1046
        }
1047
        if (property_exists($this, 'sellvalue') && $this->sellvalue != 0) {
0 ignored issues
show
Bug Best Practice introduced by
The property sellvalue does not exist on Dolibarr\Code\Product\Classes\Entrepot. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing $this->sellvalue of type mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
1048
            $return .= '<br><span class="info-box-label amount">' . price($this->sellvalue) . '</span>';
1049
        }
1050
        if (method_exists($this, 'getLibStatut')) {
1051
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
1052
        }
1053
        $return .= '</div>';
1054
        $return .= '</div>';
1055
        $return .= '</div>';
1056
        return $return;
1057
    }
1058
}
1059