Passed
Push — GENERAL_BUG_REVIEW_240911 ( c757bd...0ad96d )
by Rafael
45:56
created

StockTransferLine::getNomUrl()   F

Complexity

Conditions 32
Paths 5184

Size

Total Lines 95
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 32
eloc 61
nc 5184
nop 5
dl 0
loc 95
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2017  Laurent Destailleur <[email protected]>
4
 * Copyright (C) 2021  Gauthier VERDOL <[email protected]>
5
 * Copyright (C) ---Put here your own copyright and developer email---
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
namespace Dolibarr\Code\Product\Classes;
25
26
use Dolibarr\Core\Base\CommonObjectLine;
27
use DoliDB;
28
use MouvementStock;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Dolibarr\Code\Product\Classes\MouvementStock. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
29
use Product;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Dolibarr\Code\Product\Classes\Product. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
30
use Translate;
31
use User;
32
33
/**
34
 * \file        class/stocktransferline.class.php
35
 * \ingroup     stocktransfer
36
 * \brief       This file is a CRUD class file for StockTransferLine (Create/Read/Update/Delete)
37
 */
38
39
40
//use Dolibarr\Code\Societe\Classes\Societe;
41
//
42
43
/**
44
 * Class for StockTransferLine
45
 */
46
class StockTransferLine extends CommonObjectLine
47
{
48
    /**
49
     * @var string ID to identify managed object.
50
     */
51
    public $element = 'stocktransferline';
52
53
    /**
54
     * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management.
55
     */
56
    public $table_element = 'stocktransfer_stocktransferline';
57
58
    /**
59
     * @var string String with name of icon for stocktransferline. Must be the part after the 'object_' into object_stocktransferline.png
60
     */
61
    public $picto = 'stocktransferline@stocktransfer';
62
63
    const STATUS_DRAFT = 0;
64
    const STATUS_VALIDATED = 1;
65
    const STATUS_CANCELED = 9;
66
67
68
    /**
69
     *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
70
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
71
     *  'label' the translation key.
72
     *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalString("MY_SETUP_PARAM")'
73
     *  'position' is the sort order of field.
74
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
75
     *  '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)
76
     *  'noteditable' says if field is not editable (1 or 0)
77
     *  '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.
78
     *  'index' if we want an index in database.
79
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
80
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
81
     *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
82
     *  'css' is the CSS style to use on field. For example: 'maxwidth200'
83
     *  'help' is a string visible as a tooltip on field
84
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
85
     *  '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.
86
     *  'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
87
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
88
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
89
     *
90
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
91
     */
92
93
    // BEGIN MODULEBUILDER PROPERTIES
94
    /**
95
     * @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...
96
     */
97
    public $fields = array(
98
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'comment' => "Id"),
99
        'amount' => array('type' => 'price', 'label' => 'Amount', 'enabled' => 1, 'position' => 40, 'notnull' => 0, 'visible' => 1, 'default' => 'null', 'isameasure' => 1, 'help' => "Help text for amount",),
100
        'qty' => array('type' => 'real', 'label' => 'Qty', 'enabled' => 1, 'position' => 45, 'notnull' => 0, 'visible' => 1, 'default' => '0', 'isameasure' => 1, 'css' => 'maxwidth75imp', 'help' => "Help text for quantity",),
101
        'fk_warehouse_destination' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Entrepôt de destination', 'enabled' => 1, 'position' => 50, 'notnull' => 1, 'visible' => 1,),
102
        'fk_warehouse_source' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Entrepôt source', 'enabled' => 1, 'position' => 50, 'notnull' => 1, 'visible' => 1,),
103
        'fk_stocktransfer' => array('type' => 'integer:StockTransfer:stocktransfer/stock/class/stocktransfer.class.php', 'label' => 'StockTransfer', 'enabled' => 1, 'position' => 50, 'notnull' => 1, 'visible' => 0,),
104
        'fk_product' => array('type' => 'integer:Product:product/class/product.class.php', 'label' => 'Product', 'enabled' => 1, 'position' => 50, 'notnull' => 1, 'visible' => 1,),
105
        'batch' => array('type' => 'varchar(128)', 'label' => 'Batch', 'enabled' => 1, 'position' => 1000, 'notnull' => -1, 'visible' => 1,),
106
        'pmp' => array('type' => 'double'/*, 'help'=>'THMEstimatedHelp'*/, 'label' => 'PMP', 'enabled' => 1, 'position' => 50, 'notnull' => 0, 'visible' => 1,),
107
        'rang' => array('type' => 'integer', 'label' => 'Qty', 'enabled' => 1, 'position' => 45, 'notnull' => 0, 'visible' => 0, 'default' => '0', 'isameasure' => 1, 'css' => 'maxwidth75imp', 'help' => "Help text for quantity",),
108
    );
