Passed
Push — EXTRACT_CLASSES ( d4f850...56e940 )
by Rafael
41:39
created

Reception   F

Complexity

Total Complexity 339

Size/Duplication

Total Lines 2071
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1155
dl 0
loc 2071
rs 0.98
c 0
b 0
f 0
wmc 339

26 Methods

Rating   Name   Duplication   Size   Complexity  
A getLibStatut() 0 3 1
D setDraft() 0 140 27
F create() 0 141 33
A list_delivery_methods() 0 25 5
C reOpen() 0 116 17
D update() 0 124 48
D delete() 0 128 23
A generateDocument() 0 21 4
B getKanbanView() 0 28 8
B getNomUrl() 0 49 10
B fetch_lines() 0 72 8
A LibStatut() 0 36 5
F valid() 0 222 42
A replaceThirdparty() 0 5 1
D getStatusDispatch() 0 87 26
A setBilled() 0 36 5
B getNextNumRef() 0 40 6
C fetch() 0 116 12
A setDeliveryDate() 0 19 4
A initAsSpecimen() 0 54 2
B getUrlTrackingStatus() 0 20 7
A fetch_delivery_methods() 0 16 4
D setClosed() 0 135 22
C addline() 0 80 17
A __construct() 0 5 1
A replaceProduct() 0 7 1

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/* Copyright (C) 2003-2008  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2005-2012	Regis Houssin			    <[email protected]>
5
 * Copyright (C) 2007		Franky Van Liedekerke	    <[email protected]>
6
 * Copyright (C) 2006-2012	Laurent Destailleur		    <[email protected]>
7
 * Copyright (C) 2011-2017	Juanjo Menent			    <[email protected]>
8
 * Copyright (C) 2013       Florian Henry		  	    <[email protected]>
9
 * Copyright (C) 2014		Cedric GROSS			    <[email protected]>
10
 * Copyright (C) 2014-2015  Marcos García               <[email protected]>
11
 * Copyright (C) 2014-2020  Francis Appels              <[email protected]>
12
 * Copyright (C) 2015       Claudio Aschieri            <[email protected]>
13
 * Copyright (C) 2016-2022	Ferran Marcet			    <[email protected]>
14
 * Copyright (C) 2018		Quentin Vial-Gouteyron      <[email protected]>
15
 * Copyright (C) 2022-2024  Frédéric France             <[email protected]>
16
 * Copyright (C) 2024		MDW							<[email protected]>
17
 * Copyright (C) 2024       Rafael San José             <[email protected]>
18
 *
19
 * This program is free software; you can redistribute it and/or modify
20
 * it under the terms of the GNU General Public License as published by
21
 * the Free Software Foundation; either version 3 of the License, or
22
 * (at your option) any later version.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
 * GNU General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU General Public License
30
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
31
 */
32
33
namespace Dolibarr\Code\Reception\Classes;
34
35
use Dolibarr\Code\Core\Traits\CommonIncoterm;
36
use Dolibarr\Core\Base\CommonObject;
37
38
/**
39
 *  \file       htdocs/reception/class/reception.class.php
40
 *  \ingroup    reception
41
 *  \brief      File for class to manage receptions
42
 */
43
44
/**
45
 *  Class to manage receptions
46
 */
