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

Entrepot   F

Complexity

Total Complexity 129

Size/Duplication

Total Lines 1018
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 470
dl 0
loc 1018
rs 2
c 0
b 0
f 0
wmc 129

19 Methods

Rating   Name   Duplication   Size   Complexity  
A get_full_arbo() 0 33 5
A setCategories() 0 4 1
B nb_products() 0 40 7
B getTooltipContentArray() 0 30 7
F getNomUrl() 0 77 25
F update() 0 77 14
A __construct() 0 12 2
A get_children_warehouses() 0 17 3
A info() 0 22 4
B fetch() 0 66 7
A initAsSpecimen() 0 21 1
A LibStatut() 0 15 2
A nb_different_products() 0 23 2
A generateDocument() 0 20 4
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\Core\Base\CommonObject;
29
30
/**
31
 *  \file       htdocs/product/stock/class/entrepot.class.php
32
 *  \ingroup    stock
33
 *  \brief      File for class to manage warehouses
34
 */
35
36
37
/**
38
 *  Class to manage warehouses
39
 */
40
class Entrepot extends CommonObject
41
{
42
    /**
43
     * @var string ID to identify managed object
44
     */
45
    public $element = 'stock';
46
47
    /**
48
     * @var string Name of table without prefix where object is stored
49
     */
50
    public $table_element = 'entrepot';
51
52
    /**
53
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
54
     */
55
    public $picto = 'stock';
56
57
    /**
58
     * @var string  Label
59
     * @deprecated
60
     * @see $label
61
     */
62
    public $libelle;
63
64
    /**
65
     * @var string  Label
66
     */
67
    public $label;
68
69
    /**
70
     * @var string description
71
     */
72
    public $description;
73
74
    public $statut;
75
76
    /**
77
     * @var string Place
78
     */
79
    public $lieu;
80
81
    /**
82
     * @var string Address
83
     */
84
    public $address;
85
86
    /**
87
     * @var string Zipcode
88
     */
89
    public $zip;
90
91
    /**
92
     * @var string Town
93
     */
94
    public $town;
95
96
    /**
97
     * @var string Phone
98
     */
99
    public $phone;
100
101
    /**
102
     * @var string Fax
103
     */
104
    public $fax;
105
106
    /**
107
     * @var int ID of parent
108
     */
109
    public $fk_parent;
110
111
    /**
112
     * @var int ID of project
113
     */
114
    public $fk_project;
115
116
    /**
117
     * @var int Warehouse usage ID
118
     */
119
    public $warehouse_usage;
120
121
    /**
122
     *  'type' field format:
123
     *      'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',
124
     *      'select' (list of values are in 'options'),
125
     *      'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:SortField]]]]]]',
126
     *      'chkbxlst:...',
127
     *      'varchar(x)',
128
     *      'text', 'text:none', 'html',
129
     *      'double(24,8)', 'real', 'price', 'stock',
130
     *      'date', 'datetime', 'timestamp', 'duration',
131
     *      'boolean', 'checkbox', 'radio', 'array',
132
     *      'mail', 'phone', 'url', 'password', 'ip'
133
     *      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)"
134
     *  'label' the translation key.
135
     *  'picto' is code of a picto to show before value in forms
136
     *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt("MY_SETUP_PARAM")' or 'isModEnabled("multicurrency")' ...)
137
     *  'position' is the sort order of field.
138
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
139
     *  '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)
140
     *  'noteditable' says if field is not editable (1 or 0)
141
     *  'alwayseditable' says if field can be modified also when status is not draft ('1' or '0')
142
     *  '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.
143
     *  'index' if we want an index in database.
144
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
145
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
146
     *  '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)
147
     *  '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'
148
     *  '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.
149
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
150
     *  '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.
151
     *  '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'
152
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
153
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
154
     *  'validate' is 1 if you need to validate the field with $this->validateField(). Need MAIN_ACTIVATE_VALIDATION_RESULT.
155
     *  'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value)
156
     *
157
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
158
     */
159
160
    // BEGIN MODULEBUILDER PROPERTIES
161
    /**
162
     * @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...
163
     */
164
    public $fields = array(
165
        'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
166
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'default' => '1', 'notnull' => 1, 'index' => 1, 'position' => 15),
167
        'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
168
        'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -2, 'position' => 35, 'searchall' => 1),
169
        'lieu' => array('type' => 'varchar(64)', 'label' => 'LocationSummary', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'showoncombobox' => 2, 'searchall' => 1),
170
        '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),
171
        '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),
172
        'address' => array('type' => 'varchar(255)', 'label' => 'Address', 'enabled' => 1, 'visible' => -2, 'position' => 45, 'searchall' => 1),
173
        'zip' => array('type' => 'varchar(10)', 'label' => 'Zip', 'enabled' => 1, 'visible' => -2, 'position' => 50, 'searchall' => 1),
174
        'town' => array('type' => 'varchar(50)', 'label' => 'Town', 'enabled' => 1, 'visible' => -2, 'position' => 55, 'searchall' => 1),
175
        'fk_departement' => array('type' => 'integer', 'label' => 'State', 'enabled' => 1, 'visible' => 0, 'position' => 60),
176
        'fk_pays' => array('type' => 'integer:Ccountry:core/class/ccountry.class.php', 'label' => 'Country', 'enabled' => 1, 'visible' => -1, 'position' => 65),
177
        'phone' => array('type' => 'varchar(20)', 'label' => 'Phone', 'enabled' => 1, 'visible' => -2, 'position' => 70, 'searchall' => 1),
178
        'fax' => array('type' => 'varchar(20)', 'label' => 'Fax', 'enabled' => 1, 'visible' => -2, 'position' => 75, 'searchall' => 1),
179
        //'fk_user_author' =>array('type'=>'integer', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-2, 'position'=>82),
180
        'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 300),
181
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 301),
182
        'warehouse_usage' => array('type' => 'integer', 'label' => 'WarehouseUsage', 'enabled' => 'getDolGlobalInt("MAIN_FEATURES_LEVEL")', 'visible' => 1, 'position' => 400, 'default' => 1, 'arrayofkeyval' => array(1 => 'InternalWarehouse', 2 => 'ExternalWarehouse')),