109
    public $rowid;
110
    public $amount;
111
112
    /**
113
     * @var float Quantity
114
     */
115
    public $qty;
116
    public $fk_warehouse_destination;
117
    public $fk_warehouse_source;
118
    public $fk_stocktransfer;
119
    public $fk_product;
120
    public $batch;
121
122
    /**
123
     * @var double pmp
124
     */
125
    public $pmp;
126
127
    // END MODULEBUILDER PROPERTIES
128
129
130
    /**
131
     * Constructor
132
     *
133
     * @param DoliDB $db Database handler
134
     */
135
    public function __construct(DoliDB $db)
136
    {
137
        global $conf, $langs;
138
139
        $this->db = $db;
140
141
        $this->ismultientitymanaged = 0;
142
        $this->isextrafieldmanaged = 1;
143
144
        if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
145
            $this->fields['rowid']['visible'] = 0;
146
        }
147
        if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
148
            $this->fields['entity']['enabled'] = 0;
149
        }
150
151
        // Example to show how to set values of fields definition dynamically
152
        /*if ($user->rights->stocktransfer->stocktransferline->read) {
153
            $this->fields['myfield']['visible'] = 1;
154
            $this->fields['myfield']['noteditable'] = 0;
155
        }*/
156
157
        // Unset fields that are disabled
158
        foreach ($this->fields as $key => $val) {
159
            if (isset($val['enabled']) && empty($val['enabled'])) {
160
                unset($this->fields[$key]);
161
            }
162
        }
163
164
        // Translate some data of arrayofkeyval
165
        if (is_object($langs)) {
166
            foreach ($this->fields as $key => $val) {
167
                if (isset($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
168
                    foreach ($val['arrayofkeyval'] as $key2 => $val2) {
169
                        $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
170
                    }
171
                }
172
            }
173
        }
174
    }
175
176
    /**
177
     * Create object into database
178
     *
179
     * @param User $user User that creates
180
     * @param int $notrigger 0=launch triggers after, 1=disable triggers
181
     * @return int             Return integer <0 if KO, Id of created object if OK
182
     */
183
    public function create(User $user, $notrigger = 0)
184
    {
185
        return $this->createCommon($user, $notrigger);
186
    }
187
188
    /**
189
     * Clone an object into another one
190
     *
191
     * @param User $user User that creates
192
     * @param int $fromid Id of object to clone
193
     * @return  mixed               New object created, <0 if KO
194
     */
195
    public function createFromClone(User $user, $fromid)
196
    {
197
        global $langs, $extrafields;
198
        $error = 0;
199
200
        dol_syslog(__METHOD__, LOG_DEBUG);
201
202
        $object = new self($this->db);
203
204
        $this->db->begin();
205
206
        // Load source object
207
        $result = $object->fetchCommon($fromid);
208
        if ($result > 0 && !empty($object->table_element_line)) {
209
            $object->fetchLines();
210
        }
211
212
        // get lines so they will be clone
213
        //foreach($this->lines as $line)
214
        //  $line->fetch_optionals();
215
216
        // Reset some properties
217
        unset($object->id);
218
        unset($object->import_key);
219
220
221
        // Clear fields
222
        $object->ref = empty($this->fields['ref']['default']) ? "copy_of_" . $object->ref : $this->fields['ref']['default'];
223
        $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf") . " " . $object->label : $this->fields['label']['default'];
224
        $object->status = self::STATUS_DRAFT;
225
        // ...
226
        // Clear extrafields that are unique
227
        if (is_array($object->array_options) && count($object->array_options) > 0) {
228
            $extrafields->fetch_name_optionals_label($this->table_element);
229
            foreach ($object->array_options as $key => $option) {
230
                $shortkey = preg_replace('/options_/', '', $key);
231
                if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
232
                    //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
233
                    unset($object->array_options[$key]);
234
                }
235
            }
236
        }
