Passed
Push — main ( 7eb3b3...9a81fe )
by Rafael
42:16
created

Bom::addLine()   F

Complexity

Conditions 16
Paths 1025

Size

Total Lines 90
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 61
c 0
b 0
f 0
nc 1025
nop 11
dl 0
loc 90
rs 1.4

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2019       Laurent Destailleur     <[email protected]>
4
 * Copyright (C) 2023	    Benjamin Falière	    <[email protected]>
5
 * Copyright (C) 2023	    Charlene Benke		    <[email protected]>
6
 * Copyright (C) 2024       Frédéric France         <[email protected]>
7
 * Copyright (C) 2024		MDW						<[email protected]>
8
 * Copyright (C) 2024       Rafael San José         <[email protected]>
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23
24
/**
25
 * \file        htdocs/bom/class/bom.class.php
26
 * \ingroup     bom
27
 * \brief       This file is a CRUD class file for BOM (Create/Read/Update/Delete)
28
 */
29
30
// Put here all includes required by your class file
31
namespace DoliModules\Bom\Model;
32
33
use CommonObject;
34
use DoliCore\Base\GenericDocument;
35
use DoliDB;
36
use Product;
37
use ProductFournisseur;
38
use Translate;
39
use User;
40
use Workstation;
41
42
require_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
43
44
if (isModEnabled('workstation')) {
45
    require_once DOL_DOCUMENT_ROOT . '/workstation/class/workstation.class.php';
46
}
47
48
////
49
50
/**
51
 * Class for BOM
52
 */