183
        //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000),
184
        //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
185
        'statut' => array('type' => 'tinyint(4)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 500, 'css' => 'minwidth50'),
186
    );
187
    // END MODULEBUILDER PROPERTIES
188
189
190
    /**
191
     * Warehouse closed, inactive
192
     */
193
    const STATUS_CLOSED = 0;
194
195
    /**
196
     * Warehouse open and any operations are allowed (customer shipping, supplier dispatch, internal stock transfers/corrections).
197
     */
198
    const STATUS_OPEN_ALL = 1;
199
200
    /**
201
     * Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping and supplier dispatch).
202
     */
203
    const STATUS_OPEN_INTERNAL = 2;
204
205
    /**
206
     * Warehouse open and any operations are allowed, but warehouse is not included into calculation of stock.
207
     */
208
    const STATUS_OPENEXT_ALL = 3;   // TODO Implement this
209
210
211
212
    /**
213
     *  Constructor
214
     *
215
     *  @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...
216
     */
217
    public function __construct($db)
218
    {
219
        $this->db = $db;
220
221
        $this->ismultientitymanaged = 1;
222
223
        $this->labelStatus[self::STATUS_CLOSED] = 'Closed2';
224
        if (getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
225
            $this->labelStatus[self::STATUS_OPEN_ALL] = 'OpenAnyMovement';
226
            $this->labelStatus[self::STATUS_OPEN_INTERNAL] = 'OpenInternal';
227
        } else {
228
            $this->labelStatus[self::STATUS_OPEN_ALL] = 'Opened';
229
        }
230
    }
231
232
    /**
233
     *  Creation d'un entrepot en base
234
     *
235
     *  @param      User    $user       Object user that create the warehouse
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Product\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
236
     *  @param      int     $notrigger  0=launch triggers after, 1=disable triggers
237
     *  @return     int                 Return >0 if OK, =<0 if KO
238
     */