237
238
        // Create clone
239
        $object->context['createfromclone'] = 'createfromclone';
240
        $result = $object->createCommon($user);
241
        if ($result < 0) {
242
            $error++;
243
            $this->error = $object->error;
244
            $this->errors = $object->errors;
245
        }
246
247
        if (!$error) {
248
            // copy internal contacts
249
            if ($this->copy_linked_contact($object, 'internal') < 0) {
250
                $error++;
251
            }
252
        }
253
254
        if (!$error) {
255
            // copy external contacts if same company
256
            if (property_exists($this, 'socid') && $this->socid == $object->socid) {
257
                if ($this->copy_linked_contact($object, 'external') < 0) {
258
                    $error++;
259
                }
260
            }
261
        }
262
263
        unset($object->context['createfromclone']);
264
265
        // End
266
        if (!$error) {
267
            $this->db->commit();
268
            return $object;
269
        } else {
270
            $this->db->rollback();
271
            return -1;
272
        }
273
    }
274
275
    /**
276
     * Load object in memory from the database
277
     *
278
     * @param int $id Id object
279
     * @param string $ref Ref
280
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
281
     */
282
    public function fetch($id, $ref = null)
283
    {
284
        $result = $this->fetchCommon($id, $ref);
285
        if ($result > 0 && !empty($this->table_element_line)) {
286
            $this->fetchLines();
287
        }
288
        return $result;
289
    }
290
291
    /**
292
     * Load object lines in memory from the database
293
     *
294
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
295
     */
296
    public function fetchLines()
297
    {
298
        $this->lines = array();
299
300
        $result = $this->fetchLinesCommon();
301
        return $result;
302
    }
303
304
305
    /**
306
     * Load list of objects in memory from the database.
307
     *
308
     * @param string $sortorder Sort Order
309
     * @param string $sortfield Sort field
310
     * @param int $limit limit
311
     * @param int $offset Offset
312
     * @param string $filter Filter as an Universal Search string.
313
     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')'
314
     * @param string $filtermode No more used
315
     * @return array|int                    int <0 if KO, array of pages if OK
316
     */
317
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
318
    {
319
        dol_syslog(__METHOD__, LOG_DEBUG);
320
321
        $records = array();
322
323
        $sql = 'SELECT ';
324
        $sql .= $this->getFieldList();
325
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
326
        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
327
            $sql .= ' WHERE t.entity IN (' . getEntity($this->element) . ')';
328
        } else {
329
            $sql .= ' WHERE 1 = 1';
330
        }
331
332
        // Manage filter
333
        $errormessage = '';
334
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
335
        if ($errormessage) {
336
            $this->errors[] = $errormessage;
337
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
338
            return -1;
339
        }
340
341
        if (!empty($sortfield)) {
342
            $sql .= $this->db->order($sortfield, $sortorder);
343
        }
344
        if (!empty($limit)) {
345
            $sql .= ' ' . $this->db->plimit($limit, $offset);
346
        }
347
348
        $resql = $this->db->query($sql);
349
        if ($resql) {
350
            $num = $this->db->num_rows($resql);
351
            $i = 0;
352
            while ($i < ($limit ? min($limit, $num) : $num)) {
353
                $obj = $this->db->fetch_object($resql);
354
355
                $record = new self($this->db);
356
                $record->setVarsFromFetchObj($obj);
357
358
                $records[$record->id] = $record;
359
360
                $i++;
361
            }
362
            $this->db->free($resql);
363
364
            return $records;
365
        } else {
366
            $this->errors[] = 'Error ' . $this->db->lasterror();
367
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
368
369
            return -1;
370
        }