53
class Bom extends GenericDocument
54
{
55
    const STATUS_DRAFT = 0;
56
    const STATUS_VALIDATED = 1;
57
    const STATUS_CANCELED = 9;
58
    /**
59
     * @var string ID of module.
60
     */
61
    public $module = 'bom';
62
    /**
63
     * @var string ID to identify managed object
64
     */
65
    public $element = 'bom';
66
    /**
67
     * @var string Name of table without prefix where object is stored
68
     */
69
    public $table_element = 'bom_bom';
70
    /**
71
     * @var int  Does bom support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link
72
     *      by societe
73
     */
74
    public $ismultientitymanaged = 1;
75
    /**
76
     * @var int  Does object support extrafields ? 0=No, 1=Yes
77
     */
78
    public $isextrafieldmanaged = 1;
79
    /**
80
     * @var string String with name of icon for bom. Must be the part after the 'object_' into object_bom.png
81
     */
82
    public $picto = 'bom';
83
    /**
84
     * @var Product Object product of the BOM
85
     */
86
    public $product;
87
88
89
    /**
90
     *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]',
91
     *  'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)',
92
     *  'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone',
93
     *  'url', 'password') Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or
94
     *  (t.nature:is:NULL)"
95
     *  'label' the translation key.
96
     *  'picto' is code of a picto to show before value in forms
97
     *  'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM)
98
     *  'position' is the sort order of field.
99
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
100
     *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view
101
     *  forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and
102
     *  update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative
103
     *  value means field is not shown by default on list but can be selected for viewing)
104
     *  'noteditable' says if field is not editable (1 or 0)
105
     *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is
106
     *  editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be
107
     *  set to '(PROVid)' where id is rowid when a new record is created.
108
     *  'index' if we want an index in database.
109
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
110
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
111
     *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable
112
     *  like integer or double(24,8).
113
     *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update.
114
     *  'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300
115
     *  maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
116
     *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use
117
     *  'TranslationString:keyfortooltiponlick' for a tooltip on click.
118
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
119
     *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set
120
     *  into the definition of $fields into class, but is set dynamically by some part of code.
121
     *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example:
122
     *  array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
123
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set
124
     *  to 1.
125
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
126
     *
127
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the
128
     *  constructor.
129
     */
130
131
    // BEGIN MODULEBUILDER PROPERTIES
132
    /**
133
     * @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}>
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...
134
     *      Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
135
     */
136
    public $fields = [
137
        'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id",],
138
        'entity' => ['type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'default' => 1, 'index' => 1, 'position' => 5],
139
        'ref' => ['type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'noteditable' => 1, 'visible' => 4, 'position' => 10, 'notnull' => 1, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'comment' => "Reference of BOM", 'showoncombobox' => 1, 'csslist' => 'nowraponall'],
140
        'label' => ['type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => 1, 'position' => 30, 'notnull' => 1, 'searchall' => 1, 'showoncombobox' => '2', 'autofocusoncreate' => 1, 'css' => 'minwidth300 maxwidth400', 'csslist' => 'tdoverflowmax200'],
141
        'bomtype' => ['type' => 'integer', 'label' => 'Type', 'enabled' => 1, 'visible' => 1, 'position' => 33, 'notnull' => 1, 'default' => '0', 'arrayofkeyval' => [0 => 'Manufacturing', 1 => 'Disassemble'], 'css' => 'minwidth175', 'csslist' => 'minwidth175 center'],
142
        //'bomtype' => array('type'=>'integer', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'position'=>32, 'notnull'=>1, 'default'=>'0', 'arrayofkeyval'=>array(0=>'Manufacturing')),
143
        'fk_product' => ['type' => 'integer:Product:product/class/product.class.php:1:((finished:is:null) or (finished:!=:0))', 'label' => 'Product', 'picto' => 'product', 'enabled' => 1, 'visible' => 1, 'position' => 35, 'notnull' => 1, 'index' => 1, 'help' => 'ProductBOMHelp', 'css' => 'maxwidth500', 'csslist' => 'tdoverflowmax100'],
144
        'description' => ['type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -1, 'position' => 60, 'notnull' => -1,],
145
        'qty' => ['type' => 'real', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'default' => 1, 'position' => 55, 'notnull' => 1, 'isameasure' => 1, 'css' => 'maxwidth50imp right'],
146
        //'efficiency' => array('type'=>'real', 'label'=>'ManufacturingEfficiency', 'enabled'=>1, 'visible'=>-1, 'default'=>1, 'position'=>100, 'notnull'=>0, 'css'=>'maxwidth50imp', 'help'=>'ValueOfMeansLossForProductProduced'),
147
        'duration' => ['type' => 'duration', 'label' => 'EstimatedDuration', 'enabled' => 1, 'visible' => -1, 'position' => 101, 'notnull' => -1, 'css' => 'maxwidth50imp', 'help' => 'EstimatedDurationDesc'],
148
        'fk_warehouse' => ['type' => 'integer:Entrepot:product/stock/class/entrepot.class.php:0', 'label' => 'WarehouseForProduction', 'picto' => 'stock', 'enabled' => 1, 'visible' => -1, 'position' => 102, 'css' => 'maxwidth500', 'csslist' => 'tdoverflowmax100'],
149
        'note_public' => ['type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => -2, 'position' => 161, 'notnull' => -1,],
150
        'note_private' => ['type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => -2, 'position' => 162, 'notnull' => -1,],
151
        'date_creation' => ['type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 300, 'notnull' => 1,],
152
        'tms' => ['type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'position' => 501, 'notnull' => 1,],
153
        'date_valid' => ['type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -2, 'position' => 502, 'notnull' => 0,],
154
        'fk_user_creat' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserCreation', 'picto' => 'user', 'enabled' => 1, 'visible' => -2, 'position' => 510, 'notnull' => 1, 'foreignkey' => 'user.rowid', 'csslist' => 'tdoverflowmax100'],
155
        'fk_user_modif' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'picto' => 'user', 'enabled' => 1, 'visible' => -2, 'position' => 511, 'notnull' => -1, 'csslist' => 'tdoverflowmax100'],
156
        'fk_user_valid' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'picto' => 'user', 'enabled' => 1, 'visible' => -2, 'position' => 512, 'notnull' => 0, 'csslist' => 'tdoverflowmax100'],
157
        'import_key' => ['type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 1000, 'notnull' => -1,],
158
        'model_pdf' => ['type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010],
159
        'status' => ['type' => 'integer', 'label' => 'Status', 'enabled' => 1, 'visible' => 2, 'position' => 1000, 'notnull' => 1, 'default' => 0, 'index' => 1, 'arrayofkeyval' => [0 => 'Draft', 1 => 'Enabled', 9 => 'Disabled']],
160
    ];
161
162
    /**
163
     * @var int rowid
164
     */
165
    public $rowid;
166
167
    /**
168
     * @var string ref
169
     */
170
    public $ref;
171
172
    /**
173
     * @var string label
174
     */
175
    public $label;
176
177
    /**
178
     * @var int bomtype
179
     */
180
    public $bomtype;
181
182
    /**
183
     * @var string description
184
     */
185
    public $description;
186
187
    /**
188
     * @var integer|string date_creation
189
     */
190
    public $date_creation;
191
192
    /**
193
     * @var integer|string date_valid
194
     */
195
    public $date_valid;
196
197
    /**
198
     * @var int Id User creator
199
     */
200
    public $fk_user_creat;
201
202
    /**
203
     * @var int Id User modifying
204
     */
205
    public $fk_user_modif;
206
207
    /**
208
     * @var int Id User modifying
209
     */
210
    public $fk_user_valid;
211
212
    /**
213
     * @var int Id User modifying
214
     */
215
    public $fk_warehouse;
216
217
    /**
218
     * @var string import key
219
     */
220
    public $import_key;
221
222
    /**
223
     * @var int status
224
     */
225
    public $status;
226
227
    /**
228
     * @var int product Id
229
     */
230
    public $fk_product;
231
    public $qty;
232
    public $duration;
233
    public $efficiency;
234
    // END MODULEBUILDER PROPERTIES
235
236
237
    // If this object has a subtable with lines
238
239
    /**
240
     * @var string    Name of subtable line
241
     */
242
    public $table_element_line = 'bom_bomline';
243
244
    /**
245
     * @var string    Fieldname with ID of parent key if this field has a parent
246
     */
247
    public $fk_element = 'fk_bom';
248
249
    /**
250
     * @var string    Name of subtable class that manage subtable lines
251
     */
252
    public $class_element_line = 'BomLine';
253
254
    // /**
255
    //  * @var array    List of child tables. To test if we can delete object.
256
    //  */
257
    // protected $childtables=array();
258
    /**
259
     * @var BOMLine[]     Array of subtable lines
260
     */
261
    public $lines = [];
262
    /**
263
     * @var float       Calculated cost for the BOM
264
     */
265
    public $total_cost = 0;
266
    /**
267
     * @var float       Calculated cost for 1 unit of the product in BOM
268
     */
269
    public $unit_cost = 0;
270
    /**
271
     * @var string[]    List of child tables. To know object to delete on cascade.
272
     */
273
    protected $childtablesoncascade = ['bom_bomline'];
274
275
    /**
276
     * Constructor
277
     *
278
     * @param DoliDB $db Database handler
279
     */
280
    public function __construct(DoliDB $db)
281
    {
282
        global $conf, $langs;
283
284
        $this->db = $db;
285
286
        if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
287
            $this->fields['rowid']['visible'] = 0;
288
        }
289
        if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
290
            $this->fields['entity']['enabled'] = 0;
291
        }
292
293
        // Unset fields that are disabled
294
        foreach ($this->fields as $key => $val) {
295
            if (isset($val['enabled']) && empty($val['enabled'])) {
296
                unset($this->fields[$key]);
297
            }
298
        }
299
300
        // Translate some data of arrayofkeyval
301
        foreach ($this->fields as $key => $val) {
302
            if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
303
                foreach ($val['arrayofkeyval'] as $key2 => $val2) {
304
                    $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
305
                }
306
            }
307
        }
308
    }
309
310
    /**
311
     * Function used to replace a product id with another one.
312
     *
313
     * @param DoliDB $db        Database handler
314
     * @param int    $origin_id Old product id
315
     * @param int    $dest_id   New product id
316
     *
317
     * @return bool
318
     */
319
    public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
320
    {
321
        $tables = [
322
            'bom_bomline',
323
        ];
324
325
        return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
326
    }
327
328
    /**
329
     * Clone an object into another one
330
     *
331
     * @param User $user   User that creates
332
     * @param int  $fromid Id of object to clone
333
     *
334
     * @return  mixed               New object created, <0 if KO
335
     */
336
    public function createFromClone(User $user, $fromid)
337
    {
338
        global $langs, $hookmanager, $extrafields;
339
        $error = 0;
340
341
        dol_syslog(__METHOD__, LOG_DEBUG);
342
343
        $object = new self($this->db);
344
345
        $this->db->begin();
346
347
        // Load source object
348
        $result = $object->fetchCommon($fromid);
349
        if ($result > 0 && !empty($object->table_element_line)) {
350
            $object->fetchLines();
351
        }
352
353
        // Get lines so they will be clone
354
        //foreach ($object->lines as $line)
355
        //  $line->fetch_optionals();
356
357
        // Reset some properties
358
        unset($object->id);
359
        unset($object->fk_user_creat);
360
        unset($object->import_key);
361
362
        // Clear fields
363
        $object->ref = empty($this->fields['ref']['default']) ? $langs->trans("copy_of_") . $object->ref : $this->fields['ref']['default'];
364
        $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf") . " " . $object->label : $this->fields['label']['default'];
365
        $object->status = self::STATUS_DRAFT;
366
        // ...
367
        // Clear extrafields that are unique
368
        if (is_array($object->array_options) && count($object->array_options) > 0) {
369
            $extrafields->fetch_name_optionals_label($object->table_element);
370
            foreach ($object->array_options as $key => $option) {
371
                $shortkey = preg_replace('/options_/', '', $key);
372
                if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
373
                    //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
374
                    unset($object->array_options[$key]);
375
                }
376
            }
377
        }
378
379
        // Create clone
380
        $object->context['createfromclone'] = 'createfromclone';
381
        $result = $object->createCommon($user);
382
        if ($result < 0) {
383
            $error++;
384
            $this->error = $object->error;
385
            $this->errors = $object->errors;
386
        }
387
388
        if (!$error) {
389
            // copy internal contacts
390
            if ($this->copy_linked_contact($object, 'internal') < 0) {
391
                $error++;
392
            }
393
        }
394
395
        if (!$error) {
396
            // copy external contacts if same company
397
            if (property_exists($this, 'socid') && $this->socid == $object->socid) {
0 ignored issues
show
Bug Best Practice introduced by
The property socid does not exist on DoliModules\Bom\Model\Bom. Did you maybe forget to declare it?
Loading history...
398
                if ($this->copy_linked_contact($object, 'external') < 0) {
399
                    $error++;
400
                }
401
            }
402
        }
403
404
        // If there is lines, create lines too
405
406
407
        unset($object->context['createfromclone']);
408
409
        // End
410
        if (!$error) {
411
            $this->db->commit();
412
            return $object;
413
        } else {
414
            $this->db->rollback();
415
            return -1;
416
        }
417
    }
418
419
    /**
420
     * Load object lines in memory from the database
421
     *
422
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
423
     */
424
    public function fetchLines()
425
    {
426
        $this->lines = [];
427
428
        $result = $this->fetchLinesCommon();
429
        return $result;
430
    }
431
432
    /**
433
     * Load object lines in memory from the database by type of product
434
     *
435
     * @param int $typeproduct 0 type product, 1 type service
436
     *
437
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
438
     */
439
    public function fetchLinesbytypeproduct($typeproduct = 0)
440
    {
441
        $this->lines = [];
442
443
        $objectlineclassname = get_class($this) . 'Line';
444
        if (!class_exists($objectlineclassname)) {
445
            $this->error = 'Error, class ' . $objectlineclassname . ' not found during call of fetchLinesCommon';
446
            return -1;
447
        }
448
449
        $objectline = new $objectlineclassname($this->db);
450
451
        $sql = "SELECT " . $objectline->getFieldList('l');
452
        $sql .= " FROM " . $this->db->prefix() . $objectline->table_element . " as l";
453
        $sql .= " LEFT JOIN " . $this->db->prefix() . "product as p ON p.rowid = l.fk_product";
454
        $sql .= " WHERE l.fk_" . $this->db->escape($this->element) . " = " . ((int) $this->id);
455
        $sql .= " AND p.fk_product_type = " . ((int) $typeproduct);
456
        if (isset($objectline->fields['position'])) {
457
            $sql .= $this->db->order('position', 'ASC');
458
        }
459
460
        $resql = $this->db->query($sql);
461
        if ($resql) {
462
            $num_rows = $this->db->num_rows($resql);
463
            $i = 0;
464
            while ($i < $num_rows) {
465
                $obj = $this->db->fetch_object($resql);
466
                if ($obj) {
467
                    $newline = new $objectlineclassname($this->db);
468
                    $newline->setVarsFromFetchObj($obj);
469
470
                    $this->lines[] = $newline;
471
                }
472
                $i++;
473
            }
474
475
            return $num_rows;
476
        } else {
477
            $this->error = $this->db->lasterror();
478
            $this->errors[] = $this->error;
479
            return -1;
480
        }
481
    }
482
483
    /**
484
     * Add an BOM line into database (linked to BOM)
485
     *
486
     * @param int    $fk_product             Id of product
487
     * @param float  $qty                    Quantity
488
     * @param int    $qty_frozen             Frozen quantity
489
     * @param int    $disable_stock_change   Disable stock change on using in MO
490
     * @param float  $efficiency             Efficiency in MO
491
     * @param int    $position               Position of BOM-Line in BOM-Lines
492
     * @param int    $fk_bom_child           Id of BOM Child
493
     * @param string $import_key             Import Key
494
     * @param int    $fk_unit                Unit
495
     * @param array  $array_options          extrafields array
496
     * @param int    $fk_default_workstation Default workstation
497
     *
498
     * @return  int                             Return integer <0 if KO, Id of created object if OK
499
     */
500
    public function addLine($fk_product, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $fk_bom_child = null, $import_key = null, $fk_unit = 0, $array_options = [], $fk_default_workstation = null)
501
    {
502
        global $mysoc, $conf, $langs, $user;
503
504
        $logtext = "::addLine bomid=$this->id, qty=$qty, fk_product=$fk_product, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
505
        $logtext .= ", fk_bom_child=$fk_bom_child, import_key=$import_key";
506
        dol_syslog(get_class($this) . $logtext, LOG_DEBUG);
507
508
        if ($this->statut == self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property DoliCore\Base\GenericDocument::$statut has been deprecated: Use $status instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

508
        if (/** @scrutinizer ignore-deprecated */ $this->statut == self::STATUS_DRAFT) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
509
            include_once BASE_PATH . '/../Dolibarr/Lib/Price.php';
510
511
            // Clean parameters
512
            if (empty($qty)) {
513
                $qty = 0;
514
            }
515
            if (empty($qty_frozen)) {
516
                $qty_frozen = 0;
517
            }
518
            if (empty($disable_stock_change)) {
519
                $disable_stock_change = 0;
520
            }
521
            if (empty($efficiency)) {
522
                $efficiency = 1.0;
523
            }
524
            if (empty($fk_bom_child)) {
525
                $fk_bom_child = null;
526
            }
527
            if (empty($import_key)) {
528
                $import_key = null;
529
            }
530
            if (empty($position)) {
531
                $position = -1;
532
            }
533
534
            $qty = (float) price2num($qty);
535
            $efficiency = (float) price2num($efficiency);
536
            $position = (float) price2num($position);
537
538
            $this->db->begin();
539
540
            // Rank to use
541
            $rangMax = $this->line_max();
542
            $rankToUse = $position;
543
            if ($rankToUse <= 0 or $rankToUse > $rangMax) { // New line after existing lines
544
                $rankToUse = $rangMax + 1;
545
            } else { // New line between the existing lines
546
                foreach ($this->lines as $bl) {
547
                    if ($bl->position >= $rankToUse) {
548
                        $bl->position++;
549
                        $bl->update($user);
550
                    }
551
                }
552
            }
553
554
            // Insert line
555
            $line = new BomLine($this->db);
556
557
            $line->context = $this->context;
558
559
            $line->fk_bom = $this->id;
560
            $line->fk_product = $fk_product;
561
            $line->qty = $qty;
562
            $line->qty_frozen = $qty_frozen;
563
            $line->disable_stock_change = $disable_stock_change;
564
            $line->efficiency = $efficiency;
565
            $line->fk_bom_child = $fk_bom_child;
566
            $line->import_key = $import_key;
567
            $line->position = $rankToUse;
568
            $line->fk_unit = $fk_unit;
569
            $line->fk_default_workstation = $fk_default_workstation;
570
571
            if (is_array($array_options) && count($array_options) > 0) {
572
                $line->array_options = $array_options;
573
            }
574
575
            $result = $line->create($user);
576
577
            if ($result > 0) {
578
                $this->calculateCosts();
579
                $this->db->commit();
580
                return $result;
581
            } else {
582
                $this->setErrorsFromObject($line);
583
                dol_syslog(get_class($this) . "::addLine error=" . $this->error, LOG_ERR);
584
                $this->db->rollback();
585
                return -2;
586
            }
587
        } else {
588
            dol_syslog(get_class($this) . "::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
589
            return -3;
590
        }
591
    }
592
593
    /**
594
     * Update object into database
595
     *
596
     * @param User $user      User that modifies
597
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
598
     *
599
     * @return int             Return integer <0 if KO, >0 if OK
600
     */
601
    public function update(User $user, $notrigger = 1)
602
    {
603
        if ($this->efficiency <= 0 || $this->efficiency > 1) {
604
            $this->efficiency = 1;
605
        }
606
607
        return $this->updateCommon($user, $notrigger);
608
    }
609
610
    /**
611
     * Create object into database
612
     *
613
     * @param User $user      User that creates
614
     * @param int  $notrigger false=launch triggers after, true=disable triggers
615
     *
616
     * @return int             Return integer <0 if KO, Id of created object if OK
617
     */
618
    public function create(User $user, $notrigger = 1)
619
    {
620
        if ($this->efficiency <= 0 || $this->efficiency > 1) {
621
            $this->efficiency = 1;
622
        }
623
624
        return $this->createCommon($user, $notrigger);
625
    }
626
627
    /**
628
     * BOM costs calculation based on cost_price or pmp of each BOM line.
629
     * Set the property ->total_cost and ->unit_cost of BOM.
630
     *
631
     * @return int|string   Return integer <0 if KO, >0 if OK, or printable error result from hook
632
     */
633
    public function calculateCosts()
634
    {
635
        global $conf, $hookmanager;
636
637
        $this->unit_cost = 0;
638
        $this->total_cost = 0;
639
640
        $parameters = [];
641
        $reshook = $hookmanager->executeHooks('calculateCostsBom', $parameters, $this); // Note that $action and $object may have been modified by hook
642
643
        if ($reshook > 0) {
644
            return $hookmanager->resPrint;
645
        }
646
647
        if (is_array($this->lines) && count($this->lines)) {
648
            $productFournisseur = new ProductFournisseur($this->db);
649
            $tmpproduct = new Product($this->db);
650
651
            foreach ($this->lines as &$line) {
652
                $tmpproduct->cost_price = 0;
653
                $tmpproduct->pmp = 0;
654
                $result = $tmpproduct->fetch($line->fk_product, '', '', '', 0, 1, 1);   // We discard selling price and language loading
655
656
                if ($tmpproduct->type == $tmpproduct::TYPE_PRODUCT) {
657
                    if (empty($line->fk_bom_child)) {
658
                        if ($result < 0) {
659
                            $this->error = $tmpproduct->error;
660
                            return -1;
661
                        }
662
                        $unit_cost = (!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp;
663
                        $line->unit_cost = (float) price2num($unit_cost);
664
                        if (empty($line->unit_cost)) {
665
                            if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
666
                                if ($productFournisseur->fourn_remise_percent != "0") {
667
                                    $line->unit_cost = $productFournisseur->fourn_unitprice_with_discount;
668
                                } else {
669
                                    $line->unit_cost = $productFournisseur->fourn_unitprice;
670
                                }
671
                            }
672
                        }
673
674
                        $line->total_cost = (float) price2num($line->qty * $line->unit_cost, 'MT');
675
676
                        $this->total_cost += $line->total_cost;
677
                    } else {
678
                        $bom_child = new Bom($this->db);
679
                        $res = $bom_child->fetch($line->fk_bom_child);
680
                        if ($res > 0) {
681
                            $bom_child->calculateCosts();
682
                            $line->childBom[] = $bom_child;
683
                            $this->total_cost += (float) price2num($bom_child->total_cost * $line->qty, 'MT');
684
                            $this->total_cost += $line->total_cost;
685
                        } else {
686
                            $this->error = $bom_child->error;
687
                            return -2;
688
                        }
689
                    }
690
                } else {
691
                    // Convert qty of line into hours
692
                    $unitforline = measuringUnitString($line->fk_unit, '', '', 1);
693
                    $qtyhourforline = convertDurationtoHour($line->qty, $unitforline);
694
695
                    if (isModEnabled('workstation') && !empty($line->fk_default_workstation)) {
696
                        $workstation = new Workstation($this->db);
697
                        $res = $workstation->fetch($line->fk_default_workstation);
698
699
                        if ($res > 0) {
700
                            $line->total_cost = (float) price2num($qtyhourforline * ($workstation->thm_operator_estimated + $workstation->thm_machine_estimated), 'MT');
701
                        } else {
702
                            $this->error = $workstation->error;
703
                            return -3;
704
                        }
705
                    } else {
706
                        $defaultdurationofservice = $tmpproduct->duration;
707
                        $reg = [];
708
                        $qtyhourservice = 0;
709
                        if (preg_match('/^(\d+)([a-z]+)$/', $defaultdurationofservice, $reg)) {
710
                            $qtyhourservice = convertDurationtoHour($reg[1], $reg[2]);
711
                        }
712
713
                        if ($qtyhourservice) {
714
                            $line->total_cost = (float) price2num($qtyhourforline / $qtyhourservice * $tmpproduct->cost_price, 'MT');
715
                        } else {
716
                            $line->total_cost = (float) price2num($line->qty * $tmpproduct->cost_price, 'MT');
717
                        }
718
                    }
719
720
                    $this->total_cost += $line->total_cost;
721
                }
722
            }
723
724
            $this->total_cost = (float) price2num($this->total_cost, 'MT');
725
726
            if ($this->qty > 0) {
727
                $this->unit_cost = (float) price2num($this->total_cost / $this->qty, 'MU');
728
            } elseif ($this->qty < 0) {
729
                $this->unit_cost = (float) price2num($this->total_cost * $this->qty, 'MU');
730
            }
731
        }
732
733
        return 1;
734
    }
735
736
    /**
737
     * Load object in memory from the database
738
     *
739
     * @param int    $id  Id object
740
     * @param string $ref Ref
741
     *
742
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
743
     */
744
    public function fetch($id, $ref = null)
745
    {
746
        $result = $this->fetchCommon($id, $ref);
747
748
        if ($result > 0 && !empty($this->table_element_line)) {
749
            $this->fetchLines();
750
        }
751
        //$this->calculateCosts();      // This consume a high number of subrequests. Do not call it into fetch but when you need it.
752
753
        return $result;
754
    }
755
756
    /**
757
     * Update an BOM line into database
758
     *
759
     * @param int    $rowid                  Id of line to update
760
     * @param float  $qty                    Quantity
761
     * @param int    $qty_frozen             Frozen quantity
762
     * @param int    $disable_stock_change   Disable stock change on using in MO
763
     * @param float  $efficiency             Efficiency in MO
764
     * @param int    $position               Position of BOM-Line in BOM-Lines
765
     * @param string $import_key             Import Key
766
     * @param int    $fk_unit                Unit of line
767
     * @param array  $array_options          extrafields array
768
     * @param int    $fk_default_workstation Default workstation
769
     *
770
     * @return  int                             Return integer <0 if KO, Id of updated BOM-Line if OK
771
     */
772
    public function updateLine($rowid, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $import_key = null, $fk_unit = 0, $array_options = [], $fk_default_workstation = null)
773
    {
774
        global $mysoc, $conf, $langs, $user;
775
776
        $logtext = "::updateLine bomid=$this->id, qty=$qty, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
777
        $logtext .= ", import_key=$import_key";
778
        dol_syslog(get_class($this) . $logtext, LOG_DEBUG);
779
780
        if ($this->statut == self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property DoliCore\Base\GenericDocument::$statut has been deprecated: Use $status instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

780
        if (/** @scrutinizer ignore-deprecated */ $this->statut == self::STATUS_DRAFT) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
781
            include_once BASE_PATH . '/../Dolibarr/Lib/Price.php';
782
783
            // Clean parameters
784
            if (empty($qty)) {
785
                $qty = 0;
786
            }
787
            if (empty($qty_frozen)) {
788
                $qty_frozen = 0;
789
            }
790
            if (empty($disable_stock_change)) {
791
                $disable_stock_change = 0;
792
            }
793
            if (empty($efficiency)) {
794
                $efficiency = 1.0;
795
            }
796
            if (empty($import_key)) {
797
                $import_key = null;
798
            }
799
            if (empty($position)) {
800
                $position = -1;
801
            }
802
803
            $qty = (float) price2num($qty);
804
            $efficiency = (float) price2num($efficiency);
805
            $position = (float) price2num($position);
806
807
            $this->db->begin();
808
809
            //Fetch current line from the database and then clone the object and set it in $oldline property
810
            $line = new BomLine($this->db);
811
            $line->fetch($rowid);
812
            $line->fetch_optionals();
813
814
            $staticLine = clone $line;
815
            $line->oldcopy = $staticLine;
0 ignored issues
show
Documentation Bug introduced by
It seems like $staticLine of type DoliModules\Bom\Model\BomLine is incompatible with the declared type CommonObject of property $oldcopy.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
816
            $line->context = $this->context;
817
818
            // Rank to use
819
            $rankToUse = (int) $position;
820
            if ($rankToUse != $line->oldcopy->position) { // check if position have a new value
821
                foreach ($this->lines as $bl) {
822
                    if ($bl->position >= $rankToUse and $bl->position < ($line->oldcopy->position + 1)) { // move rank up
823
                        $bl->position++;
824
                        $bl->update($user);
825
                    }
826
                    if ($bl->position <= $rankToUse and $bl->position > ($line->oldcopy->position)) { // move rank down
827
                        $bl->position--;
828
                        $bl->update($user);
829
                    }
830
                }
831
            }
832
833
834
            $line->fk_bom = $this->id;
835
            $line->qty = $qty;
836
            $line->qty_frozen = $qty_frozen;
837
            $line->disable_stock_change = $disable_stock_change;
838
            $line->efficiency = $efficiency;
839
            $line->import_key = $import_key;
840
            $line->position = $rankToUse;
841
842
843
            if (!empty($fk_unit)) {
844
                $line->fk_unit = $fk_unit;
845
            }
846
847
848
            if (is_array($array_options) && count($array_options) > 0) {
849
                // We replace values in this->line->array_options only for entries defined into $array_options
850
                foreach ($array_options as $key => $value) {
851
                    $line->array_options[$key] = $array_options[$key];
852
                }
853
            }
854
            if ($fk_default_workstation >= 0 && $line->fk_default_workstation != $fk_default_workstation) {
855
                $line->fk_default_workstation = $fk_default_workstation;
856
            }
857
858
            $result = $line->update($user);
859
860
            if ($result > 0) {
861
                $this->calculateCosts();
862
                $this->db->commit();
863
                return $result;
864
            } else {
865
                $this->setErrorsFromObject($line);
866
                dol_syslog(get_class($this) . "::addLine error=" . $this->error, LOG_ERR);
867
                $this->db->rollback();
868
                return -2;
869
            }
870
        } else {
871
            dol_syslog(get_class($this) . "::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
872
            return -3;
873
        }
874
    }
875
876
    /**
877
     *  Delete a line of object in database
878
     *
879
     * @param User $user      User that delete
880
     * @param int  $idline    Id of line to delete
881
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
882
     *
883
     * @return int                 >0 if OK, <0 if KO
884
     */
885
    public function deleteLine(User $user, $idline, $notrigger = 0)
886
    {
887
        if ($this->status < 0) {
888
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
889
            return -2;
890
        }
891
892
        $this->db->begin();
893
894
        //Fetch current line from the database and then clone the object and set it in $oldline property
895
        $line = new BomLine($this->db);
896
        $line->fetch($idline);
897
        $line->fetch_optionals();
898
899
        $staticLine = clone $line;
900
        $line->oldcopy = $staticLine;
0 ignored issues
show
Documentation Bug introduced by
It seems like $staticLine of type DoliModules\Bom\Model\BomLine is incompatible with the declared type CommonObject of property $oldcopy.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
901
        $line->context = $this->context;
902
903
        $result = $line->delete($user, $notrigger);
904
905
        //Positions (rank) reordering
906
        foreach ($this->lines as $bl) {
907
            if ($bl->position > ($line->oldcopy->position)) { // move rank down
908
                $bl->position--;
909
                $bl->update($user);
910
            }
911
        }
912
913
        if ($result > 0) {
914
            $this->calculateCosts();
915
            $this->db->commit();
916
            return $result;
917
        } else {
918
            $this->setErrorsFromObject($line);
919
            dol_syslog(get_class($this) . "::addLine error=" . $this->error, LOG_ERR);
920
            $this->db->rollback();
921
            return -2;
922
        }
923
    }
924
925
    /**
926
     * Delete object in database
927
     *
928
     * @param User $user      User that deletes
929
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
930
     *
931
     * @return int              Return integer <0 if KO, >0 if OK
932
     */
933
    public function delete(User $user, $notrigger = 1)
934
    {
935
        return $this->deleteCommon($user, $notrigger);
936
        //return $this->deleteCommon($user, $notrigger, 1);
937
    }
938
939
    /**
940
     *  Validate bom
941
     *
942
     * @param User $user      User making status change
943
     * @param int  $notrigger 1=Does not execute triggers, 0= execute triggers
944
     *
945
     * @return     int                     Return integer <=0 if OK, 0=Nothing done, >0 if KO
946
     */
947
    public function validate($user, $notrigger = 0)
948
    {
949
        global $conf;
950
951
        require_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
952
953
        $error = 0;
954
955
        // Protection
956
        if ($this->status == self::STATUS_VALIDATED) {
957
            dol_syslog(get_class($this) . "::validate action abandoned: already validated", LOG_WARNING);
958
            return 0;
959
        }
960
961
        $now = dol_now();
962
963
        $this->db->begin();
964
965
        // Define new ref
966
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
967
            $this->fetch_product();
968
            $num = $this->getNextNumRef($this->product);
969
        } else {
970
            $num = $this->ref;
971
        }
972
        $this->newref = dol_sanitizeFileName($num);
973
974
        // Validate
975
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
976
        $sql .= " SET ref = '" . $this->db->escape($num) . "',";
977
        $sql .= " status = " . self::STATUS_VALIDATED . ",";
978
        $sql .= " date_valid='" . $this->db->idate($now) . "',";
979
        $sql .= " fk_user_valid = " . ((int) $user->id);
980
        $sql .= " WHERE rowid = " . ((int) $this->id);
981
982
        dol_syslog(get_class($this) . "::validate()", LOG_DEBUG);
983
        $resql = $this->db->query($sql);
984
        if (!$resql) {
985
            dol_print_error($this->db);
986
            $this->error = $this->db->lasterror();
987
            $error++;
988
        }
989
990
        if (!$error && !$notrigger) {
991
            // Call trigger
992
            $result = $this->call_trigger('BOM_VALIDATE', $user);
993
            if ($result < 0) {
994
                $error++;
995
            }
996
            // End call triggers
997
        }
998
999
        if (!$error) {
1000
            $this->oldref = $this->ref;
1001
1002
            // Rename directory if dir was a temporary ref
1003
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1004
                // Now we rename also files into index
1005
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'bom/" . $this->db->escape($this->newref) . "'";
1006
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'bom/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1007
                $resql = $this->db->query($sql);
1008
                if (!$resql) {
1009
                    $error++;
1010
                    $this->error = $this->db->lasterror();
1011
                }
1012
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'bom/" . $this->db->escape($this->newref) . "'";
1013
                $sql .= " WHERE filepath = 'bom/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1014
                $resql = $this->db->query($sql);
1015
                if (!$resql) {
1016
                    $error++;
1017
                    $this->error = $this->db->lasterror();
1018
                }
1019
1020
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1021
                $oldref = dol_sanitizeFileName($this->ref);
1022
                $newref = dol_sanitizeFileName($num);
1023
                $dirsource = $conf->bom->dir_output . '/' . $oldref;
1024
                $dirdest = $conf->bom->dir_output . '/' . $newref;
1025
                if (!$error && file_exists($dirsource)) {
1026
                    dol_syslog(get_class($this) . "::validate() rename dir " . $dirsource . " into " . $dirdest);
1027
1028
                    if (@rename($dirsource, $dirdest)) {
1029
                        dol_syslog("Rename ok");
1030
                        // Rename docs starting with $oldref with $newref
1031
                        $listoffiles = dol_dir_list($conf->bom->dir_output . '/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
1032
                        foreach ($listoffiles as $fileentry) {
1033
                            $dirsource = $fileentry['name'];
1034
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
1035
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
1036
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
1037
                            @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1037
                            /** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1038
                        }
1039
                    }
1040
                }
1041
            }
1042
        }
1043
1044
        // Set new ref and current status
1045
        if (!$error) {
1046
            $this->ref = $num;
1047
            $this->status = self::STATUS_VALIDATED;
1048
        }
1049
1050
        if (!$error) {
1051
            $this->db->commit();
1052
            return 1;
1053
        } else {
1054
            $this->db->rollback();
1055
            return -1;
1056
        }
1057
    }
1058
1059
    /**
1060
     *  Returns the reference to the following non used BOM depending on the active numbering module
1061
     *  defined into BOM_ADDON
1062
     *
1063
     * @param Product $prod Object product
1064
     *
1065
     * @return string              BOM free reference
1066
     */
1067
    public function getNextNumRef($prod)
1068
    {
1069
        global $langs, $conf;
1070
        $langs->load("mrp");
1071
1072
        if (getDolGlobalString('BOM_ADDON')) {
1073
            $mybool = false;
1074
1075
            $file = getDolGlobalString('BOM_ADDON') . ".php";
1076
            $classname = getDolGlobalString('BOM_ADDON');
1077
1078
            // Include file with class
1079
            $dirmodels = array_merge(['/'], (array) $conf->modules_parts['models']);
1080
            foreach ($dirmodels as $reldir) {
1081
                $dir = dol_buildpath($reldir . "core/modules/bom/");
1082
1083
                // Load file with numbering class (if found)
1084
                $mybool |= @include_once $dir . $file;
1085
            }
1086
1087
            if ($mybool === false) {
1088
                dol_print_error(null, "Failed to include file " . $file);
1089
                return '';
1090
            }
1091
1092
            $obj = new $classname();
1093
            $numref = $obj->getNextValue($prod, $this);
1094
1095
            if ($numref != "") {
1096
                return $numref;
1097
            } else {
1098
                $this->error = $obj->error;
1099
                //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1100
                return "";
1101
            }
1102
        } else {
1103
            print $langs->trans("Error") . " " . $langs->trans("Error_BOM_ADDON_NotDefined");
1104
            return "";
1105
        }
1106
    }
1107
1108
    /**
1109
     *  Set draft status
1110
     *
1111
     * @param User $user      Object user that modify
1112
     * @param int  $notrigger 1=Does not execute triggers, 0=Execute triggers
1113
     *
1114
     * @return int                     Return integer <0 if KO, >0 if OK
1115
     */
1116
    public function setDraft($user, $notrigger = 0)
1117
    {
1118
        // Protection
1119
        if ($this->status <= self::STATUS_DRAFT) {
1120
            return 0;
1121
        }
1122
1123
        return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'BOM_UNVALIDATE');
1124
    }
1125
1126
    /**
1127
     *  Set cancel status
1128
     *
1129
     * @param User $user      Object user that modify
1130
     * @param int  $notrigger 1=Does not execute triggers, 0=Execute triggers
1131
     *
1132
     * @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
1133
     */
1134
    public function cancel($user, $notrigger = 0)
1135
    {
1136
        // Protection
1137
        if ($this->status != self::STATUS_VALIDATED) {
1138
            return 0;
1139
        }
1140
1141
        return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'BOM_CLOSE');
1142
    }
1143
1144
    /**
1145
     *  Set cancel status
1146
     *
1147
     * @param User $user      Object user that modify
1148
     * @param int  $notrigger 1=Does not execute triggers, 0=Execute triggers
1149
     *
1150
     * @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
1151
     */
1152
    public function reopen($user, $notrigger = 0)
1153
    {
1154
        // Protection
1155
        if ($this->status != self::STATUS_CANCELED) {
1156
            return 0;
1157
        }
1158
1159
        return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'BOM_REOPEN');
1160
    }
1161
1162
    /**
1163
     *  Load the info information in the object
1164
     *
1165
     * @param int $id Id of object
1166
     *
1167
     * @return void
1168
     */
1169
    public function info($id)
1170
    {
1171
        $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1172
        $sql .= ' fk_user_creat, fk_user_modif';
1173
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
1174
        $sql .= ' WHERE t.rowid = ' . ((int) $id);
1175
        $result = $this->db->query($sql);
1176
        if ($result) {
1177
            if ($this->db->num_rows($result)) {
1178
                $obj = $this->db->fetch_object($result);
1179
1180
                $this->id = $obj->rowid;
1181
1182
                $this->user_creation_id = $obj->fk_user_creat;
1183
                $this->user_modification_id = $obj->fk_user_modif;
1184
                $this->date_creation = $this->db->jdate($obj->datec);
1185
                $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1186
            }
1187
1188
            $this->db->free($result);
1189
        } else {
1190
            dol_print_error($this->db);
1191
        }
1192
    }
1193
1194
    /**
1195
     *  Create an array of lines
1196
     *
1197
     * @return array|int       array of lines if OK, <0 if KO
1198
     */
1199
    public function getLinesArray()
1200
    {
1201
        $this->lines = [];
1202
1203
        $objectline = new BomLine($this->db);
1204
        $result = $objectline->fetchAll('ASC', 'position', 0, 0, '(fk_bom:=:' . ((int) $this->id) . ')');
1205
1206
        if (is_numeric($result)) {
1207
            $this->error = $objectline->error;
1208
            $this->errors = $objectline->errors;
1209
            return $result;
1210
        } else {
1211
            $this->lines = $result;
1212
            return $this->lines;
1213
        }
1214
    }
1215
1216
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1217
1218
    /**
1219
     * Load list of objects in memory from the database.
1220
     *
1221
     * @param string $sortorder  Sort Order
1222
     * @param string $sortfield  Sort field
1223
     * @param int    $limit      Limit
1224
     * @param int    $offset     Offset
1225
     * @param string $filter     Filter USF
1226
     * @param string $filtermode Filter mode (AND or OR)
1227
     *
1228
     * @return array|int                      int <0 if KO, array of pages if OK
1229
     */
1230
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
1231
    {
1232
        dol_syslog(__METHOD__, LOG_DEBUG);
1233
1234
        $records = [];
1235
1236
        $sql = 'SELECT ';
1237
        $sql .= $this->getFieldList();
1238
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
1239
        if ($this->ismultientitymanaged) {
1240
            $sql .= ' WHERE t.entity IN (' . getEntity($this->element) . ')';
1241
        } else {
1242
            $sql .= ' WHERE 1 = 1';
1243
        }
1244
1245
        // Manage filter
1246
        $errormessage = '';
1247
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1248
        if ($errormessage) {
1249
            $this->errors[] = $errormessage;
1250
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1251
            return -1;
1252
        }
1253
1254
        if (!empty($sortfield)) {
1255
            $sql .= $this->db->order($sortfield, $sortorder);
1256
        }
1257
        if (!empty($limit)) {
1258
            $sql .= $this->db->plimit($limit, $offset);
1259
        }
1260
1261
        $resql = $this->db->query($sql);
1262
        if ($resql) {
1263
            $num = $this->db->num_rows($resql);
1264
1265
            while ($obj = $this->db->fetch_object($resql)) {
1266
                $record = new self($this->db);
1267
                $record->setVarsFromFetchObj($obj);
1268
1269
                $records[$record->id] = $record;
1270
            }
1271
            $this->db->free($resql);
1272
1273
            return $records;
1274
        } else {
1275
            $this->errors[] = 'Error ' . $this->db->lasterror();
1276
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1277
1278
            return -1;
1279
        }
1280
    }
1281
1282
    /**
1283
     *  Create a document onto disk according to template module.
1284
     *
1285
     * @param string     $modele      Force template to use ('' to not force)
1286
     * @param Translate  $outputlangs object lang a utiliser pour traduction
1287
     * @param int        $hidedetails Hide details of lines
1288
     * @param int        $hidedesc    Hide description
1289
     * @param int        $hideref     Hide ref
1290
     * @param null|array $moreparams  Array to provide more information
1291
     *
1292
     * @return     int                         0 if KO, 1 if OK
1293
     */
1294
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1295
    {
1296
        global $conf, $langs;
1297
1298
        $langs->load("mrp");
1299
        $outputlangs->load("products");
1300
1301
        if (!dol_strlen($modele)) {
1302
            $modele = '';
1303
1304
            if ($this->model_pdf) {
1305
                $modele = $this->model_pdf;
1306
            } elseif (getDolGlobalString('BOM_ADDON_PDF')) {
1307
                $modele = getDolGlobalString('BOM_ADDON_PDF');
1308
            }
1309
        }
1310
1311
        $modelpath = "core/modules/bom/doc/";
1312
        if (!empty($modele)) {
1313
            return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1314
        } else {
1315
            return 0;
1316
        }
1317
    }
1318
1319
    /**
1320
     *  Return if at least one photo is available
1321
     *
1322
     * @param string $sdir Directory to scan
1323
     *
1324
     * @return boolean                 True if at least one photo is available, False if not
1325
     */
1326
    public function is_photo_available($sdir)
1327
    {
1328
        // phpcs:enable
1329
        include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1330
        include_once BASE_PATH . '/../Dolibarr/Lib/Images.php';
1331
1332
        $sdir .= '/' . get_exdir(0, 0, 0, 0, $this, 'bom');
1333
1334
        $dir_osencoded = dol_osencode($sdir);
1335
        if (file_exists($dir_osencoded)) {
1336
            $handle = opendir($dir_osencoded);
1337
            if (is_resource($handle)) {
1338
                while (($file = readdir($handle)) !== false) {
1339
                    if (!utf8_check($file)) {
1340
                        $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
1341
                    }
1342
                    if (dol_is_file($sdir . $file) && image_format_supported($file) >= 0) {
1343
                        return true;
1344
                    }
1345
                }
1346
            }
1347
        }
1348
        return false;
1349
    }
1350
1351
    /**
1352
     * Initialise object with example values
1353
     * Id must be 0 if object instance is a specimen
1354
     *
1355
     * @return int
1356
     */
1357
    public function initAsSpecimen()
1358
    {
1359
        $this->initAsSpecimenCommon();
1360
        $this->ref = 'BOM-123';
1361
        $this->date_creation = dol_now() - 20000;
1362
1363
        return 1;
1364
    }
1365
1366
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1367
1368
    /**
1369
     * Action executed by scheduler
1370
     * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters'
1371
     *
1372
     * @return  int         0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK)
1373
     */
1374
    public function doScheduledJob()
1375
    {
1376
        global $conf, $langs;
1377
1378
        //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1379
1380
        $error = 0;
1381
        $this->output = '';
1382
        $this->error = '';
1383
1384
        dol_syslog(__METHOD__, LOG_DEBUG);
1385
1386
        $now = dol_now();
1387
1388
        $this->db->begin();
1389
1390
        // ...
1391
1392
        $this->db->commit();
1393
1394
        return $error;
1395
    }
1396
1397
    /**
1398
     * Get Net needs by product
1399
     *
1400
     * @param array $TNetNeeds Array of ChildBom and infos linked to
1401
     * @param int   $qty       qty needed
1402
     *
1403
     * @return void
1404
     */
1405
    public function getNetNeeds(&$TNetNeeds = [], $qty = 0)
1406
    {
1407
        if (!empty($this->lines)) {
1408
            foreach ($this->lines as $line) {
1409
                if (!empty($line->childBom)) {
1410
                    foreach ($line->childBom as $childBom) {
1411
                        $childBom->getNetNeeds($TNetNeeds, $line->qty * $qty);
1412
                    }
1413
                } else {
1414
                    if (empty($TNetNeeds[$line->fk_product])) {
1415
                        $TNetNeeds[$line->fk_product] = 0;
1416
                    }
1417
                    $TNetNeeds[$line->fk_product] += $line->qty * $qty;
1418
                }
1419
            }
1420
        }
1421
    }
1422
1423
    /**
1424
     * Get Net needs Tree by product or bom
1425
     *
1426
     * @param array $TNetNeeds Array of ChildBom and infos linked to
1427
     * @param int   $qty       qty needed
1428
     * @param int   $level     level of recursivity
1429
     *
1430
     * @return void
1431
     */
1432
    public function getNetNeedsTree(&$TNetNeeds = [], $qty = 0, $level = 0)
1433
    {
1434
        if (!empty($this->lines)) {
1435
            foreach ($this->lines as $line) {
1436
                if (!empty($line->childBom)) {
1437
                    foreach ($line->childBom as $childBom) {
1438
                        $TNetNeeds[$childBom->id]['bom'] = $childBom;
1439
                        $TNetNeeds[$childBom->id]['parentid'] = $this->id;
1440
                        $TNetNeeds[$childBom->id]['qty'] = $line->qty * $qty;
1441
                        $TNetNeeds[$childBom->id]['level'] = $level;
1442
                        $childBom->getNetNeedsTree($TNetNeeds, $line->qty * $qty, $level + 1);
1443
                    }
1444
                } else {
1445
                    $TNetNeeds[$this->id]['product'][$line->fk_product]['qty'] += $line->qty * $qty;
1446
                    $TNetNeeds[$this->id]['product'][$line->fk_product]['level'] = $level;
1447
                }
1448
            }
1449
        }
1450
    }
1451
1452
    /**
1453
     * Recursively retrieves all parent bom in the tree that leads to the $bom_id bom
1454
     *
1455
     * @param array $TParentBom We put all found parent bom in $TParentBom
1456
     * @param int   $bom_id     ID of bom from which we want to get parent bom ids
1457
     * @param int   $level      Protection against infinite loop
1458
     *
1459
     * @return  void
1460
     */
1461
    public function getParentBomTreeRecursive(&$TParentBom, $bom_id = 0, $level = 1)
1462
    {
1463
1464
        // Protection against infinite loop
1465
        if ($level > 1000) {
1466
            return;
1467
        }
1468
1469
        if (empty($bom_id)) {
1470
            $bom_id = $this->id;
1471
        }
1472
1473
        $sql = 'SELECT l.fk_bom, b.label
1474
				FROM ' . MAIN_DB_PREFIX . 'bom_bomline l
1475
				INNER JOIN ' . MAIN_DB_PREFIX . $this->table_element . ' b ON b.rowid = l.fk_bom
1476
				WHERE fk_bom_child = ' . ((int) $bom_id);
1477
1478
        $resql = $this->db->query($sql);
1479
        if (!empty($resql)) {
1480
            while ($res = $this->db->fetch_object($resql)) {
1481
                $TParentBom[$res->fk_bom] = $res->fk_bom;
1482
                $this->getParentBomTreeRecursive($TParentBom, $res->fk_bom, $level + 1);
1483
            }
1484
        }
1485
    }
1486
1487
    /**
1488
     *  Return clicable link of object (with eventually picto)
1489
     *
1490
     * @param string $option    Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
1491
     * @param array  $arraydata Array of data
1492
     *
1493
     * @return     string                              HTML Code for Kanban thumb.
1494
     */
1495
    public function getKanbanView($option = '', $arraydata = null)
1496
    {
1497
        global $db, $langs;
1498
1499
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1500
1501
        $return = '<div class="box-flex-item box-flex-grow-zero">';
1502
        $return .= '<div class="info-box info-box-sm">';
1503
        $return .= '<span class="info-box-icon bg-infobox-action">';
1504
        $return .= img_picto('', $this->picto);
1505
        $return .= '</span>';
1506
        $return .= '<div class="info-box-content">';
1507
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : '') . '</span>';
1508
        if ($selected >= 0) {
1509
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
1510
        }
1511
        if (property_exists($this, 'fields') && !empty($this->fields['bomtype']['arrayofkeyval'])) {
1512
            $return .= '<br><span class="info-box-label opacitymedium">' . $langs->trans("Type") . ' : </span>';
1513
            if ($this->bomtype == 0) {
1514
                $return .= '<span class="info-box-label">' . $this->fields['bomtype']['arrayofkeyval'][0] . '</span>';
1515
            } else {
1516
                $return .= '<span class="info-box-label">' . $this->fields['bomtype']['arrayofkeyval'][1] . '</span>';
1517
            }
1518
        }
1519
        if (!empty($arraydata['prod'])) {
1520
            $prod = $arraydata['prod'];
1521
            $return .= '<br><span class="info-box-label">' . $prod->getNomUrl(1) . '</span>';
1522
        }
1523
        if (method_exists($this, 'getLibStatut')) {
1524
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
1525
        }
1526
1527
        $return .= '</div>';
1528
        $return .= '</div>';
1529
        $return .= '</div>';
1530
        return $return;
1531
    }
1532
1533
    /**
1534
     *  Return a link to the object card (with optionally the picto)
1535
     *
1536
     * @param int    $withpicto             Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
1537
     * @param string $option                On what the link point to ('nolink', ...)
1538
     * @param int    $notooltip             1=Disable tooltip
1539
     * @param string $morecss               Add more css on link
1540
     * @param int    $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save
1541
     *                                      lastsearch_values whenclicking
1542
     *
1543
     * @return string                              String with URL
1544
     */
1545
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1546
    {
1547
        global $db, $conf, $langs, $hookmanager;
1548
1549
        if (!empty($conf->dol_no_mouse_hover)) {
1550
            $notooltip = 1; // Force disable tooltips
1551
        }
1552
1553
        $result = '';
1554
        $params = [
1555
            'id' => $this->id,
1556
            'objecttype' => $this->element,
1557
            'option' => $option,
1558
        ];
1559
        $classfortooltip = 'classfortooltip';
1560
        $dataparams = '';
1561
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1562
            $classfortooltip = 'classforajaxtooltip';
1563
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
1564
            $label = '';
1565
        } else {
1566
            $label = implode($this->getTooltipContentArray($params));
1567
        }
1568
1569
        $url = DOL_URL_ROOT . '/bom/bom_card.php?id=' . $this->id;
1570
1571
        if ($option != 'nolink') {
1572
            // Add param to save lastsearch_values or not
1573
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1574
            if ($save_lastsearch_value == -1 && isset($_SERVER['PHP_SELF']) && preg_match('/list\.php/', $_SERVER['PHP_SELF'])) {
1575
                $add_save_lastsearch_values = 1;
1576
            }
1577
            if ($add_save_lastsearch_values) {
1578
                $url .= '&save_lastsearch_values=1';
1579
            }
1580
        }
1581
1582
        $linkclose = '';
1583
        if (empty($notooltip)) {
1584
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1585
                $label = $langs->trans("ShowBillOfMaterials");
1586
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1587
            }
1588
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
1589
            $linkclose .= $dataparams . ' class="' . $classfortooltip . ($morecss ? ' ' . $morecss : '') . '"';
1590
        } else {
1591
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1592
        }
1593
1594
        $linkstart = '<a href="' . $url . '"';
1595
        $linkstart .= $linkclose . '>';
1596
        $linkend = '</a>';
1597
1598
        $result .= $linkstart;
1599
        if ($withpicto) {
1600
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1601
        }
1602
        if ($withpicto != 2) {
1603
            $result .= $this->ref;
1604
        }
1605
        $result .= $linkend;
1606
        //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1607
1608
        global $action, $hookmanager;
1609
        $hookmanager->initHooks(['bomdao']);
1610
        $parameters = ['id' => $this->id, 'getnomurl' => &$result];
1611
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1612
        if ($reshook > 0) {
1613
            $result = $hookmanager->resPrint;
1614
        } else {
1615
            $result .= $hookmanager->resPrint;
1616
        }
1617
1618
        return $result;
1619
    }
1620
1621
    /**
1622
     * getTooltipContentArray
1623
     *
1624
     * @param array $params params to construct tooltip data
1625
     *
1626
     * @return array
1627
     * @since v18
1628
     */
1629
    public function getTooltipContentArray($params)
1630
    {
1631
        global $conf, $langs, $user;
1632
1633
        $langs->loadLangs(['product', 'mrp']);
1634
1635
        $datas = [];
1636
1637
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1638
            return ['optimize' => $langs->trans("ShowBillOfMaterials")];
1639
        }
1640
        $picto = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("BillOfMaterials") . '</u>';
1641
        if (isset($this->status)) {
1642
            $picto .= ' ' . $this->getLibStatut(5);
1643
        }
1644
        $datas['picto'] = $picto;
1645
        $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1646
        if (isset($this->label)) {
1647
            $datas['label'] = '<br><b>' . $langs->trans('Label') . ':</b> ' . $this->label;
1648
        }
1649
        if (!empty($this->fk_product) && $this->fk_product > 0) {
1650
            $product = new Product($this->db);
1651
            $resultFetch = $product->fetch($this->fk_product);
1652
            if ($resultFetch > 0) {
1653
                $datas['product'] = "<br><b>" . $langs->trans("Product") . '</b>: ' . $product->ref . ' - ' . $product->label;
1654
            }
1655
        }
1656
1657
        return $datas;
1658
    }
1659
1660
    /**
1661
     *  Return label of the status
1662
     *
1663
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
1664
     *                  label + Picto, 6=Long label + Picto
1665
     *
1666
     * @return string                 Label of status
1667
     */
1668
    public function getLibStatut($mode = 0)
1669
    {
1670
        return $this->LibStatut($this->status, $mode);
1671
    }
1672
1673
    /**
1674
     *  Return the status
1675
     *
1676
     * @param int $status Id status
1677
     * @param int $mode   0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
1678
     *                    label + Picto, 6=Long label + Picto
1679
     *
1680
     * @return string                 Label of status
1681
     */
1682
    public function LibStatut($status, $mode = 0)
1683
    {
1684
        // phpcs:enable
1685
        if (empty($this->labelStatus)) {
1686
            global $langs;
1687
            //$langs->load("mrp");
1688
            $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1689
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1690
            $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
1691
        }
1692
1693
        $statusType = 'status' . $status;
1694
        if ($status == self::STATUS_VALIDATED) {
1695
            $statusType = 'status4';
1696
        }
1697
        if ($status == self::STATUS_CANCELED) {
1698
            $statusType = 'status6';
1699
        }
1700
1701
        return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status], '', $statusType, $mode);
1702
    }
1703
}
1704