239
    public function create($user, $notrigger = 0)
240
    {
241
        global $conf;
242
243
        $error = 0;
244
245
        $this->label = trim($this->label);
246
247
        // Error if label not defined
248
        if ($this->label == '') {
249
            $this->error = "ErrorFieldRequired";
250
            return 0;
251
        }
252
253
        $now = dol_now();
254
255
        $this->db->begin();
256
257
        $sql = "INSERT INTO " . $this->db->prefix() . "entrepot (ref, entity, datec, fk_user_author, fk_parent, fk_project)";
258
        $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") . ")";
259
260
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
261
        $result = $this->db->query($sql);
262
        if ($result) {
263
            $id = $this->db->last_insert_id($this->db->prefix() . "entrepot");
264
            if ($id > 0) {
265
                $this->id = $id;
266
267
                if (!$error) {
268
                    $result = $this->update($id, $user);
269
                    if ($result <= 0) {
270
                        $error++;
271
                    }
272
                }
273
274
                // Actions on extra fields
275
                if (!$error) {
276
                    $result = $this->insertExtraFields();
277
                    if ($result < 0) {
278
                        $error++;
279
                    }
280
                }
281
282
                if (!$error && !$notrigger) {
283
                    // Call triggers
284
                    $result = $this->call_trigger('WAREHOUSE_CREATE', $user);
285
                    if ($result < 0) {
286
                        $error++;
287
                    }
288
                    // End call triggers
289
                }
290
291
                if (!$error) {
292
                    $this->db->commit();
293
                    return $id;
294
                } else {
295
                    dol_syslog(get_class($this) . "::create return -3");
296
                    $this->db->rollback();
297
                    return -3;
298
                }
299
            } else {
300
                $this->error = "Failed to get insert id";
301
                dol_syslog(get_class($this) . "::create return -2");
302
                return -2;
303
            }
304
        } else {
305
            $this->error = $this->db->error();
306
            dol_syslog(get_class($this) . "::create Error " . $this->db->error());
307
            $this->db->rollback();
308
            return -1;
309
        }
310
    }
311
312
    /**
313
     *  Update properties of a warehouse
314
     *
315
     *  @param      int     $id         id of warehouse to modify
316
     *  @param      User    $user       User object
317
     *  @param      int     $notrigger  0=launch triggers after, 1=disable trigge
318
     *  @return     int                 Return >0 if OK, <0 if KO
319
     */
320
    public function update($id, $user, $notrigger = 0)
321
    {
322
        $error = 0;
323
324
        if (empty($id)) {
325
            $id = $this->id;
326
        }
327
328
        // Check if new parent is already a child of current warehouse
329
        if (!empty($this->fk_parent)) {
330
            $TChildWarehouses = array($id);
331
            $TChildWarehouses = $this->get_children_warehouses($this->id, $TChildWarehouses);
332
            if (in_array($this->fk_parent, $TChildWarehouses)) {
333
                $this->error = 'ErrorCannotAddThisParentWarehouse';
334
                return -2;
335
            }
336
        }
337
338
        $this->label = trim($this->label);
339
340
        $this->description = trim($this->description);
341
342
        $this->lieu = trim($this->lieu);
343
344
        $this->address = trim($this->address);
345
        $this->zip = trim($this->zip);
346
        $this->town = trim($this->town);
347
        $this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
348
349
        $sql = "UPDATE " . $this->db->prefix() . "entrepot";
350
        $sql .= " SET ref = '" . $this->db->escape($this->label) . "'";
351
        $sql .= ", fk_parent = " . (($this->fk_parent > 0) ? $this->fk_parent : "NULL");
352
        $sql .= ", fk_project = " . (($this->fk_project > 0) ? $this->fk_project : "NULL");
353
        $sql .= ", description = '" . $this->db->escape($this->description) . "'";
354
        $sql .= ", statut = " . ((int) $this->statut);
355
        $sql .= ", lieu = '" . $this->db->escape($this->lieu) . "'";
356
        $sql .= ", address = '" . $this->db->escape($this->address) . "'";
357
        $sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
358
        $sql .= ", town = '" . $this->db->escape($this->town) . "'";
359
        $sql .= ", fk_pays = " . ((int) $this->country_id);
360
        $sql .= ", phone = '" . $this->db->escape($this->phone) . "'";
361
        $sql .= ", fax = '" . $this->db->escape($this->fax) . "'";
362
        $sql .= " WHERE rowid = " . ((int) $id);
363
364
        $this->db->begin();
365
366
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
367
        $resql = $this->db->query($sql);
368
369
        if (!$resql) {
370
            $error++;
371
            $this->errors[] = "Error " . $this->db->lasterror();
372
        }
373
374
        if (!$error) {
375
            $result = $this->insertExtraFields();
376
            if ($result < 0) {
377
                $error++;
378
            }
379
        }
380
381
        if (!$error && !$notrigger) {
382
            // Call triggers
383
            $result = $this->call_trigger('WAREHOUSE_MODIFY', $user);
384
            if ($result < 0) {
385
                $error++;
386
            }
387
            // End call triggers
388
        }
389
390
        if (!$error) {
391
            $this->db->commit();
392
            return 1;
393
        } else {
394
            $this->db->rollback();
395
            $this->error = $this->db->lasterror();
396
            return -1;
397
        }
398
    }