371
    }
372
373
    /**
374
     * Update object into database
375
     *
376
     * @param User $user User that modifies
377
     * @param int $notrigger 0=launch triggers after, 1=disable triggers
378
     * @return int             Return integer <0 if KO, >0 if OK
379
     */
380
    public function update(User $user, $notrigger = 0)
381
    {
382
        return $this->updateCommon($user, $notrigger);
383
    }
384
385
    /**
386
     * Delete object in database
387
     *
388
     * @param User $user User that deletes
389
     * @param int $notrigger 0=launch triggers after, 1=disable triggers
390
     * @return int              Return integer <0 if KO, >0 if OK
391
     */
392
    public function delete(User $user, $notrigger = 0)
393
    {
394
        return $this->deleteCommon($user, $notrigger);
395
        //return $this->deleteCommon($user, $notrigger, 1);
396
    }
397
398
    /**
399
     *  Delete a line of object in database
400
     *
401
     * @param User $user User that delete
402
     * @param int $idline Id of line to delete
403
     * @param int $notrigger 0=launch triggers after, 1=disable triggers
404
     * @return int                 Return >0 if OK, <0 if KO
405
     */
406
    public function deleteLine(User $user, $idline, $notrigger = 0)
407
    {
408
        if ($this->status < 0) {
409
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
410
            return -2;
411
        }
412
413
        return $this->deleteLineCommon($user, $idline, $notrigger);
414
    }
415
416
    /**
417
     * Makes all stock movements (add quantity, remove quantity or cancel all actions)
418
     *
419
     * @param string $label label of stock movement
420
     * @param string $code_inv label of stock movement
421
     * @param int $fk_entrepot Warehouse concerned by stock movement
422
     * @param int $direction add or remove qty
423
     * @return int   1 if ok, <= 0 if ko
424
     */
425
    public function doStockMovement($label, $code_inv, $fk_entrepot, $direction = 1)
426
    {
427
        global $user, $langs;
428
429
        include_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php';
430
        include_once DOL_DOCUMENT_ROOT . '/product/stock/stocktransfer/class/stocktransfer.class.php';
431
432
        $p = new Product($this->db);
433
        $p->fetch($this->fk_product);
434
435
        $op = array();
436
        $op[0] = "+" . trim((string)$this->qty);
437
        $op[1] = "-" . trim((string)$this->qty);
438
        $movementstock = new MouvementStock($this->db);
439
        $st = new StockTransfer($this->db);
440
        $movementstock->origin_type = $st->origin_type;
441
        $movementstock->origin_id = $this->fk_stocktransfer;
442
443
        if (empty($this->batch)) { // no batch for line
444
            $result = $movementstock->_create(
445
                $user,
446
                $p->id,
447
                $fk_entrepot,
448
                $op[$direction],
449
                $direction,
450
                empty($direction) ? $this->pmp : 0,
451
                $label,
452
                $code_inv
453
            );
454
455
            if ($result < 0) {
456
                $this->setErrorsFromObject($movementstock);
457
                return -1;
458
            }
459
        } else {
460
            if ($p->hasbatch()) {
461
                $arraybatchinfo = $p->loadBatchInfo($this->batch);
462
                if (count($arraybatchinfo) > 0) {
463
                    $firstrecord = array_shift($arraybatchinfo);
464
                    $dlc = $firstrecord['eatby'];
465
                    $dluo = $firstrecord['sellby'];
466
                } else {
467
                    $dlc = '';
468
                    $dluo = '';
469
                }
470
471
                $result = $movementstock->_create(
472
                    $user,
473
                    $p->id,
474
                    $fk_entrepot,
475
                    $op[$direction],
476
                    $direction,
477
                    empty($direction) ? $this->pmp : 0,
478
                    $label,
479
                    $code_inv,
480
                    '',
481
                    $dlc,
482
                    $dluo,
483
                    $this->batch
484
                );
485
486
                if ($result < 0) {
487
                    $this->setErrorsFromObject($movementstock);
488
                    return $result;
489
                }
490
            } else {
491
                $this->error = $langs->trans('StockTransferNoBatchForProduct', $p->getNomUrl());
492
                $this->errors[] = $this->error;
493
                return -1;
494
            }
495
        }
496
497
        return 1;
498
    }