47
class Reception extends CommonObject
48
{
49
    use CommonIncoterm;
50
51
    /**
52
     * @var string code
53
     */
54
    public $code = "";
55
56
    /**
57
     * @var string element name
58
     */
59
    public $element = "reception";
60
61
    /**
62
     * @var string Fieldname with ID of parent key if this field has a parent
63
     */
64
    public $fk_element = "fk_reception";
65
    public $table_element = "reception";
66
    public $table_element_line = "receptiondet_batch";
67
68
    /**
69
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
70
     */
71
    public $picto = 'dollyrevert';
72
73
    public $socid;
74
    public $ref_supplier;
75
76
    public $entrepot_id;
77
    public $tracking_number;
78
    public $tracking_url;
79
    public $billed;
80
    public $model_pdf;
81
82
    public $weight;
83
    public $trueWeight;
84
    public $weight_units;
85
    public $trueWidth;
86
    public $width_units;
87
    public $trueHeight;
88
    public $height_units;
89
    public $trueDepth;
90
    public $depth_units;
91
    // A denormalized value
92
    public $trueSize;
93
    public $size_units;
94
    public $user_author_id;
95
96
    public $date_delivery; // Date delivery planned
97
98
    /**
99
     * @var integer|string Effective delivery date
100
     * @deprecated
101
     * @see $date_reception
102
     */
103
    public $date;
104
105
    /**
106
     * @var integer|string Effective delivery date
107
     */
108
    public $date_reception;
109
110
    /**
111
     * @var integer|string date_creation
112
     */
113
    public $date_creation;
114
115
    /**
116
     * @var integer|string date_validation
117
     */
118
    public $date_valid;
119
120
    public $meths;
121
    public $listmeths; // List of carriers
122
123
    /**
124
     * @var ReceptionLineBatch[]|CommandeFournisseurDispatch[]
125
     */
126
    public $lines = array();
127
128
129
    // detail of lot and qty = array(id in receptiondet_batch, batch, qty)
130
    // We can use this to know warehouse planned to be used for each lot.
131
    public $detail_batch;
132
133
    const STATUS_DRAFT = 0;
134
    const STATUS_VALIDATED = 1;
135
    const STATUS_CLOSED = 2;
136
137
138
139
    /**
140
     *  Constructor
141
     *
142
     *  @param      DoliDB      $db      Database handler
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Reception\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
143
     */
144
    public function __construct($db)
145
    {
146
        $this->db = $db;
147
148
        $this->ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
149
    }
150
151
    /**
152
     *  Return next contract ref
153
     *
154
     *  @param  Societe     $soc    Thirdparty object
155
     *  @return string              Free reference for contract
156
     */
157
    public function getNextNumRef($soc)
158
    {
159
        global $langs, $conf;
160
        $langs->load("receptions");
161
162
        if (getDolGlobalString('RECEPTION_ADDON_NUMBER')) {
163
            $mybool = false;
164
165
            $file = getDolGlobalString('RECEPTION_ADDON_NUMBER') . ".php";
166
            $classname = getDolGlobalString('RECEPTION_ADDON_NUMBER');
167
168
            // Include file with class
169
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
170
171
            foreach ($dirmodels as $reldir) {
172
                $dir = dol_buildpath($reldir . "core/modules/reception/");
173
174
                // Load file with numbering class (if found)
175
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
176
            }
177
178
            if (!$mybool) {
179
                dol_print_error(null, "Failed to include file " . $file);
180
                return '';
181
            }
182
183
            $obj = new $classname();
184
185
            $numref = "";
186
            $numref = $obj->getNextValue($soc, $this);
187
188
            if ($numref != "") {
189
                return $numref;
190
            } else {
191
                dol_print_error($this->db, get_class($this) . "::getNextNumRef " . $obj->error);
192
                return "";
193
            }
194
        } else {
195
            print $langs->trans("Error") . " " . $langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
196
            return "";
197
        }
198
    }
199
200
    /**
201
     *  Create reception en base
202
     *
203
     *  @param  User    $user       Object du user qui cree
204
     *  @param  int     $notrigger  1=Does not execute triggers, 0= execute triggers
205
     *  @return int                 Return integer <0 si erreur, id reception creee si ok
206
     */
207
    public function create($user, $notrigger = 0)
208
    {
209
        global $conf;
210
211
        $now = dol_now();
212
213
        require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
214
        $error = 0;
215
216
        // Clean parameters
217
        $this->tracking_number = dol_sanitizeFileName($this->tracking_number);
218
        if (empty($this->fk_project)) {
219
            $this->fk_project = 0;
220
        }
221
        if (empty($this->weight_units)) {
222
            $this->weight_units = 0;
223
        }
224
        if (empty($this->size_units)) {
225
            $this->size_units = 0;
226
        }
227
228
        $this->user = $user;
229
230
        $this->db->begin();
231
232
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "reception (";
233
        $sql .= "ref";
234
        $sql .= ", entity";
235
        $sql .= ", ref_supplier";
236
        $sql .= ", date_creation";
237
        $sql .= ", fk_user_author";
238
        $sql .= ", date_reception";
239
        $sql .= ", date_delivery";
240
        $sql .= ", fk_soc";
241
        $sql .= ", fk_projet";
242
        $sql .= ", fk_shipping_method";
243
        $sql .= ", tracking_number";
244
        $sql .= ", weight";
245
        $sql .= ", size";
246
        $sql .= ", width";
247
        $sql .= ", height";
248
        $sql .= ", weight_units";
249
        $sql .= ", size_units";
250
        $sql .= ", note_private";
251
        $sql .= ", note_public";
252
        $sql .= ", model_pdf";
253
        $sql .= ", fk_incoterms, location_incoterms";
254
        $sql .= ") VALUES (";
255
        $sql .= "'(PROV)'";
256
        $sql .= ", " . ((int) $conf->entity);
257
        $sql .= ", " . ($this->ref_supplier ? "'" . $this->db->escape($this->ref_supplier) . "'" : "null");
258
        $sql .= ", '" . $this->db->idate($now) . "'";
259
        $sql .= ", " . ((int) $user->id);
260
        $sql .= ", " . ($this->date_reception > 0 ? "'" . $this->db->idate($this->date_reception) . "'" : "null");
261
        $sql .= ", " . ($this->date_delivery > 0 ? "'" . $this->db->idate($this->date_delivery) . "'" : "null");
262
        $sql .= ", " . ((int) $this->socid);
263
        $sql .= ", " . ((int) $this->fk_project);
264
        $sql .= ", " . ($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : "null");
265
        $sql .= ", '" . $this->db->escape($this->tracking_number) . "'";
266
        $sql .= ", " . (is_null($this->weight) ? "NULL" : ((float) $this->weight));
267
        $sql .= ", " . (is_null($this->trueDepth) ? "NULL" : ((float) $this->trueDepth));
268
        $sql .= ", " . (is_null($this->trueWidth) ? "NULL" : ((float) $this->trueWidth));
269
        $sql .= ", " . (is_null($this->trueHeight) ? "NULL" : ((float) $this->trueHeight));
270
        $sql .= ", " . (is_null($this->weight_units) ? "NULL" : ((float) $this->weight_units));
271
        $sql .= ", " . (is_null($this->size_units) ? "NULL" : ((float) $this->size_units));
272
        $sql .= ", " . (!empty($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null");
273
        $sql .= ", " . (!empty($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null");
274
        $sql .= ", " . (!empty($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null");
275
        $sql .= ", " . (int) $this->fk_incoterms;
276
        $sql .= ", '" . $this->db->escape($this->location_incoterms) . "'";
277
        $sql .= ")";
278
279
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
280
281
        $resql = $this->db->query($sql);
282
283
        if ($resql) {
284
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "reception");
285
286
            $sql = "UPDATE " . MAIN_DB_PREFIX . "reception";
287
            $sql .= " SET ref = '(PROV" . ((int) $this->id) . ")'";
288
            $sql .= " WHERE rowid = " . ((int) $this->id);
289
290
            dol_syslog(get_class($this) . "::create", LOG_DEBUG);
291
            if ($this->db->query($sql)) {
292
                // Insert of lines
293
                $num = count($this->lines);
294
                for ($i = 0; $i < $num; $i++) {
295
                    $this->lines[$i]->fk_reception = $this->id;
296
297
                    if (!$this->lines[$i]->create($user) > 0) {
298
                        $error++;
299
                    }
300
                }
301
302
                if (!$error && $this->id && $this->origin_id) {
303
                    $ret = $this->add_object_linked();
304
                    if (!$ret) {
305
                        $error++;
306
                    }
307
                }
308
309
                // Create extrafields
310
                if (!$error) {
311
                    $result = $this->insertExtraFields();
312
                    if ($result < 0) {
313
                        $error++;
314
                    }
315
                }
316
317
                if (!$error && !$notrigger) {
318
                    // Call trigger
319
                    $result = $this->call_trigger('RECEPTION_CREATE', $user);
320
                    if ($result < 0) {
321
                        $error++;
322
                    }
323
                    // End call triggers
324
                }
325
326
                if (!$error) {
327
                    $this->db->commit();
328
                    return $this->id;
329
                } else {
330
                    foreach ($this->errors as $errmsg) {
331
                        dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR);
332
                        $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
333
                    }
334
                    $this->db->rollback();
335
                    return -1 * $error;
336
                }
337
            } else {
338
                $error++;
339
                $this->error = $this->db->lasterror() . " - sql=$sql";
340
                $this->db->rollback();
341
                return -2;
342
            }
343
        } else {
344
            $error++;
345
            $this->error = $this->db->error() . " - sql=$sql";
346
            $this->db->rollback();
347
            return -1;
348
        }
349
    }
350
351
352
353
    /**
354
     *  Get object and lines from database
355
     *
356
     *  @param  int     $id         Id of object to load
357
     *  @param  string  $ref        Ref of object
358
     *  @param  string  $ref_ext    External reference of object
359
     *  @return int                 >0 if OK, 0 if not found, <0 if KO
360
     */
361
    public function fetch($id, $ref = '', $ref_ext = '')
362
    {
363
        // Check parameters
364
        if (empty($id) && empty($ref) && empty($ref_ext)) {
365
            return -1;
366
        }
367
368
        $sql = "SELECT e.rowid, e.entity, e.ref, e.fk_soc as socid, e.date_creation, e.ref_supplier, e.ref_ext, e.fk_user_author, e.fk_statut as status, e.billed";
369
        $sql .= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
370
        $sql .= ", e.date_reception as date_reception, e.model_pdf,  e.date_delivery";
371
        $sql .= ", e.fk_shipping_method, e.tracking_number";
372
        $sql .= ", el.fk_source as origin_id, el.sourcetype as origin";
373
        $sql .= ", e.note_private, e.note_public";
374
        $sql .= ', e.fk_incoterms, e.location_incoterms';
375
        $sql .= ', i.libelle as label_incoterms';
376
        $sql .= " FROM " . MAIN_DB_PREFIX . "reception as e";
377
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_element as el ON el.fk_target = e.rowid AND el.targettype = '" . $this->db->escape($this->element) . "'";
378
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_incoterms as i ON e.fk_incoterms = i.rowid';
379
        $sql .= " WHERE e.entity IN (" . getEntity('reception') . ")";
380
        if ($id) {
381
            $sql .= " AND e.rowid = " . ((int) $id);
382
        }
383
        if ($ref) {
384
            $sql .= " AND e.ref = '" . $this->db->escape($ref) . "'";
385
        }
386
        if ($ref_ext) {
387
            $sql .= " AND e.ref_ext = '" . $this->db->escape($ref_ext) . "'";
388
        }
389
390
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
391
        $result = $this->db->query($sql);
392
        if ($result) {
393
            if ($this->db->num_rows($result)) {
394
                $obj = $this->db->fetch_object($result);
395
396
                $this->id                   = $obj->rowid;
397
                $this->entity               = $obj->entity;
398
                $this->ref                  = $obj->ref;
399
                $this->socid                = $obj->socid;
400
                $this->ref_supplier = $obj->ref_supplier;
401
                $this->ref_ext = $obj->ref_ext;
402
                $this->statut               = $obj->status;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

402
                /** @scrutinizer ignore-deprecated */ $this->statut               = $obj->status;

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...
403
                $this->status               = $obj->status;
404
                $this->billed               = $obj->billed;
405
406
                $this->user_author_id       = $obj->fk_user_author;
407
                $this->date_creation        = $this->db->jdate($obj->date_creation);
408
                $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
409
                $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
410
                $this->date_delivery        = $this->db->jdate($obj->date_delivery); // Date planned
411
                $this->model_pdf            = $obj->model_pdf;
412
                $this->shipping_method_id = $obj->fk_shipping_method;
413
                $this->tracking_number      = $obj->tracking_number;
414
                $this->origin               = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

414
                /** @scrutinizer ignore-deprecated */ $this->origin               = ($obj->origin ? $obj->origin : 'commande'); // For compatibility

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...
415
                $this->origin_type          = ($obj->origin ? $obj->origin : 'commande'); // For compatibility
416
                $this->origin_id            = $obj->origin_id;
417
418
                $this->trueWeight           = $obj->weight;
419
                $this->weight_units         = $obj->weight_units;
420
421
                $this->trueWidth            = $obj->width;
422
                $this->width_units          = $obj->size_units;
423
                $this->trueHeight           = $obj->height;
424
                $this->height_units         = $obj->size_units;
425
                $this->trueDepth            = $obj->size;
426
                $this->depth_units          = $obj->size_units;
427
428
                $this->note_public          = $obj->note_public;
429
                $this->note_private         = $obj->note_private;
430
431
                // A denormalized value
432
                $this->trueSize = $obj->size . "x" . $obj->width . "x" . $obj->height;
433
                $this->size_units = $obj->size_units;
434
435
                //Incoterms
436
                $this->fk_incoterms = $obj->fk_incoterms;
437
                $this->location_incoterms = $obj->location_incoterms;
438
                $this->label_incoterms = $obj->label_incoterms;
439
440
                $this->db->free($result);
441
442
                //$file = $conf->reception->dir_output."/".get_exdir(0, 0, 0, 1, $this, 'reception')."/".$this->id.".pdf";
443
                //$this->pdf_filename = $file;
444
445
                // Tracking url
446
                $this->getUrlTrackingStatus($obj->tracking_number);
447
448
                /*
449
                 * Thirdparty
450
                 */
451
                $result = $this->fetch_thirdparty();
452
453
454
                // Retrieve all extrafields for reception
455
                // fetch optionals attributes and labels
456
                $extrafields = new ExtraFields($this->db);
457
                $extrafields->fetch_name_optionals_label($this->table_element, true);
458
                $this->fetch_optionals();
459
460
                /*
461
                 * Lines
462
                 */
463
                $result = $this->fetch_lines();
464
                if ($result < 0) {
465
                    return -3;
466
                }
467
468
                return 1;
469
            } else {
470
                dol_syslog(get_class($this) . '::Fetch no reception found', LOG_ERR);
471
                $this->error = 'Reception with id ' . $id . ' not found';
472
                return 0;
473
            }
474
        } else {
475
            $this->error = $this->db->error();
476
            return -1;
477
        }
478
    }
479
480
    /**
481
     *  Validate object and update stock if option enabled
482
     *
483
     *  @param      User        $user       Object user that validate
484
     *  @param      int         $notrigger  1=Does not execute triggers, 0= execute triggers
485
     *  @return     int                     Return integer <0 if OK, >0 if KO
486
     */
487
    public function valid($user, $notrigger = 0)
488
    {
489
        global $conf, $langs;
490
491
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
492
493
        dol_syslog(get_class($this) . "::valid");
494
495
        // Protection
496
        if ($this->statut) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

496
        if (/** @scrutinizer ignore-deprecated */ $this->statut) {

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...
497
            dol_syslog(get_class($this) . "::valid no draft status", LOG_WARNING);
498
            return 0;
499
        }
500
501
        if (
502
            !((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
503
            || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))
504
        ) {
505
            $this->error = 'Permission denied';
506
            dol_syslog(get_class($this) . "::valid " . $this->error, LOG_ERR);
507
            return -1;
508
        }
509
510
        $this->db->begin();
511
512
        $error = 0;
513
514
        // Define new ref
515
        $soc = new Societe($this->db);
516
        $soc->fetch($this->socid);
517
518
519
        // Define new ref
520
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
521
            $numref = $this->getNextNumRef($soc);
522
        } else {
523
            $numref = $this->ref;
524
        }
525
526
        $this->newref = dol_sanitizeFileName($numref);
527
528
        $now = dol_now();
529
530
        // Validate
531
        $sql = "UPDATE " . MAIN_DB_PREFIX . "reception SET";
532
        $sql .= " ref='" . $this->db->escape($numref) . "'";
533
        $sql .= ", fk_statut = 1";
534
        $sql .= ", date_valid = '" . $this->db->idate($now) . "'";
535
        $sql .= ", fk_user_valid = " . $user->id;
536
        $sql .= " WHERE rowid = " . ((int) $this->id);
537
        dol_syslog(get_class($this) . "::valid update reception", LOG_DEBUG);
538
        $resql = $this->db->query($sql);
539
        if (!$resql) {
540
            $this->error = $this->db->lasterror();
541
            $error++;
542
        }
543
544
        // If stock increment is done on reception (recommended choice)
545
        if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
546
            require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
547
548
            $langs->load("agenda");
549
550
            // Loop on each product line to add a stock movement
551
            // TODO in future, reception lines may not be linked to order line
552
            $sql = "SELECT cd.fk_product, cd.subprice, cd.remise_percent,";
553
            $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
554
            $sql .= " ed.eatby, ed.sellby, ed.batch,";
555
            $sql .= " ed.cost_price";
556
            $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
557
            $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
558
            $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
559
            $sql .= " AND cd.rowid = ed.fk_elementdet";
560
561
            dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
562
            $resql = $this->db->query($sql);
563
            if ($resql) {
564
                $cpt = $this->db->num_rows($resql);
565
                for ($i = 0; $i < $cpt; $i++) {
566
                    $obj = $this->db->fetch_object($resql);
567
568
                    $qty = $obj->qty;
569
570
                    if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('RECEPTION_ALLOW_NEGATIVE_QTY'))) {
571
                        continue;
572
                    }
573
                    dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
574
575
                    //var_dump($this->lines[$i]);
576
                    $mouvS = new MouvementStock($this->db);
577
                    $mouvS->origin = &$this;
578
                    $mouvS->setOrigin($this->element, $this->id);
579
580
                    if (empty($obj->batch)) {
581
                        // line without batch detail
582
583
                        // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
584
                        $inventorycode = '';
585
                        $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
586
587
                        if (intval($result) < 0) {
588
                            $error++;
589
                            $this->errors[] = $mouvS->error;
590
                            $this->errors = array_merge($this->errors, $mouvS->errors);
591
                            break;
592
                        }
593
                    } else {
594
                        // line with batch detail
595
596
                        // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
597
                        // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
598
                        $inventorycode = '';
599
                        $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionValidatedInDolibarr", $numref), $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, '', 0, $inventorycode);
600
601
                        if (intval($result) < 0) {
602
                            $error++;
603
                            $this->errors[] = $mouvS->error;
604
                            $this->errors = array_merge($this->errors, $mouvS->errors);
605
                            break;
606
                        }
607
                    }
608
                }
609
            } else {
610
                $this->db->rollback();
611
                $this->error = $this->db->error();
612
                return -2;
613
            }
614
        }
615
616
        if (!$error) {
617
            // Change status of purchase order to "reception in process" or "totally received"
618
            $status = $this->getStatusDispatch();
619
            if ($status < 0) {
620
                $error++;
621
            } else {
622
                $trigger_key = '';
623
                if ($this->origin_object instanceof CommandeFournisseur && $status == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
624
                    $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
0 ignored issues
show
Bug introduced by
The method Livraison() does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

624
                    /** @scrutinizer ignore-call */ 
625
                    $ret = $this->origin_object->Livraison($user, dol_now(), 'tot', '');
Loading history...
625
                    if ($ret < 0) {
626
                        $error++;
627
                        $this->errors = array_merge($this->errors, $this->origin_object->errors);
628
                    }
629
                } else {
630
                    $ret = $this->setStatut($status, $this->origin_id, 'commande_fournisseur', $trigger_key);
631
                    if ($ret < 0) {
632
                        $error++;
633
                    }
634
                }
635
            }
636
        }
637
638
        if (!$error && !$notrigger) {
639
            // Call trigger
640
            $result = $this->call_trigger('RECEPTION_VALIDATE', $user);
641
            if ($result < 0) {
642
                $error++;
643
            }
644
            // End call triggers
645
        }
646
647
        if (!$error) {
648
            $this->oldref = $this->ref;
649
650
            // Rename directory if dir was a temporary ref
651
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
652
                // Now we rename also files into index
653
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'reception/" . $this->db->escape($this->newref) . "'";
654
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'reception/" . $this->db->escape($this->ref) . "' AND entity = " . ((int) $conf->entity);
655
                $resql = $this->db->query($sql);
656
                if (!$resql) {
657
                    $error++;
658
                    $this->error = $this->db->lasterror();
659
                }
660
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'reception/" . $this->db->escape($this->newref) . "'";
661
                $sql .= " WHERE filepath = 'reception/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
662
                $resql = $this->db->query($sql);
663
                if (!$resql) {
664
                    $error++;
665
                    $this->error = $this->db->lasterror();
666
                }
667
668
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
669
                $oldref = dol_sanitizeFileName($this->ref);
670
                $newref = dol_sanitizeFileName($numref);
671
                $dirsource = $conf->reception->dir_output . '/' . $oldref;
672
                $dirdest = $conf->reception->dir_output . '/' . $newref;
673
                if (!$error && file_exists($dirsource)) {
674
                    dol_syslog(get_class($this) . "::valid rename dir " . $dirsource . " into " . $dirdest);
675
676
                    if (@rename($dirsource, $dirdest)) {
677
                        dol_syslog("Rename ok");
678
                        // Rename docs starting with $oldref with $newref
679
                        $listoffiles = dol_dir_list($conf->reception->dir_output . '/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
680
                        foreach ($listoffiles as $fileentry) {
681
                            $dirsource = $fileentry['name'];
682
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
683
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
684
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
685
                            @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

685
                            /** @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...
686
                        }
687
                    }
688
                }
689
            }
690
        }
691
692
        // Set new ref and current status
693
        if (!$error) {
694
            $this->ref = $numref;
695
            $this->statut = self::STATUS_VALIDATED;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

695
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_VALIDATED;

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...
696
            $this->status = self::STATUS_VALIDATED;
697
        }
698
699
        if (!$error) {
700
            $this->db->commit();
701
            return 1;
702
        } else {
703
            foreach ($this->errors as $errmsg) {
704
                dol_syslog(get_class($this) . "::valid " . $errmsg, LOG_ERR);
705
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
706
            }
707
            $this->db->rollback();
708
            return -1 * $error;
709
        }
710
    }
711
712
    /**
713
     * Get status from all dispatched lines
714
     *
715
     * @return      int                             Return integer <0 if KO, Status of reception if OK
716
     */
717
    public function getStatusDispatch()
718
    {
719
        require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.commande.dispatch.class.php';
720
721
        $status = CommandeFournisseur::STATUS_RECEIVED_PARTIALLY;
722
723
        if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

723
        if (!empty(/** @scrutinizer ignore-deprecated */ $this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || $this->origin == 'commandeFournisseur')) {

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...
724
            if (empty($this->origin_object)) {
725
                $this->fetch_origin();
726
                if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
727
                    $res = $this->origin_object->fetch_lines();
0 ignored issues
show
Bug introduced by
The method fetch_lines() does not exist on Dolibarr\Core\Base\CommonObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

727
                    /** @scrutinizer ignore-call */ 
728
                    $res = $this->origin_object->fetch_lines();
Loading history...
728
                    if ($this->origin_object instanceof CommandeFournisseur) {
729
                        $this->commandeFournisseur = $this->origin_object;  // deprecated
0 ignored issues
show
Bug Best Practice introduced by
The property $commandeFournisseur is declared private in Dolibarr\Core\Base\CommonObject. Since you implement __set, consider adding a @property or @property-write.
Loading history...
Deprecated Code introduced by
The property Dolibarr\Core\Base\Commo...t::$commandeFournisseur has been deprecated: Use $origin_object instead. ( Ignorable by Annotation )

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

729
                        /** @scrutinizer ignore-deprecated */ $this->commandeFournisseur = $this->origin_object;  // deprecated

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...
730
                    } else {
731
                        $this->commandeFournisseur = null;  // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\Commo...t::$commandeFournisseur has been deprecated: Use $origin_object instead. ( Ignorable by Annotation )

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

731
                        /** @scrutinizer ignore-deprecated */ $this->commandeFournisseur = null;  // deprecated

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...
732
                    }
733
                    if ($res < 0) {
734
                        return $res;
735
                    }
736
                }
737
            }
738
739
            $qty_received = array();
740
            $qty_wished = array();
741
742
            $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
743
            $filter = array('t.fk_element' => $this->origin_id);
744
            if (getDolGlobalInt('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
745
                $filter['t.status'] = 1; // Restrict to lines with status validated
746
            }
747
748
            $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
749
            if ($ret < 0) {
750
                $this->error = $supplierorderdispatch->error;
751
                $this->errors = $supplierorderdispatch->errors;
752
                return $ret;
753
            } else {
754
                // build array with quantity received by product in all supplier orders (origin)
755
                foreach ($supplierorderdispatch->lines as $dispatch_line) {
756
                    if (array_key_exists($dispatch_line->fk_product, $qty_received)) {
757
                        $qty_received[$dispatch_line->fk_product] += $dispatch_line->qty;
758
                    } else {
759
                        $qty_received[$dispatch_line->fk_product] = $dispatch_line->qty;
760
                    }
761
                }
762
763
                // qty wished in origin (purchase order, ...)
764
                foreach ($this->origin_object->lines as $origin_line) {
765
                    // exclude lines not qualified for reception
766
                    if ((!getDolGlobalInt('STOCK_SUPPORTS_SERVICES') && $origin_line->product_type > 0) || $origin_line->product_type > 1) {
767
                        continue;
768
                    }
769
770
                    $qty_wished[$origin_line->fk_product] += $origin_line->qty;
771
                }
772
773
                // compare array
774
                $diff_array = array_diff_assoc($qty_received, $qty_wished); // Warning: $diff_array is done only on common keys.
775
                $keys_in_wished_not_in_received = array_diff(array_keys($qty_wished), array_keys($qty_received));
776
                $keys_in_received_not_in_wished = array_diff(array_keys($qty_received), array_keys($qty_wished));
777
778
                if (count($diff_array) == 0 && count($keys_in_wished_not_in_received) == 0 && count($keys_in_received_not_in_wished) == 0) { // no diff => mean everything is received
779
                    $status = CommandeFournisseur::STATUS_RECEIVED_COMPLETELY;
780
                } elseif (getDolGlobalInt('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
781
                    // set totally received if more products received than ordered
782
                    $close = 0;
783
784
                    if (count($diff_array) > 0) {
785
                        // there are some difference between the two arrays
786
                        // scan the array of results
787
                        foreach ($diff_array as $key => $value) {
788
                            // if the quantity delivered is greater or equal to ordered quantity
789
                            if ($qty_received[$key] >= $qty_wished[$key]) {
790
                                $close++;
791
                            }
792
                        }
793
                    }
794
795
                    if ($close == count($diff_array)) {
796
                        // all the products are received equal or more than the ordered quantity
797
                        $status = CommandeFournisseur::STATUS_RECEIVED_COMPLETELY;
798
                    }
799
                }
800
            }
801
        }
802
803
        return $status;
804
    }
805
806
    /**
807
     * Add an reception line.
808
     * If STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS is set, you can add a reception line, with no stock source defined
809
     * If STOCK_MUST_BE_ENOUGH_FOR_RECEPTION is not set, you can add a reception line, even if not enough into stock
810
     *
811
     * @param   int         $entrepot_id        Id of warehouse
812
     * @param   int         $id                 Id of source line (supplier order line)
813
     * @param   float       $qty                Quantity
814
     * @param   array       $array_options      extrafields array
815
     * @param   string      $comment            Comment for stock movement
816
     * @param   int         $eatby              eat-by date
817
     * @param   int         $sellby             sell-by date
818
     * @param   string      $batch              Lot number
819
     * @param   double      $cost_price         Line cost
820
     * @return  int                         Return integer <0 if KO, index of line if OK
821
     */
822
    public function addline($entrepot_id, $id, $qty, $array_options = [], $comment = '', $eatby = null, $sellby = null, $batch = '', $cost_price = 0)
823
    {
824
        global $conf, $langs, $user;
825
826
        $num = count($this->lines);
827
        $line = new CommandeFournisseurDispatch($this->db);
828
829
        $line->fk_entrepot = $entrepot_id;
830
        $line->fk_commandefourndet = $id;
831
        $line->qty = $qty;
832
833
        $supplierorderline = new CommandeFournisseurLigne($this->db);
834
        $result = $supplierorderline->fetch($id);
835
        if ($result <= 0) {
836
            $this->error = $supplierorderline->error;
837
            $this->errors = $supplierorderline->errors;
838
            return -1;
839
        }
840
841
        $fk_product = 0;
842
        if (isModEnabled('stock') && !empty($supplierorderline->fk_product)) {
843
            $fk_product = $supplierorderline->fk_product;
844
845
            if (!($entrepot_id > 0) && !getDolGlobalInt('STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS')) {
846
                $langs->load("errors");
847
                $this->error = $langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
848
                return -1;
849
            }
850
        }
851
852
        // Check batch is set
853
        $product = new Product($this->db);
854
        $product->fetch($fk_product);
855
        if (isModEnabled('productbatch')) {
856
            $langs->load("errors");
857
            if (!empty($product->status_batch) && empty($batch)) {
858
                $this->error = $langs->trans('ErrorProductNeedBatchNumber', $product->ref);
859
                return -1;
860
            } elseif (empty($product->status_batch) && !empty($batch)) {
861
                $this->error = $langs->trans('ErrorProductDoesNotNeedBatchNumber', $product->ref);
862
                return -1;
863
            }
864
865
            // check sell-by / eat-by date is mandatory
866
            $errorMsgArr = Productlot::checkSellOrEatByMandatoryFromProductAndDates($product, $sellby, $eatby);
867
            if (!empty($errorMsgArr)) {
868
                $errorMessage = '<b>' . $product->ref . '</b> : ';
869
                $errorMessage .= '<ul>';
870
                foreach ($errorMsgArr as $errorMsg) {
871
                    $errorMessage .= '<li>' . $errorMsg . '</li>';
872
                }
873
                $errorMessage .= '</ul>';
874
                $this->error = $errorMessage;
875
                return -1;
876
            }
877
        }
878
        unset($product);
879
880
        // extrafields
881
        $line->array_options = $supplierorderline->array_options;
882
        if (!getDolGlobalInt('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) {
883
            foreach ($array_options as $key => $value) {
884
                $line->array_options[$key] = $value;
885
            }
886
        }
887
888
        $line->fk_product = $fk_product;
889
        $line->fk_commande = $supplierorderline->fk_commande;
890
        $line->fk_user = $user->id;
891
        $line->comment = $comment;
892
        $line->batch = $batch;
893
        $line->eatby = $eatby;
894
        $line->sellby = $sellby;
895
        $line->status = 1;
896
        $line->cost_price = $cost_price;
897
        $line->fk_reception = $this->id;
898
899
        $this->lines[$num] = $line;
900
901
        return $num;
902
    }
903
904
905
    /**
906
     *  Update database
907
     *
908
     *  @param  User    $user           User that modify
909
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
910
     *  @return int                     Return integer <0 if KO, >0 if OK
911
     */
912
    public function update($user = null, $notrigger = 0)
913
    {
914
        global $conf;
915
        $error = 0;
916
917
        // Clean parameters
918
919
        if (isset($this->ref)) {
920
            $this->ref = trim($this->ref);
921
        }
922
        if (isset($this->entity)) {
923
            $this->entity = (int) $this->entity;
924
        }
925
        if (isset($this->ref_supplier)) {
926
            $this->ref_supplier = trim($this->ref_supplier);
927
        }
928
        if (isset($this->socid)) {
929
            $this->socid = trim($this->socid);
930
        }
931
        if (isset($this->fk_user_author)) {
932
            $this->fk_user_author = (int) $this->fk_user_author;
933
        }
934
        if (isset($this->fk_user_valid)) {
935
            $this->fk_user_valid = (int) $this->fk_user_valid;
936
        }
937
        if (isset($this->shipping_method_id)) {
938
            $this->shipping_method_id = (int) $this->shipping_method_id;
939
        }
940
        if (isset($this->tracking_number)) {
941
            $this->tracking_number = trim($this->tracking_number);
942
        }
943
        if (isset($this->statut)) {
944
            $this->statut = (int) $this->statut;
945
        }
946
        if (isset($this->trueDepth)) {
947
            $this->trueDepth = trim($this->trueDepth);
948
        }
949
        if (isset($this->trueWidth)) {
950
            $this->trueWidth = trim($this->trueWidth);
951
        }
952
        if (isset($this->trueHeight)) {
953
            $this->trueHeight = trim($this->trueHeight);
954
        }
955
        if (isset($this->size_units)) {
956
            $this->size_units = trim((string) $this->size_units);
957
        }
958
        if (isset($this->weight_units)) {
959
            $this->weight_units = trim((string) $this->weight_units);
960
        }
961
        if (isset($this->trueWeight)) {
962
            $this->weight = trim((string) $this->trueWeight);
963
        }
964
        if (isset($this->note_private)) {
965
            $this->note_private = trim($this->note_private);
966
        }
967
        if (isset($this->note_public)) {
968
            $this->note_public = trim($this->note_public);
969
        }
970
        if (isset($this->model_pdf)) {
971
            $this->model_pdf = trim($this->model_pdf);
972
        }
973
974
975
        // Check parameters
976
        // Put here code to add control on parameters values
977
978
        // Update request
979
        $sql = "UPDATE " . MAIN_DB_PREFIX . "reception SET";
980
981
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ",";
982
        $sql .= " ref_supplier=" . (isset($this->ref_supplier) ? "'" . $this->db->escape($this->ref_supplier) . "'" : "null") . ",";
983
        $sql .= " fk_soc=" . (isset($this->socid) ? $this->socid : "null") . ",";
984
        $sql .= " date_creation=" . (dol_strlen($this->date_creation) != 0 ? "'" . $this->db->idate($this->date_creation) . "'" : 'null') . ",";
985
        $sql .= " fk_user_author=" . (isset($this->fk_user_author) ? $this->fk_user_author : "null") . ",";
986
        $sql .= " date_valid=" . (dol_strlen($this->date_valid) != 0 ? "'" . $this->db->idate($this->date_valid) . "'" : 'null') . ",";
987
        $sql .= " fk_user_valid=" . (isset($this->fk_user_valid) ? $this->fk_user_valid : "null") . ",";
988
        $sql .= " date_reception=" . (dol_strlen($this->date_reception) != 0 ? "'" . $this->db->idate($this->date_reception) . "'" : 'null') . ",";
989
        $sql .= " date_delivery=" . (dol_strlen($this->date_delivery) != 0 ? "'" . $this->db->idate($this->date_delivery) . "'" : 'null') . ",";
990
        $sql .= " fk_shipping_method=" . ((isset($this->shipping_method_id) && $this->shipping_method_id > 0) ? $this->shipping_method_id : "null") . ",";
991
        $sql .= " tracking_number=" . (isset($this->tracking_number) ? "'" . $this->db->escape($this->tracking_number) . "'" : "null") . ",";
992
        $sql .= " fk_statut=" . (isset($this->statut) ? $this->statut : "null") . ",";
993
        $sql .= " height=" . (($this->trueHeight != '') ? $this->trueHeight : "null") . ",";
994
        $sql .= " width=" . (($this->trueWidth != '') ? $this->trueWidth : "null") . ",";
995
        $sql .= " size_units=" . (isset($this->size_units) ? $this->size_units : "null") . ",";
996
        $sql .= " size=" . (($this->trueDepth != '') ? $this->trueDepth : "null") . ",";
997
        $sql .= " weight_units=" . (isset($this->weight_units) ? $this->weight_units : "null") . ",";
998
        $sql .= " weight=" . (($this->trueWeight != '') ? $this->trueWeight : "null") . ",";
999
        $sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
1000
        $sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
1001
        $sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ",";
1002
        $sql .= " entity = " . ((int) $conf->entity);
1003
        $sql .= " WHERE rowid=" . ((int) $this->id);
1004
1005
        $this->db->begin();
1006
1007
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1008
        $resql = $this->db->query($sql);
1009
        if (!$resql) {
1010
            $error++;
1011
            $this->errors[] = "Error " . $this->db->lasterror();
1012
        }
1013
1014
        if (!$error) {
1015
            if (!$notrigger) {
1016
                // Call trigger
1017
                $result = $this->call_trigger('RECEPTION_MODIFY', $user);
1018
                if ($result < 0) {
1019
                    $error++;
1020
                }
1021
                // End call triggers
1022
            }
1023
        }
1024
1025
        // Commit or rollback
1026
        if ($error) {
1027
            foreach ($this->errors as $errmsg) {
1028
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1029
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1030
            }
1031
            $this->db->rollback();
1032
            return -1 * $error;
1033
        } else {
1034
            $this->db->commit();
1035
            return 1;
1036
        }
1037
    }
1038
1039
    /**
1040
     *  Delete reception.
1041
     *
1042
     *  @param  User    $user   Object user
1043
     *  @return int             >0 if OK, 0 if deletion done but failed to delete files, <0 if KO
1044
     */
1045
    public function delete(User $user)
1046
    {
1047
        global $conf, $langs, $user;
1048
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1049
1050
        $error = 0;
1051
        $this->error = '';
1052
1053
1054
        $this->db->begin();
1055
1056
        // Stock control
1057
        if (isModEnabled('stock') && !getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->statut > 0) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1057
        if (isModEnabled('stock') && !getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && /** @scrutinizer ignore-deprecated */ $this->statut > 0) {

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...
1058
            require_once DOL_DOCUMENT_ROOT . "/product/stock/class/mouvementstock.class.php";
1059
1060
            $langs->load("agenda");
1061
1062
            // Loop on each product line to add a stock movement
1063
            $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1064
            $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1065
            $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1066
            $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1067
            $sql .= " AND cd.rowid = ed.fk_elementdet";
1068
1069
            dol_syslog(get_class($this) . "::delete select details", LOG_DEBUG);
1070
            $resql = $this->db->query($sql);
1071
            if ($resql) {
1072
                $cpt = $this->db->num_rows($resql);
1073
                for ($i = 0; $i < $cpt; $i++) {
1074
                    dol_syslog(get_class($this) . "::delete movement index " . $i);
1075
                    $obj = $this->db->fetch_object($resql);
1076
1077
                    $mouvS = new MouvementStock($this->db);
1078
                    // we do not log origin because it will be deleted
1079
                    $mouvS->origin = null;
1080
1081
                    $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ReceptionDeletedInDolibarr", $this->ref), '', $obj->eatby, $obj->sellby, $obj->batch); // Price is set to 0, because we don't want to see WAP changed
1082
                }
1083
            } else {
1084
                $error++;
1085
                $this->errors[] = "Error " . $this->db->lasterror();
1086
            }
1087
        }
1088
1089
        if (!$error) {
1090
            $main = MAIN_DB_PREFIX . 'receptiondet_batch';
1091
            $ef = $main . "_extrafields";
1092
1093
            $sqlef = "DELETE FROM " . $ef . " WHERE fk_object IN (SELECT rowid FROM " . $main . " WHERE fk_reception = " . ((int) $this->id) . ")";
1094
1095
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "receptiondet_batch";
1096
            $sql .= " WHERE fk_reception = " . ((int) $this->id);
1097
1098
            if ($this->db->query($sqlef) && $this->db->query($sql)) {
1099
                // Delete linked object
1100
                $res = $this->deleteObjectLinked();
1101
                if ($res < 0) {
1102
                    $error++;
1103
                }
1104
1105
                if (!$error) {
1106
                    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "reception";
1107
                    $sql .= " WHERE rowid = " . ((int) $this->id);
1108
1109
                    if ($this->db->query($sql)) {
1110
                        // Call trigger
1111
                        $result = $this->call_trigger('RECEPTION_DELETE', $user);
1112
                        if ($result < 0) {
1113
                            $error++;
1114
                        }
1115
                        // End call triggers
1116
1117
                        if (!empty($this->origin) && $this->origin_id > 0) {
1118
                            $this->fetch_origin();
1119
                            if ($this->origin_object->statut == 4) {     // If order source of reception is "partially received"
1120
                                // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
1121
                                $this->origin_object->loadReceptions();
1122
                                //var_dump($this->$origin->receptions);exit;
1123
                                if (count($this->origin_object->receptions) <= 0) {
1124
                                    $this->origin_object->setStatut(3); // ordered
1125
                                }
1126
                            }
1127
                        }
1128
1129
                        if (!$error) {
1130
                            $this->db->commit();
1131
1132
                            // We delete PDFs
1133
                            $ref = dol_sanitizeFileName($this->ref);
1134
                            if (!empty($conf->reception->dir_output)) {
1135
                                $dir = $conf->reception->dir_output . '/' . $ref;
1136
                                $file = $dir . '/' . $ref . '.pdf';
1137
                                if (file_exists($file)) {
1138
                                    if (!dol_delete_file($file)) {
1139
                                        return 0;
1140
                                    }
1141
                                }
1142
                                if (file_exists($dir)) {
1143
                                    if (!dol_delete_dir_recursive($dir)) {
1144
                                        $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
1145
                                        return 0;
1146
                                    }
1147
                                }
1148
                            }
1149
1150
                            return 1;
1151
                        } else {
1152
                            $this->db->rollback();
1153
                            return -1;
1154
                        }
1155
                    } else {
1156
                        $this->error = $this->db->lasterror() . " - sql=$sql";
1157
                        $this->db->rollback();
1158
                        return -3;
1159
                    }
1160
                } else {
1161
                    $this->error = $this->db->lasterror() . " - sql=$sql";
1162
                    $this->db->rollback();
1163
                    return -2;
1164
                }
1165
            } else {
1166
                $this->error = $this->db->lasterror() . " - sql=$sql";
1167
                $this->db->rollback();
1168
                return -1;
1169
            }
1170
        } else {
1171
            $this->db->rollback();
1172
            return -1;
1173
        }
1174
    }
1175
1176
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1177
    /**
1178
     *  Load lines
1179
     *
1180
     *  @return int     >0 if OK, Otherwise if KO
1181
     */
1182
    public function fetch_lines()
1183
    {
1184
		// phpcs:enable
1185
        $this->lines = array();
1186
1187
        require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.commande.dispatch.class.php';
1188
1189
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "receptiondet_batch";
1190
        $sql .= " WHERE fk_reception = " . ((int) $this->id);
1191
1192
        $resql = $this->db->query($sql);
1193
1194
        if (!empty($resql)) {
1195
            while ($obj = $this->db->fetch_object($resql)) {
1196
                $line = new CommandeFournisseurDispatch($this->db);
1197
1198
                $line->fetch($obj->rowid);
1199
1200
                // TODO Remove or keep this ?
1201
                $line->fetch_product();
1202
1203
                $sql_commfourndet = 'SELECT qty, ref, label, description, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent, total_ht, total_ttc, total_tva';
1204
                $sql_commfourndet .= ' FROM ' . MAIN_DB_PREFIX . 'commande_fournisseurdet';
1205
                $sql_commfourndet .= ' WHERE rowid = ' . ((int) $line->fk_commandefourndet);
1206
                $sql_commfourndet .= ' ORDER BY rang';
1207
1208
                $resql_commfourndet = $this->db->query($sql_commfourndet);
1209
                if (!empty($resql_commfourndet)) {
1210
                    $obj = $this->db->fetch_object($resql_commfourndet);
1211
                    $line->qty_asked = $obj->qty;
1212
                    $line->description = $obj->description;
1213
                    $line->desc = $obj->description;
1214
                    $line->tva_tx = $obj->tva_tx;
1215
                    $line->vat_src_code = $obj->vat_src_code;
1216
                    $line->subprice = $obj->subprice;
1217
                    $line->multicurrency_subprice = $obj->multicurrency_subprice;
1218
                    $line->remise_percent = $obj->remise_percent;
1219
                    $line->label = !empty($obj->label) ? $obj->label : (is_object($line->product) ? $line->product->label : '');
1220
                    $line->ref_supplier = $obj->ref;
1221
                    $line->total_ht = $obj->total_ht;
1222
                    $line->total_ttc = $obj->total_ttc;
1223
                    $line->total_tva = $obj->total_tva;
1224
                } else {
1225
                    $line->qty_asked = 0;
1226
                    $line->description = '';
1227
                    $line->desc = '';
1228
                    $line->label = $obj->label;
1229
                }
1230
1231
                $pu_ht = ($line->subprice * $line->qty) * (100 - $line->remise_percent) / 100;
1232
                $tva = $pu_ht * $line->tva_tx / 100;
1233
                $this->total_ht += $pu_ht;
1234
                $this->total_tva += $pu_ht * $line->tva_tx / 100;
1235
1236
                $this->total_ttc += $pu_ht + $tva;
1237
1238
                if (isModEnabled('productbatch') && !empty($line->batch)) {
1239
                    $detail_batch = new stdClass();
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Reception\Classes\stdClass was not found. Did you mean stdClass? If so, make sure to prefix the type with \.
Loading history...
1240
                    $detail_batch->eatby = $line->eatby;
1241
                    $detail_batch->sellby = $line->sellby;
1242
                    $detail_batch->batch = $line->batch;
1243
                    $detail_batch->qty = $line->qty;
1244
1245
                    $line->detail_batch[] = $detail_batch;
1246
                }
1247
1248
                $this->lines[] = $line;
1249
            }
1250
1251
            return 1;
1252
        } else {
1253
            return -1;
1254
        }
1255
    }
1256
1257
    /**
1258
     *  Return clicable link of object (with eventually picto)
1259
     *
1260
     *  @param      int         $withpicto      Add picto into link
1261
     *  @param      int         $option         Where point the link
1262
     *  @param      int         $max            Max length to show
1263
     *  @param      int         $short          Use short labels
1264
     *  @param      int         $notooltip      1=No tooltip
1265
     *  @return     string                      String with URL
1266
     */
1267
    public function getNomUrl($withpicto = 0, $option = 0, $max = 0, $short = 0, $notooltip = 0)
1268
    {
1269
        global $langs, $hookmanager;
1270
1271
        $result = '';
1272
        $label = img_picto('', $this->picto) . ' <u>' . $langs->trans("Reception") . '</u>';
1273
        $label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1274
        $label .= '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . ($this->ref_supplier ? $this->ref_supplier : '');
1275
1276
        $url = constant('BASE_URL') . '/reception/card.php?id=' . $this->id;
1277
1278
        if ($short) {
1279
            return $url;
1280
        }
1281
1282
        $linkclose = '';
1283
        if (empty($notooltip)) {
1284
            if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1285
                $label = $langs->trans("Reception");
1286
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1287
            }
1288
            $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
1289
            $linkclose .= ' class="classfortooltip"';
1290
        }
1291
1292
        $linkstart = '<a href="' . $url . '"';
1293
        $linkstart .= $linkclose . '>';
1294
        $linkend = '</a>';
1295
1296
        $result .= $linkstart;
1297
        if ($withpicto) {
1298
            $result .= img_object(($notooltip ? '' : $label), $this->picto, '', 0, 0, $notooltip ? 0 : 1);
1299
        }
1300
        if ($withpicto != 2) {
1301
            $result .= $this->ref;
1302
        }
1303
1304
        $result .= $linkend;
1305
1306
        global $action;
1307
        $hookmanager->initHooks(array($this->element . 'dao'));
1308
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1309
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1310
        if ($reshook > 0) {
1311
            $result = $hookmanager->resPrint;
1312
        } else {
1313
            $result .= $hookmanager->resPrint;
1314
        }
1315
        return $result;
1316
    }
1317
1318
    /**
1319
     *  Return status label
1320
     *
1321
     *  @param      int     $mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
1322
     *  @return     string              Libelle
1323
     */
1324
    public function getLibStatut($mode = 0)
1325
    {
1326
        return $this->LibStatut($this->statut, $mode);
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1326
        return $this->LibStatut(/** @scrutinizer ignore-deprecated */ $this->statut, $mode);

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...
1327
    }
1328
1329
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1330
    /**
1331
     * Return label of a status
1332
     *
1333
     * @param      int      $status     Id status
1334
     * @param      int      $mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
1335
     * @return     string               Label of status
1336
     */
1337
    public function LibStatut($status, $mode)
1338
    {
1339
		// phpcs:enable
1340
        global $langs;
1341
1342
        // List of long language codes for status
1343
        $this->labelStatus[-1] = 'StatusReceptionCanceled';
1344
        $this->labelStatus[0]  = 'StatusReceptionDraft';
1345
        // product to receive if stock increase is on close or already received if stock increase is on validation
1346
        $this->labelStatus[1]  = 'StatusReceptionValidated';
1347
        if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION")) {
1348
            $this->labelStatus[1]  = 'StatusReceptionValidatedReceived';
1349
        }
1350
        if (getDolGlobalInt("STOCK_CALCULATE_ON_RECEPTION_CLOSE")) {
1351
            $this->labelStatus[1]  = 'StatusReceptionValidatedToReceive';
1352
        }
1353
        $this->labelStatus[2]  = 'StatusReceptionProcessed';
1354
1355
        // List of short language codes for status
1356
        $this->labelStatusShort[-1] = 'StatusReceptionCanceledShort';
1357
        $this->labelStatusShort[0]  = 'StatusReceptionDraftShort';
1358
        $this->labelStatusShort[1]  = 'StatusReceptionValidatedShort';
1359
        $this->labelStatusShort[2]  = 'StatusReceptionProcessedShort';
1360
1361
        $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1362
        $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1363
1364
        $statusType = 'status' . $status;
1365
        if ($status == self::STATUS_VALIDATED) {
1366
            $statusType = 'status4';
1367
        }
1368
        if ($status == self::STATUS_CLOSED) {
1369
            $statusType = 'status6';
1370
        }
1371
1372
        return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1373
    }
1374
1375
    /**
1376
     *  Return clicable link of object (with eventually picto)
1377
     *
1378
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
1379
     *  @param      array       $arraydata              Array of data
1380
     *  @return     string                              HTML Code for Kanban thumb.
1381
     */
1382
    public function getKanbanView($option = '', $arraydata = null)
1383
    {
1384
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1385
1386
        $return = '<div class="box-flex-item box-flex-grow-zero">';
1387
        $return .= '<div class="info-box info-box-sm">';
1388
        $return .= '<div class="info-box-icon bg-infobox-action">';
1389
        $return .= img_picto('', 'order');
1390
        $return .= '</div>';
1391
        $return .= '<div class="info-box-content">';
1392
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
1393
        if ($selected >= 0) {
1394
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
1395
        }
1396
        if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
1397
            $return .= '<br><div class="info-box-ref tdoverflowmax150">' . $this->thirdparty->getNomUrl(1) . '</div>';
1398
        }
1399
        /*if (property_exists($this, 'total_ht')) {
1400
            $return .= '<div class="info-box-ref amount">'.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'</div>';
1401
        }*/
1402
        if (method_exists($this, 'getLibStatut')) {
1403
            $return .= '<div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
1404
        }
1405
        $return .= '</div>';
1406
        $return .= '</div>';
1407
        $return .= '</div>';
1408
1409
        return $return;
1410
    }
1411
1412
    /**
1413
     *  Initialise an instance with random values.
1414
     *  Used to build previews or test instances.
1415
     *  id must be 0 if object instance is a specimen.
1416
     *
1417
     *  @return int
1418
     */
1419
    public function initAsSpecimen()
1420
    {
1421
        global $langs;
1422
1423
        include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.commande.dispatch.class.php';
1424
        $now = dol_now();
1425
1426
        dol_syslog(get_class($this) . "::initAsSpecimen");
1427
1428
        $order = new CommandeFournisseur($this->db);
1429
        $order->initAsSpecimen();
1430
1431
        // Initialise parameters
1432
        $this->id = 0;
1433
        $this->ref = 'SPECIMEN';
1434
        $this->specimen = 1;
1435
        $this->statut               = 1;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1435
        /** @scrutinizer ignore-deprecated */ $this->statut               = 1;

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...
1436
        $this->status               = 1;
1437
        $this->date                 = $now;
1438
        $this->date_creation        = $now;
1439
        $this->date_valid           = $now;
1440
        $this->date_delivery        = $now;
1441
        $this->date_reception = $now + 24 * 3600;
1442
1443
        $this->entrepot_id          = 0;
1444
        $this->socid                = 1;
1445
1446
        $this->origin_id            = 1;
1447
        $this->origin_type          = 'supplier_order';
1448
        $this->origin_object        = $order;
1449
1450
        $this->note_private = 'Private note';
1451
        $this->note_public = 'Public note';
1452
1453
        $this->tracking_number = 'TRACKID-ABC123';
1454
1455
        $this->fk_incoterms = 1;
1456
1457
        $nbp = 5;
1458
        $xnbp = 0;
1459
        while ($xnbp < $nbp) {
1460
            $line = new CommandeFournisseurDispatch($this->db);
1461
            $line->desc = $langs->trans("Description") . " " . $xnbp;
1462
            $line->libelle = $langs->trans("Description") . " " . $xnbp;    // deprecated
1463
            $line->label = $langs->trans("Description") . " " . $xnbp;
1464
            $line->qty = 10;
1465
1466
            $line->fk_product = $this->origin_object->lines[$xnbp]->fk_product;
1467
1468
            $this->lines[] = $line;
1469
            $xnbp++;
1470
        }
1471
1472
        return 1;
1473
    }
1474
1475
    /**
1476
     *  Set the planned delivery date
1477
     *
1478
     *  @param      User            $user               Object utilisateur qui modifie
1479
     *  @param      integer         $delivery_date     Delivery date
1480
     *  @return     int                                 Return integer <0 if KO, >0 if OK
1481
     */
1482
    public function setDeliveryDate($user, $delivery_date)
1483
    {
1484
        // phpcs:enable
1485
        if ($user->hasRight('reception', 'creer')) {
1486
            $sql = "UPDATE " . MAIN_DB_PREFIX . "reception";
1487
            $sql .= " SET date_delivery = " . ($delivery_date ? "'" . $this->db->idate($delivery_date) . "'" : 'null');
1488
            $sql .= " WHERE rowid = " . ((int) $this->id);
1489
1490
            dol_syslog(get_class($this) . "::setDeliveryDate", LOG_DEBUG);
1491
            $resql = $this->db->query($sql);
1492
            if ($resql) {
1493
                $this->date_delivery = $delivery_date;
1494
                return 1;
1495
            } else {
1496
                $this->error = $this->db->error();
1497
                return -1;
1498
            }
1499
        } else {
1500
            return -2;
1501
        }
1502
    }
1503
1504
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1505
    /**
1506
     *  Fetch deliveries method and return an array. Load array this->meths(rowid=>label).
1507
     *
1508
     *  @return void
1509
     */
1510
    public function fetch_delivery_methods()
1511
    {
1512
		// phpcs:enable
1513
        global $langs;
1514
        $this->meths = array();
1515
1516
        $sql = "SELECT em.rowid, em.code, em.libelle";
1517
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_shipment_mode as em";
1518
        $sql .= " WHERE em.active = 1";
1519
        $sql .= " ORDER BY em.libelle ASC";
1520
1521
        $resql = $this->db->query($sql);
1522
        if ($resql) {
1523
            while ($obj = $this->db->fetch_object($resql)) {
1524
                $label = $langs->trans('ReceptionMethod' . $obj->code);
1525
                $this->meths[$obj->rowid] = ($label != 'ReceptionMethod' . $obj->code ? $label : $obj->libelle);
1526
            }
1527
        }
1528
    }
1529
1530
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1531
    /**
1532
     *  Fetch all deliveries method and return an array. Load array this->listmeths.
1533
     *
1534
     *  @param  int      $id     only this carrier, all if none
1535
     *  @return void
1536
     */
1537
    public function list_delivery_methods($id = 0)
1538
    {
1539
		// phpcs:enable
1540
        global $langs;
1541
1542
        $this->listmeths = array();
1543
        $i = 0;
1544
1545
        $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1546
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_shipment_mode as em";
1547
        if (!empty($id)) {
1548
            $sql .= " WHERE em.rowid = " . ((int) $id);
1549
        }
1550
1551
        $resql = $this->db->query($sql);
1552
        if ($resql) {
1553
            while ($obj = $this->db->fetch_object($resql)) {
1554
                $this->listmeths[$i]['rowid'] = $obj->rowid;
1555
                $this->listmeths[$i]['code'] = $obj->code;
1556
                $label = $langs->trans('ReceptionMethod' . $obj->code);
1557
                $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod' . $obj->code ? $label : $obj->libelle);
1558
                $this->listmeths[$i]['description'] = $obj->description;
1559
                $this->listmeths[$i]['tracking'] = $obj->tracking;
1560
                $this->listmeths[$i]['active'] = $obj->active;
1561
                $i++;
1562
            }
1563
        }
1564
    }
1565
1566
    /**
1567
     * Forge an set tracking url
1568
     *
1569
     * @param   string  $value      Value
1570
     * @return  void
1571
     */
1572
    public function getUrlTrackingStatus($value = '')
1573
    {
1574
        if (!empty($this->shipping_method_id)) {
1575
            $sql = "SELECT em.code, em.tracking";
1576
            $sql .= " FROM " . MAIN_DB_PREFIX . "c_shipment_mode as em";
1577
            $sql .= " WHERE em.rowid = " . ((int) $this->shipping_method_id);
1578
1579
            $resql = $this->db->query($sql);
1580
            if ($resql) {
1581
                if ($obj = $this->db->fetch_object($resql)) {
1582
                    $tracking = $obj->tracking;
1583
                }
1584
            }
1585
        }
1586
1587
        if (!empty($tracking) && !empty($value)) {
1588
            $url = str_replace('{TRACKID}', $value, $tracking);
1589
            $this->tracking_url = sprintf('<a target="_blank" rel="noopener noreferrer" href="%s">%s</a>', $url, ($value ? $value : 'url'));
1590
        } else {
1591
            $this->tracking_url = $value;
1592
        }
1593
    }
1594
1595
    /**
1596
     *  Classify the reception as closed (this records also the stock movement)
1597
     *
1598
     *  @return     int     Return integer <0 if KO, >0 if OK
1599
     */
1600
    public function setClosed()
1601
    {
1602
        global $conf, $langs, $user;
1603
1604
        $error = 0;
1605
1606
        // Protection. This avoid to move stock later when we should not
1607
        if ($this->statut == Reception::STATUS_CLOSED) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1607
        if (/** @scrutinizer ignore-deprecated */ $this->statut == Reception::STATUS_CLOSED) {

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...
1608
            dol_syslog(get_class($this) . "::setClosed already in closed status", LOG_WARNING);
1609
            return 0;
1610
        }
1611
1612
        $this->db->begin();
1613
1614
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'reception SET fk_statut = ' . self::STATUS_CLOSED;
1615
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > 0';
1616
1617
        $resql = $this->db->query($sql);
1618
        if ($resql) {
1619
            // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1620
            if ($this->origin == 'order_supplier' && $this->origin_id > 0) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

1620
            if (/** @scrutinizer ignore-deprecated */ $this->origin == 'order_supplier' && $this->origin_id > 0) {

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...
1621
                $order = new CommandeFournisseur($this->db);
1622
                $order->fetch($this->origin_id);
1623
1624
                $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1625
1626
                $receptions_match_order = 1;
1627
                foreach ($order->lines as $line) {
1628
                    $lineid = $line->id;
1629
                    $qty = $line->qty;
1630
                    if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1631
                        $receptions_match_order = 0;
1632
                        $text = 'Qty for order line id ' . $lineid . ' is ' . $qty . '. However in the receptions with status Reception::STATUS_CLOSED=' . self::STATUS_CLOSED . ' we have qty = ' . $order->receptions[$lineid] . ', so we can t close order';
1633
                        dol_syslog($text);
1634
                        break;
1635
                    }
1636
                }
1637
                if ($receptions_match_order) {
1638
                    dol_syslog("Qty for the " . count($order->lines) . " lines of order have same value for receptions with status Reception::STATUS_CLOSED=" . self::STATUS_CLOSED . ', so we close order');
1639
                    $order->Livraison($user, dol_now(), 'tot', 'Reception ' . $this->ref);
1640
                }
1641
            }
1642
1643
            $this->statut = self::STATUS_CLOSED;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1643
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_CLOSED;

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...
1644
            $this->status = self::STATUS_CLOSED;
1645
1646
            // If stock increment is done on closing
1647
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1648
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1649
1650
                $langs->load("agenda");
1651
1652
                // Loop on each product line to add a stock movement
1653
                // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1654
                $sql = "SELECT cd.fk_product, cd.subprice,";
1655
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1656
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1657
                $sql .= " ed.cost_price";
1658
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1659
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1660
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1661
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1662
1663
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1664
                $resql = $this->db->query($sql);
1665
1666
                if ($resql) {
1667
                    $cpt = $this->db->num_rows($resql);
1668
                    for ($i = 0; $i < $cpt; $i++) {
1669
                        $obj = $this->db->fetch_object($resql);
1670
1671
                        $qty = $obj->qty;
1672
1673
                        if ($qty <= 0) {
1674
                            continue;
1675
                        }
1676
                        dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
1677
1678
                        $mouvS = new MouvementStock($this->db);
1679
                        $mouvS->origin = &$this;
1680
                        $mouvS->setOrigin($this->element, $this->id);
1681
1682
                        if (empty($obj->batch)) {
1683
                            // line without batch detail
1684
1685
                            // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1686
                            $inventorycode = '';
1687
                            $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1688
                            if ($result < 0) {
1689
                                $this->error = $mouvS->error;
1690
                                $this->errors = $mouvS->errors;
1691
                                $error++;
1692
                                break;
1693
                            }
1694
                        } else {
1695
                            // line with batch detail
1696
1697
                            // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1698
                            $inventorycode = '';
1699
                            $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, '', 0, $inventorycode);
1700
1701
                            if ($result < 0) {
1702
                                $this->error = $mouvS->error;
1703
                                $this->errors = $mouvS->errors;
1704
                                $error++;
1705
                                break;
1706
                            }
1707
                        }
1708
                    }
1709
                } else {
1710
                    $this->error = $this->db->lasterror();
1711
                    $error++;
1712
                }
1713
            }
1714
1715
            // Call trigger
1716
            if (!$error) {
1717
                $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1718
                if ($result < 0) {
1719
                    $error++;
1720
                }
1721
            }
1722
        } else {
1723
            dol_print_error($this->db);
1724
            $error++;
1725
        }
1726
1727
        if (!$error) {
1728
            $this->db->commit();
1729
            return 1;
1730
        } else {
1731
            $this->statut = self::STATUS_VALIDATED;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1731
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_VALIDATED;

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...
1732
            $this->status = self::STATUS_VALIDATED;
1733
            $this->db->rollback();
1734
            return -1;
1735
        }
1736
    }
1737
1738
    /**
1739
     *  Classify the reception as invoiced (used for example by trigger when WORKFLOW_RECEPTION_CLASSIFY_BILLED_INVOICE is on)
1740
     *
1741
     *  @return     int     Return integer <0 if ko, >0 if ok
1742
     */
1743
    public function setBilled()
1744
    {
1745
        global $user;
1746
        $error = 0;
1747
1748
        $this->db->begin();
1749
1750
        if ($this->statut == Reception::STATUS_VALIDATED) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1750
        if (/** @scrutinizer ignore-deprecated */ $this->statut == Reception::STATUS_VALIDATED) {

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...
1751
            // do not close if already closed
1752
            $this->setClosed();
1753
        }
1754
1755
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'reception SET billed=1';
1756
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > 0';
1757
1758
        $resql = $this->db->query($sql);
1759
        if ($resql) {
1760
            $this->billed = 1;
1761
1762
            // Call trigger
1763
            $result = $this->call_trigger('RECEPTION_BILLED', $user);
1764
            if ($result < 0) {
1765
                $this->billed = 0;
1766
                $error++;
1767
            }
1768
        } else {
1769
            $error++;
1770
            $this->errors[] = $this->db->lasterror;
1771
        }
1772
1773
        if (empty($error)) {
1774
            $this->db->commit();
1775
            return 1;
1776
        } else {
1777
            $this->db->rollback();
1778
            return -1;
1779
        }
1780
    }
1781
1782
    /**
1783
     *  Classify the reception as validated/opened
1784
     *
1785
     *  @return     int     Return integer <0 if ko, >0 if ok
1786
     */
1787
    public function reOpen()
1788
    {
1789
        global $conf, $langs, $user;
1790
1791
        $error = 0;
1792
1793
        $this->db->begin();
1794
1795
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'reception SET fk_statut=1, billed=0';
1796
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > 0';
1797
1798
        $resql = $this->db->query($sql);
1799
        if ($resql) {
1800
            $this->statut = self::STATUS_VALIDATED;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1800
            /** @scrutinizer ignore-deprecated */ $this->statut = self::STATUS_VALIDATED;

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...
1801
            $this->status = self::STATUS_VALIDATED;
1802
            $this->billed = 0;
1803
1804
            // If stock increment is done on closing
1805
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1806
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1807
                $numref = $this->ref;
1808
                $langs->load("agenda");
1809
1810
                // Loop on each product line to add a stock movement
1811
                // TODO possibilite de receptionner a partir d'une propale ou autre origine
1812
                $sql = "SELECT ed.fk_product, cd.subprice,";
1813
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1814
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1815
                $sql .= " ed.cost_price";
1816
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1817
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1818
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1819
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1820
1821
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1822
                $resql = $this->db->query($sql);
1823
                if ($resql) {
1824
                    $cpt = $this->db->num_rows($resql);
1825
                    for ($i = 0; $i < $cpt; $i++) {
1826
                        $obj = $this->db->fetch_object($resql);
1827
1828
                        $qty = $obj->qty;
1829
1830
                        if ($qty <= 0) {
1831
                            continue;
1832
                        }
1833
1834
                        dol_syslog(get_class($this) . "::reopen reception movement index " . $i . " ed.rowid=" . $obj->rowid);
1835
1836
                        //var_dump($this->lines[$i]);
1837
                        $mouvS = new MouvementStock($this->db);
1838
                        $mouvS->origin = &$this;
1839
                        $mouvS->setOrigin($this->element, $this->id);
1840
1841
                        if (empty($obj->batch)) {
1842
                            // line without batch detail
1843
1844
                            // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1845
                            $inventorycode = '';
1846
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1847
1848
                            if ($result < 0) {
1849
                                $this->error = $mouvS->error;
1850
                                $this->errors = $mouvS->errors;
1851
                                $error++;
1852
                                break;
1853
                            }
1854
                        } else {
1855
                            // line with batch detail
1856
1857
                            // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1858
                            $inventorycode = '';
1859
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock, $inventorycode);
1860
                            if ($result < 0) {
1861
                                $this->error = $mouvS->error;
1862
                                $this->errors = $mouvS->errors;
1863
                                $error++;
1864
                                break;
1865
                            }
1866
                        }
1867
                    }
1868
                } else {
1869
                    $this->error = $this->db->lasterror();
1870
                    $error++;
1871
                }
1872
            }
1873
1874
            if (!$error) {
1875
                // Call trigger
1876
                $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1877
                if ($result < 0) {
1878
                    $error++;
1879
                }
1880
            }
1881
1882
            if (!$error && $this->origin == 'order_supplier') {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

1882
            if (!$error && /** @scrutinizer ignore-deprecated */ $this->origin == 'order_supplier') {

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...
1883
                $commande = new CommandeFournisseur($this->db);
1884
                $commande->fetch($this->origin_id);
1885
                $result = $commande->setStatus($user, 4);
1886
                if ($result < 0) {
1887
                    $error++;
1888
                    $this->error = $commande->error;
1889
                    $this->errors = $commande->errors;
1890
                }
1891
            }
1892
        } else {
1893
            $error++;
1894
            $this->errors[] = $this->db->lasterror();
1895
        }
1896
1897
        if (!$error) {
1898
            $this->db->commit();
1899
            return 1;
1900
        } else {
1901
            $this->db->rollback();
1902
            return -1;
1903
        }
1904
    }
1905
1906
    /**
1907
     *  Set draft status
1908
     *
1909
     *  @param  User    $user           Object user that modify
1910
     *  @return int                     Return integer <0 if KO, >0 if OK
1911
     */
1912
    public function setDraft($user)
1913
    {
1914
        // phpcs:enable
1915
        global $conf, $langs;
1916
1917
        $error = 0;
1918
1919
        // Protection
1920
        if ($this->statut <= self::STATUS_DRAFT) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

1920
        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...
1921
            return 0;
1922
        }
1923
1924
        if (
1925
            !((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1926
            || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))
1927
        ) {
1928
            $this->error = 'Permission denied';
1929
            return -1;
1930
        }
1931
1932
        $this->db->begin();
1933
1934
        $sql = "UPDATE " . MAIN_DB_PREFIX . "reception";
1935
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
1936
        $sql .= " WHERE rowid = " . ((int) $this->id);
1937
1938
        dol_syslog(__METHOD__, LOG_DEBUG);
1939
        if ($this->db->query($sql)) {
1940
            // If stock increment is done on closing
1941
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1942
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1943
1944
                $langs->load("agenda");
1945
1946
                // Loop on each product line to add a stock movement
1947
                // TODO possibilite de receptionner a partir d'une propale ou autre origine
1948
                $sql = "SELECT cd.fk_product, cd.subprice,";
1949
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1950
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1951
                $sql .= " ed.cost_price";
1952
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1953
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1954
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1955
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1956
1957
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1958
                $resql = $this->db->query($sql);
1959
                if ($resql) {
1960
                    $cpt = $this->db->num_rows($resql);
1961
                    for ($i = 0; $i < $cpt; $i++) {
1962
                        $obj = $this->db->fetch_object($resql);
1963
1964
                        $qty = $obj->qty;
1965
1966
1967
                        if ($qty <= 0) {
1968
                            continue;
1969
                        }
1970
                        dol_syslog(get_class($this) . "::reopen reception movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
1971
1972
                        //var_dump($this->lines[$i]);
1973
                        $mouvS = new MouvementStock($this->db);
1974
                        $mouvS->origin = &$this;
1975
                        $mouvS->setOrigin($this->element, $this->id);
1976
1977
                        if (empty($obj->batch)) {
1978
                            // line without batch detail
1979
1980
                            // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1981
                            $inventorycode = '';
1982
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1983
                            if ($result < 0) {
1984
                                $this->error = $mouvS->error;
1985
                                $this->errors = $mouvS->errors;
1986
                                $error++;
1987
                                break;
1988
                            }
1989
                        } else {
1990
                            // line with batch detail
1991
1992
                            // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1993
                            $inventorycode = '';
1994
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, 0, $inventorycode);
1995
                            if ($result < 0) {
1996
                                $this->error = $mouvS->error;
1997
                                $this->errors = $mouvS->errors;
1998
                                $error++;
1999
                                break;
2000
                            }
2001
                        }
2002
                    }
2003
                } else {
2004
                    $this->error = $this->db->lasterror();
2005
                    $error++;
2006
                }
2007
            }
2008
2009
            if (!$error) {
2010
                // Call trigger
2011
                $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2012
                if ($result < 0) {
2013
                    $error++;
2014
                }
2015
            }
2016
            if ($this->origin == 'order_supplier') {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

2016
            if (/** @scrutinizer ignore-deprecated */ $this->origin == 'order_supplier') {

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...
2017
                if (!empty($this->origin) && $this->origin_id > 0) {
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

2017
                if (!empty(/** @scrutinizer ignore-deprecated */ $this->origin) && $this->origin_id > 0) {

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...
2018
                    $this->fetch_origin();
2019
                    if ($this->origin_object->statut == 4) {  // If order source of reception is "partially received"
2020
                        // Check if there is no more reception validated.
2021
                        $this->origin_object->fetchObjectLinked();
2022
                        $setStatut = 1;
2023
                        if (!empty($this->origin_object->linkedObjects['reception'])) {
2024
                            foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2025
                                if ($rcption->statut > 0) {
2026
                                    $setStatut = 0;
2027
                                    break;
2028
                                }
2029
                            }
2030
                            //var_dump($this->$origin->receptions);exit;
2031
                            if ($setStatut) {
2032
                                $this->origin_object->setStatut(3); // ordered
2033
                            }
2034
                        }
2035
                    }
2036
                }
2037
            }
2038
2039
            if (!$error) {
2040
                $this->statut = self::STATUS_DRAFT;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$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

2040
                /** @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...
2041
                $this->status = self::STATUS_DRAFT;
2042
                $this->db->commit();
2043
                return 1;
2044
            } else {
2045
                $this->db->rollback();
2046
                return -1;
2047
            }
2048
        } else {
2049
            $this->error = $this->db->error();
2050
            $this->db->rollback();
2051
            return -1;
2052
        }
2053
    }
2054
2055
    /**
2056
     *  Create a document onto disk according to template module.
2057
     *
2058
     *  @param      string      $modele         Force the model to using ('' to not force)
2059
     *  @param      Translate   $outputlangs    object lang to use for translations
2060
     *  @param      int         $hidedetails    Hide details of lines
2061
     *  @param      int         $hidedesc       Hide description
2062
     *  @param      int         $hideref        Hide ref
2063
     *  @return     int                         0 if KO, 1 if OK
2064
     */
2065
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2066
    {
2067
        global $conf, $langs;
2068
2069
        $langs->load("receptions");
2070
2071
        if (!dol_strlen($modele)) {
2072
            $modele = 'squille';
2073
2074
            if ($this->model_pdf) {
2075
                $modele = $this->model_pdf;
2076
            } elseif (getDolGlobalString('RECEPTION_ADDON_PDF')) {
2077
                $modele = getDolGlobalString('RECEPTION_ADDON_PDF');
2078
            }
2079
        }
2080
2081
        $modelpath = "core/modules/reception/doc/";
2082
2083
        $this->fetch_origin();
2084
2085
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2086
    }
2087
2088
    /**
2089
     * Function used to replace a thirdparty id with another one.
2090
     *
2091
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
2092
     * @param   int     $origin_id  Old thirdparty id
2093
     * @param   int     $dest_id    New thirdparty id
2094
     * @return  bool
2095
     */
2096
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2097
    {
2098
        $tables = array('reception');
2099
2100
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2101
    }
2102
2103
    /**
2104
     * Function used to replace a product id with another one.
2105
     *
2106
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
2107
     * @param   int     $origin_id  Old thirdparty id
2108
     * @param   int     $dest_id    New thirdparty id
2109
     * @return  bool
2110
     */
2111
    public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
2112
    {
2113
        $tables = array(
2114
            'receptiondet_batch'
2115
        );
2116
2117
        return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
2118
    }
2119
}
2120