399
400
401
    /**
402
     *  Delete a warehouse
403
     *
404
     *  @param      User    $user          Object user that made deletion
405
     *  @param      int     $notrigger     1=No trigger
406
     *  @return     int                    Return integer <0 if KO, >0 if OK
407
     */
408
    public function delete($user, $notrigger = 0)
409
    {
410
        global $conf;
411
412
        $error = 0;
413
414
        dol_syslog(get_class($this) . "::delete id=" . $this->id, LOG_DEBUG);
415
416
        $this->db->begin();
417
418
        if (!$error && empty($notrigger)) {
419
            // Call trigger
420
            $result = $this->call_trigger('WAREHOUSE_DELETE', $user);
421
            if ($result < 0) {
422
                $error++;
423
            }
424
            // End call triggers
425
        }
426
427
        if (!$error) {
428
            $sql = "DELETE FROM " . $this->db->prefix() . "product_batch";
429
            $sql .= " WHERE fk_product_stock IN (SELECT rowid FROM " . $this->db->prefix() . "product_stock as ps WHERE ps.fk_entrepot = " . ((int) $this->id) . ")";
430
            $result = $this->db->query($sql);
431
            if (!$result) {
432
                $error++;
433
                $this->errors[] = $this->db->lasterror();
434
            }
435
        }
436
437
        if (!$error) {
438
            $elements = array('stock_mouvement', 'product_stock');
439
            foreach ($elements as $table) {
440
                if (!$error) {
441
                    $sql = "DELETE FROM " . $this->db->prefix() . $table;
442
                    $sql .= " WHERE fk_entrepot = " . ((int) $this->id);
443
444
                    $result = $this->db->query($sql);
445
                    if (!$result) {
446
                        $error++;
447
                        $this->errors[] = $this->db->lasterror();
448
                    }
449
                }
450
            }
451
        }
452
453
        // Removed extrafields
454
        if (!$error) {
455
            $result = $this->deleteExtraFields();
456
            if ($result < 0) {
457
                $error++;
458
                dol_syslog(get_class($this) . "::delete Error " . $this->error, LOG_ERR);
459
            }
460
        }
461
462
        if (!$error) {
463
            $sql = "DELETE FROM " . $this->db->prefix() . "entrepot";
464
            $sql .= " WHERE rowid = " . ((int) $this->id);
465
            $resql1 = $this->db->query($sql);
466
            if (!$resql1) {
467
                $error++;
468
                $this->errors[] = $this->db->lasterror();
469
                dol_syslog(get_class($this) . "::delete Error " . $this->db->lasterror(), LOG_ERR);
470
            }
471
        }
472
473
        if (!$error) {
474
            // Update denormalized fields because we change content of produt_stock. Warning: Do not use "SET p.stock", does not works with pgsql
475
            $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)";
476
            $resql2 = $this->db->query($sql);
477
            if (!$resql2) {
478
                $error++;
479
                $this->errors[] = $this->db->lasterror();
480
                dol_syslog(get_class($this) . "::delete Error " . $this->db->lasterror(), LOG_ERR);
481
            }
482
        }