499
500
    /**
501
     *  Validate object
502
     *
503
     * @param User $user User making status change
504
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
505
     * @return     int                     Return integer <=0 if OK, 0=Nothing done, >0 if KO
506
     */
507
    public function validate($user, $notrigger = 0)
508
    {
509
        global $conf, $langs;
510
511
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
512
513
        $error = 0;
514
515
        // Protection
516
        if ($this->status == self::STATUS_VALIDATED) {
517
            dol_syslog(get_class($this) . "::validate action abandoned: already validated", LOG_WARNING);
518
            return 0;
519
        }
520
521
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransferline->write))
522
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransferline->stocktransferline_advance->validate))))
523
         {
524
         $this->error='NotEnoughPermissions';
525
         dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
526
         return -1;
527
         }*/
528
529
        $now = dol_now();
530
531
        $this->db->begin();
532
533
        // Define new ref
534
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
535
            $num = $this->getNextNumRef();
536
        } else {
537
            $num = $this->ref;
538
        }
539
        $this->newref = $num;
540
541
        if (!empty($num)) {
542
            // Validate
543
            $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
544
            $sql .= " SET ref = '" . $this->db->escape($num) . "',";
545
            $sql .= " status = " . self::STATUS_VALIDATED;
546
            if (!empty($this->fields['date_validation'])) {
547
                $sql .= ", date_validation = '" . $this->db->idate($now) . "',";
548
            }
549
            if (!empty($this->fields['fk_user_valid'])) {
550
                $sql .= ", fk_user_valid = " . ((int)$user->id);
551
            }
552
            $sql .= " WHERE rowid = " . ((int)$this->id);
553
554
            dol_syslog(get_class($this) . "::validate()", LOG_DEBUG);
555
            $resql = $this->db->query($sql);
556
            if (!$resql) {
557
                dol_print_error($this->db);
558
                $this->error = $this->db->lasterror();
559
                $error++;
560
            }
561
562
            if (!$error && !$notrigger) {
563
                // Call trigger
564
                $result = $this->call_trigger('STOCKTRANSFERLINE_VALIDATE', $user);
565
                if ($result < 0) {
566
                    $error++;
567
                }
568
                // End call triggers
569
            }
570
        }