483
484
        if (!$error) {
485
            $this->db->commit();
486
            return 1;
487
        } else {
488
            $this->db->rollback();
489
            return -1;
490
        }
491
    }
492
493
494
    /**
495
     *  Load warehouse data
496
     *
497
     *  @param      int     $id     Warehouse id
498
     *  @param      string  $ref    Warehouse label
499
     *  @return     int             >0 if OK, <0 if KO
500
     */
501
    public function fetch($id, $ref = '')
502
    {
503
        global $conf;
504
505
        dol_syslog(get_class($this) . "::fetch id=" . $id . " ref=" . $ref);
506
507
        // Check parameters
508
        if (!$id && !$ref) {
509
            $this->error = 'ErrorWrongParameters';
510
            dol_syslog(get_class($this) . "::fetch " . $this->error);
511
            return -1;
512
        }
513
514
        $sql  = "SELECT rowid, entity, fk_parent, fk_project, ref as label, description, statut, lieu, address, zip, town, fk_pays as country_id, phone, fax,";
515
        $sql .= " model_pdf, import_key";
516
        $sql .= " FROM " . $this->db->prefix() . "entrepot";
517
        if ($id) {
518
            $sql .= " WHERE rowid = " . ((int) $id);
519
        } else {
520
            $sql .= " WHERE entity IN (" . getEntity('stock') . ")";
521
            if ($ref) {
522
                $sql .= " AND ref = '" . $this->db->escape($ref) . "'";
523
            }
524
        }
525
526
        $result = $this->db->query($sql);
527
        if ($result) {
528
            if ($this->db->num_rows($result) > 0) {
529
                $obj = $this->db->fetch_object($result);
530
531
                $this->id             = $obj->rowid;
532
                $this->entity         = $obj->entity;
533
                $this->fk_parent      = $obj->fk_parent;
534
                $this->fk_project     = $obj->fk_project;
535
                $this->ref            = $obj->label;
536
                $this->label          = $obj->label;
537
                $this->description    = $obj->description;
538
                $this->statut         = $obj->statut;
539
                $this->lieu           = $obj->lieu;
540
                $this->address        = $obj->address;
541
                $this->zip            = $obj->zip;
542
                $this->town           = $obj->town;
543
                $this->country_id     = $obj->country_id;
544
                $this->phone          = $obj->phone;
545
                $this->fax            = $obj->fax;
546
547
                $this->model_pdf      = $obj->model_pdf;
548
                $this->import_key     = $obj->import_key;
549
550
                // Retrieve all extrafield
551
                // fetch optionals attributes and labels
552
                $this->fetch_optionals();
553
554
                include_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
555
                $tmp = getCountry($this->country_id, 'all');
556
                $this->country = $tmp['label'];
557
                $this->country_code = $tmp['code'];
558
559
                return 1;
560
            } else {
561
                $this->error = "Record Not Found";
562
                return 0;
563
            }
564
        } else {
565
            $this->error = $this->db->error();
566
            return -1;
567
        }
568
    }
569
570
571
    /**
572
     *  Load warehouse info data
573
     *
574
     *  @param  int     $id      warehouse id
575
     *  @return void
576
     */
577
    public function info($id)
578
    {
579
        $sql = "SELECT e.rowid, e.datec, e.tms as datem, e.fk_user_author";
580
        $sql .= " FROM " . $this->db->prefix() . "entrepot as e";
581
        $sql .= " WHERE e.rowid = " . ((int) $id);
582
583
        dol_syslog(get_class($this) . "::info", LOG_DEBUG);
584
        $result = $this->db->query($sql);
585
        if ($result) {
586
            if ($this->db->num_rows($result)) {
587
                $obj = $this->db->fetch_object($result);
588
589
                $this->id = $obj->rowid;
590
591
                $this->user_creation_id = $obj->fk_user_author;
592
                $this->date_creation     = $this->db->jdate($obj->datec);
593
                $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
594
            }
595
596
            $this->db->free($result);
597
        } else {
598
            dol_print_error($this->db);
599
        }
600
    }