571
572
        if (!$error) {
573
            $this->oldref = $this->ref;
574
575
            // Rename directory if dir was a temporary ref
576
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
577
                // Now we rename also files into index
578
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'stocktransferline/" . $this->db->escape($this->newref) . "'";
579
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'stocktransferline/" . $this->db->escape($this->ref) . "' and entity = " . ((int)$conf->entity);
580
                $resql = $this->db->query($sql);
581
                if (!$resql) {
582
                    $error++;
583
                    $this->error = $this->db->lasterror();
584
                }
585
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'stocktransferline/" . $this->db->escape($this->newref) . "'";
586
                $sql .= " WHERE filepath = 'stocktransferline/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
587
                $resql = $this->db->query($sql);
588
                if (!$resql) {
589
                    $error++;
590
                    $this->error = $this->db->lasterror();
591
                }
592
593
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
594
                $oldref = dol_sanitizeFileName($this->ref);
595
                $newref = dol_sanitizeFileName($num);
596
                $dirsource = $conf->stocktransfer->dir_output . '/stocktransferline/' . $oldref;
597
                $dirdest = $conf->stocktransfer->dir_output . '/stocktransferline/' . $newref;
598
                if (!$error && file_exists($dirsource)) {
599
                    dol_syslog(get_class($this) . "::validate() rename dir " . $dirsource . " into " . $dirdest);
600
601
                    if (@rename($dirsource, $dirdest)) {
602
                        dol_syslog("Rename ok");
603
                        // Rename docs starting with $oldref with $newref
604
                        $listoffiles = dol_dir_list($conf->stocktransfer->dir_output . '/stocktransferline/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
605
                        foreach ($listoffiles as $fileentry) {
606
                            $dirsource = $fileentry['name'];
607
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
608
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
609
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
610
                            @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

610
                            /** @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...
611
                        }
612
                    }
613
                }
614
            }
615
        }
616
617
        // Set new ref and current status
618
        if (!$error) {
619
            $this->ref = $num;
620
            $this->status = self::STATUS_VALIDATED;
621
        }
622
623
        if (!$error) {
624
            $this->db->commit();
625
            return 1;
626
        } else {
627
            $this->db->rollback();
628
            return -1;
629
        }
630
    }
631
632
633
    /**
634
     *  Set draft status
635
     *
636
     * @param User $user Object user that modify
637
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
638
     * @return int                     Return integer <0 if KO, >0 if OK
639
     */
640
    public function setDraft($user, $notrigger = 0)
641
    {
642
        // Protection
643
        if ($this->status <= self::STATUS_DRAFT) {
644
            return 0;
645
        }
646
647
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->write))
648
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransfer_advance->validate))))
649
         {
650
         $this->error='Permission denied';
651
         return -1;
652
         }*/
653
654
        return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'STOCKTRANSFERLINE_UNVALIDATE');
655
    }
656
657
    /**
658
     *  Set cancel status
659
     *
660
     * @param User $user Object user that modify
661
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
662
     * @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
663
     */
664
    public function cancel($user, $notrigger = 0)
665
    {
666
        // Protection
667
        if ($this->status != self::STATUS_VALIDATED) {
668
            return 0;
669
        }
670
671
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->write))
672
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransfer_advance->validate))))
673
         {
674
         $this->error='Permission denied';
675
         return -1;
676
         }*/
677
678
        return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'STOCKTRANSFERLINE_CLOSE');
679
    }
680
681
    /**
682
     *  Set back to validated status
683
     *
684
     * @param User $user Object user that modify
685
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
686
     * @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
687
     */
688
    public function reopen($user, $notrigger = 0)
689
    {
690
        // Protection
691
        if ($this->status != self::STATUS_CANCELED) {
692
            return 0;
693
        }
694
695
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->write))
696
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransfer_advance->validate))))
697
         {
698
         $this->error='Permission denied';
699
         return -1;
700
         }*/
701
702
        return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'STOCKTRANSFERLINE_REOPEN');
703
    }
704
705
    /**
706
     *  Return a link to the object card (with optionally the picto)
707
     *
708
     * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
709
     * @param string $option On what the link point to ('nolink', ...)
710
     * @param int $notooltip 1=Disable tooltip
711
     * @param string $morecss Add more css on link
712
     * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
713
     * @return string                              String with URL
714
     */
715
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
716
    {
717
        global $conf, $langs, $hookmanager;
718
719
        if (!empty($conf->dol_no_mouse_hover)) {
720
            $notooltip = 1;
721
        } // Force disable tooltips
722
723
        $result = '';
724
725
        $label = '<u>' . $langs->trans("StockTransferLine") . '</u>';
726
        $label .= '<br>';
727
        $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
728
        if (isset($this->status)) {
729
            $label .= '<br><b>' . $langs->trans("Status") . ":</b> " . $this->getLibStatut(5);
730
        }
731
732
        $url = dol_buildpath('/stocktransfer/stocktransferline_card.php', 1) . '?id=' . $this->id;
733
734
        if ($option != 'nolink') {
735
            // Add param to save lastsearch_values or not
736
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
737
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
738
                $add_save_lastsearch_values = 1;
739
            }
740
            if ($add_save_lastsearch_values) {
741
                $url .= '&save_lastsearch_values=1';
742
            }
743
        }
744
745
        $linkclose = '';
746
        if (empty($notooltip)) {
747
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
748
                $label = $langs->trans("ShowStockTransferLine");
749
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
750
            }
751
            $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
752
            $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
753
        } else {
754
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
755
        }
756
757
        $linkstart = '<a href="' . $url . '"';
758
        $linkstart .= $linkclose . '>';
759
        $linkend = '</a>';
760
761
        $result .= $linkstart;
762
763
        if (empty($this->showphoto_on_popup)) {
764
            if ($withpicto) {
765
                $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
766
            }
767
        } else {
768
            if ($withpicto) {
769
                require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
770
771
                list($class, $module) = explode('@', $this->picto);
772
                $upload_dir = $conf->$module->multidir_output[$conf->entity] . "/$class/" . dol_sanitizeFileName($this->ref);
773
                $filearray = dol_dir_list($upload_dir, "files");
774
                $filename = $filearray[0]['name'];
775
                if (!empty($filename)) {
776
                    $pospoint = strpos($filearray[0]['name'], '.');
777
778
                    $pathtophoto = $class . '/' . $this->ref . '/thumbs/' . substr($filename, 0, $pospoint) . '_mini' . substr($filename, $pospoint);
779
                    if (!getDolGlobalString(strtoupper($module . '_' . $class) . '_FORMATLISTPHOTOSASUSERS')) {
780
                        $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo' . $module . '" alt="No photo" border="0" src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div></div>';
781
                    } else {
782
                        $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div>';
783
                    }
784
785
                    $result .= '</div>';
786
                } else {
787
                    $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
788
                }
789
            }
790
        }
791
792
        if ($withpicto != 2) {
793
            $result .= $this->ref;
794
        }
795
796
        $result .= $linkend;
797
        //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
798
799
        global $action, $hookmanager;
800
        $hookmanager->initHooks(array('stocktransferlinedao'));
801
        $parameters = array('id' => $this->id, 'getnomurl' => $result);
802
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
803
        if ($reshook > 0) {
804
            $result = $hookmanager->resPrint;
805
        } else {
806
            $result .= $hookmanager->resPrint;
807
        }
808
809
        return $result;
810
    }
811
812
    /**
813
     *  Return label of the status
814
     *
815
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
816
     * @return string                 Label of status
817
     */
818
    public function getLibStatut($mode = 0)
819
    {
820
        return $this->LibStatut($this->status, $mode);
821
    }
822
823
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
824
825
    /**
826
     *  Return the status
827
     *
828
     * @param int $status Id status
829
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
830
     * @return string                 Label of status
831
     */
832
    public function LibStatut($status, $mode = 0)
833
    {
834
        // phpcs:enable
835
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
836
            global $langs;
837
            //$langs->load("stocktransfer@stocktransfer");
838
            $this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft');
839
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Enabled');
840
            $this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Disabled');
841
            $this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft');
842
            $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Enabled');
843
            $this->labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Disabled');
844
        }
845
846
        $statusType = 'status' . $status;
847
        //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
848
        if ($status == self::STATUS_CANCELED) {
849
            $statusType = 'status6';
850
        }
851
852
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
853
    }
854
855
    /**
856
     *  Load the info information in the object
857
     *
858
     * @param int $id Id of object
859
     * @return void
860
     */
861
    public function info($id)
862
    {
863
        $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
864
        $sql .= ' fk_user_creat, fk_user_modif';
865
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
866
        $sql .= ' WHERE t.rowid = ' . ((int)$id);
867
        $result = $this->db->query($sql);
868
        if ($result) {
869
            if ($this->db->num_rows($result)) {
870
                $obj = $this->db->fetch_object($result);
871
                $this->id = $obj->rowid;
872
873
                $this->user_creation_id = $obj->fk_user_creat;
874
                $this->user_modification_id = $obj->fk_user_modif;
875
                $this->date_creation = $this->db->jdate($obj->datec);
876
                $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
877
            }
878
879
            $this->db->free($result);
880
        } else {
881
            dol_print_error($this->db);
882
        }
883
    }