601
602
603
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
604
    /**
605
     *  Return list of all warehouses
606
     *
607
     *  @param  int     $status     Status
608
     *  @return array               Array list of warehouses
609
     */
610
    public function list_array($status = 1)
611
    {
612
		// phpcs:enable
613
        $liste = array();
614
615
        $sql = "SELECT rowid, ref as label";
616
        $sql .= " FROM " . $this->db->prefix() . "entrepot";
617
        $sql .= " WHERE entity IN (" . getEntity('stock') . ")";
618
        $sql .= " AND statut = " . ((int) $status);
619
620
        $result = $this->db->query($sql);
621
        $i = 0;
622
        $num = $this->db->num_rows($result);
623
        if ($result) {
624
            while ($i < $num) {
625
                $row = $this->db->fetch_row($result);
626
                $liste[$row[0]] = $row[1];
627
                $i++;
628
            }
629
            $this->db->free($result);
630
        }
631
        return $liste;
632
    }
633
634
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
635
    /**
636
     *  Return number of unique different product into a warehouse
637
     *
638
     *  @return     array|int       Array('nb'=>Nb, 'value'=>Value)
639
     */
640
    public function nb_different_products()
641
    {
642
		// phpcs:enable
643
        $ret = array();
644
645
        $sql = "SELECT count(distinct p.rowid) as nb";
646
        $sql .= " FROM " . $this->db->prefix() . "product_stock as ps";
647
        $sql .= ", " . $this->db->prefix() . "product as p";
648
        $sql .= " WHERE ps.fk_entrepot = " . ((int) $this->id);
649
        $sql .= " AND ps.fk_product = p.rowid";
650
651
        //print $sql;
652
        $result = $this->db->query($sql);
653
        if ($result) {
654
            $obj = $this->db->fetch_object($result);
655
            $ret['nb'] = $obj->nb;
656
            $this->db->free($result);
657
        } else {
658
            $this->error = $this->db->lasterror();
659
            return -1;
660
        }
661
662
        return $ret;
663
    }
664
665
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
666
    /**
667
     *  Return stock and value of warehosue
668
     *
669
     *  @return     array|int       Array('nb'=>Nb, 'value'=>Value)
670
     */
671
    public function nb_products()
672
    {
673
        global $conf;
674
		// phpcs:enable
675
        $ret = array();
676
677
        //For MultiCompany PMP per entity
678
        $separatedPMP = false;
679
        if (getDolGlobalString('MULTICOMPANY_PRODUCT_SHARING_ENABLED') && getDolGlobalString('MULTICOMPANY_PMP_PER_ENTITY_ENABLED')) {
680
            $separatedPMP = true;
681
        }
682
683
        if ($separatedPMP) {
684
            $sql = "SELECT sum(ps.reel) as nb, sum(ps.reel * pa.pmp) as value";
685
        } else {
686
            $sql = "SELECT sum(ps.reel) as nb, sum(ps.reel * p.pmp) as value";
687
        }
688
        $sql .= " FROM " . $this->db->prefix() . "product_stock as ps";
689
        $sql .= ", " . $this->db->prefix() . "product as p";
690
        if ($separatedPMP) {
691
            $sql .= ", " . $this->db->prefix() . "product_perentity as pa";
692
        }
693
        $sql .= " WHERE ps.fk_entrepot = " . ((int) $this->id);
694
        if ($separatedPMP) {
695
            $sql .= " AND pa.fk_product = p.rowid AND pa.entity = " . (int) $conf->entity;
696
        }
697
        $sql .= " AND ps.fk_product = p.rowid";
698
        //print $sql;
699
        $result = $this->db->query($sql);
700
        if ($result) {
701
            $obj = $this->db->fetch_object($result);
702
            $ret['nb'] = $obj->nb;
703
            $ret['value'] = $obj->value;
704
            $this->db->free($result);
705
        } else {
706
            $this->error = $this->db->lasterror();
707
            return -1;
708
        }
709
710
        return $ret;
711
    }