884
885
    /**
886
     * Initialise object with example values
887
     * Id must be 0 if object instance is a specimen
888
     *
889
     * @return int
890
     */
891
    public function initAsSpecimen()
892
    {
893
        return $this->initAsSpecimenCommon();
894
    }
895
896
    /**
897
     *  Returns the reference to the following non used object depending on the active numbering module.
898
     *
899
     * @return string              Object free reference
900
     */
901
    public function getNextNumRef()
902
    {
903
        global $langs, $conf;
904
        $langs->load("stocks");
905
906
        if (!getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON')) {
907
            $conf->global->STOCKTRANSFER_STOCKTRANSFERLINE_ADDON = 'mod_stocktransferline_standard';
908
        }
909
910
        if (getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON')) {
911
            $mybool = false;
912
913
            $file = getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON') . ".php";
914
            $classname = getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON');
915
916
            // Include file with class
917
            $dirmodels = array_merge(array('/'), (array)$conf->modules_parts['models']);
918
            foreach ($dirmodels as $reldir) {
919
                $dir = dol_buildpath($reldir . "core/modules/stocktransfer/");
920
921
                // Load file with numbering class (if found)
922
                $mybool = ((bool)@include_once $dir . $file) || $mybool;
923
            }
924
925
            if ($mybool === false) {
926
                dol_print_error(null, "Failed to include file " . $file);
927
                return '';
928
            }
929
930
            if (class_exists($classname)) {
931
                $obj = new $classname();
932
                $numref = $obj->getNextValue($this);
933
934
                if ($numref != '' && $numref != '-1') {
935
                    return $numref;
936
                } else {
937
                    $this->error = $obj->error;
938
                    //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
939
                    return "";
940
                }
941
            } else {
942
                print $langs->trans("Error") . " " . $langs->trans("ClassNotFound") . ' ' . $classname;
943
                return "";
944
            }
945
        } else {
946
            print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
947
            return "";
948
        }
949
    }
950
951
    /**
952
     *  Create a document onto disk according to template module.
953
     *
954
     * @param string $modele Force template to use ('' to not force)
955
     * @param Translate $outputlangs object lang a utiliser pour traduction
956
     * @param int $hidedetails Hide details of lines
957
     * @param int $hidedesc Hide description
958
     * @param int $hideref Hide ref
959
     * @param null|array $moreparams Array to provide more information
960
     * @return     int                         0 if KO, 1 if OK
961
     */
962
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
963
    {
964
        global $conf, $langs;
965
966
        $result = 0;
967
        $includedocgeneration = 0;
968
969
        $langs->load("stocks");
970
971
        if (!dol_strlen($modele)) {
972
            $modele = 'standard_stocktransferline';
973
974
            if (!empty($this->model_pdf)) {
975
                $modele = $this->model_pdf;
976
            } elseif (getDolGlobalString('STOCKTRANSFERLINE_ADDON_PDF')) {
977
                $modele = getDolGlobalString('STOCKTRANSFERLINE_ADDON_PDF');
978
            }
979
        }
980
981
        $modelpath = "core/modules/stocktransfer/doc/";
982
983
        if ($includedocgeneration) {
984
            $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
985
        }
986
987
        return $result;
988
    }
989
990
    /**
991
     * Action executed by scheduler
992
     * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters'
993
     * Use public function doScheduledJob($param1, $param2, ...) to get parameters
994
     *
995
     * @return  int         0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK)
996
     */
997
    public function doScheduledJob()
998
    {
999
        //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1000
1001
        $error = 0;
1002
        $this->output = '';
1003
        $this->error = '';
1004
1005
        dol_syslog(__METHOD__, LOG_DEBUG);
1006
1007
        $now = dol_now();
1008
1009
        $this->db->begin();
1010
1011
        // ...
1012
1013
        $this->db->commit();
1014
1015
        return $error;
1016
    }
1017
}
1018