712
713
    /**
714
     *  Return label of status of object
715
     *
716
     *  @param      int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
717
     *  @return     string              Label of status
718
     */
719
    public function getLibStatut($mode = 0)
720
    {
721
        return $this->LibStatut($this->statut, $mode);
722
    }
723
724
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
725
    /**
726
     *  Return label of a given status
727
     *
728
     *  @param  int     $status     Id status
729
     *  @param  int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
730
     *  @return string              Label of status
731
     */
732
    public function LibStatut($status, $mode = 0)
733
    {
734
		// phpcs:enable
735
        global $langs;
736
737
        $statusType = 'status5';
738
        if ($status > 0) {
739
            $statusType = 'status4';
740
        }
741
742
        $langs->load('stocks');
743
        $label = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
744
        $labelshort = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
745
746
        return dolGetStatus($label, $labelshort, '', $statusType, $mode);
747
    }
748
749
    /**
750
     * getTooltipContentArray
751
     *
752
     * @param   array   $params     Params to construct tooltip data
753
     * @since   v18
754
     * @return  array
755
     */
756
    public function getTooltipContentArray($params)
757
    {
758
        global $conf, $langs, $user;
759
760
        $langs->load('stocks');
761
762
        $datas = [];
763
764
        $option = $params['option'] ?? '';
765
        $nofetch = !empty($params['nofetch']);
766
767
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
768
            return ['optimize' => $langs->trans("Warehouse")];
769
        }
770
        $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("Warehouse") . '</u>';
771
        if (isset($this->statut)) {
772
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
773
        }
774
        $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . (empty($this->ref) ? $this->label : $this->ref);
775
        if (!empty($this->lieu)) {
776
            $datas['locationsummary'] = '<br><b>' . $langs->trans('LocationSummary') . ':</b> ' . $this->lieu;
777
        }
778
        // show categories for this record only in ajax to not overload lists
779
        if (!$nofetch && isModEnabled('category')) {
780
            require_once constant('DOL_DOCUMENT_ROOT') . '/categories/class/categorie.class.php';
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
        require_once constant('DOL_DOCUMENT_ROOT') . '/categories/class/categorie.class.php';
1021
        return parent::setCategoriesCommon($categories, Categorie::TYPE_WAREHOUSE);
1022
    }
1023
1024
    /**
1025
     *  Return clicable link of object (with eventually picto)
1026
     *
1027
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
1028
     *  @param      array       $arraydata              Array of data
1029
     *  @return     string                              HTML Code for Kanban thumb.
1030
     */
1031
    public function getKanbanView($option = '', $arraydata = null)
1032
    {
1033
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1034
1035
        $return = '<div class="box-flex-item box-flex-grow-zero">';
1036
        $return .= '<div class="info-box info-box-sm">';
1037
        $return .= '<div class="info-box-icon bg-infobox-action" >';
1038
        $return .= img_picto('', $this->picto);
1039
        $return .= '</div>';
1040
        $return .= '<div class="info-box-content" >';
1041
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
1042
        if ($selected >= 0) {
1043
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
1044
        }
1045
        if (property_exists($this, 'lieu') && (!empty($this->lieu))) {
1046
            $return .= '<br><span class="info-box-label opacitymedium">' . $this->lieu . '</span>';
1047
        }
1048
        if (property_exists($this, 'sellvalue') && $this->sellvalue != 0) {
0 ignored issues
show
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...
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...
1049
            $return .= '<br><span class="info-box-label amount">' . price($this->sellvalue) . '</span>';
1050
        }
1051
        if (method_exists($this, 'getLibStatut')) {
1052
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
1053
        }
1054
        $return .= '</div>';
1055
        $return .= '</div>';
1056
        $return .= '</div>';
1057
        return $return;
1058
    }
1059
}
1060