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

Reception::valid()   F

Complexity

Conditions 42
Paths > 20000

Size

Total Lines 222
Code Lines 139

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 42
eloc 139
nc 32262
nop 2
dl 0
loc 222
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

411
                /** @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...
412
                $this->status               = $obj->status;
413
                $this->billed               = $obj->billed;
414
415
                $this->user_author_id       = $obj->fk_user_author;
416
                $this->date_creation        = $this->db->jdate($obj->date_creation);
417
                $this->date = $this->db->jdate($obj->date_reception); // TODO deprecated
418
                $this->date_reception = $this->db->jdate($obj->date_reception); // Date real
419
                $this->date_delivery        = $this->db->jdate($obj->date_delivery); // Date planned
420
                $this->model_pdf            = $obj->model_pdf;
421
                $this->shipping_method_id = $obj->fk_shipping_method;
422
                $this->tracking_number      = $obj->tracking_number;
423
                $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

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

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

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

695
                            /** @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...
696
                        }
697
                    }
698
                }
699
            }
700
        }
701
702
        // Set new ref and current status
703
        if (!$error) {
704
            $this->ref = $numref;
705
            $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

705
            /** @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...
706
            $this->status = self::STATUS_VALIDATED;
707
        }
708
709
        if (!$error) {
710
            $this->db->commit();
711
            return 1;
712
        } else {
713
            foreach ($this->errors as $errmsg) {
714
                dol_syslog(get_class($this) . "::valid " . $errmsg, LOG_ERR);
715
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
716
            }
717
            $this->db->rollback();
718
            return -1 * $error;
719
        }
720
    }
721
722
    /**
723
     * Get status from all dispatched lines
724
     *
725
     * @return      int                             Return integer <0 if KO, Status of reception if OK
726
     */
727
    public function getStatusDispatch()
728
    {
729
        require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.commande.class.php';
730
        require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.commande.dispatch.class.php';
731
732
        $status = CommandeFournisseur::STATUS_RECEIVED_PARTIALLY;
733
734
        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

734
        if (!empty($this->origin) && $this->origin_id > 0 && ($this->origin == 'order_supplier' || /** @scrutinizer ignore-deprecated */ $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...
735
            if (empty($this->origin_object)) {
736
                $this->fetch_origin();
737
                if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
738
                    $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

738
                    /** @scrutinizer ignore-call */ 
739
                    $res = $this->origin_object->fetch_lines();
Loading history...
739
                    if ($this->origin_object instanceof CommandeFournisseur) {
740
                        $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

740
                        /** @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...
741
                    } else {
742
                        $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

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

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

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

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

1619
        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...
1620
            dol_syslog(get_class($this) . "::setClosed already in closed status", LOG_WARNING);
1621
            return 0;
1622
        }
1623
1624
        $this->db->begin();
1625
1626
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'reception SET fk_statut = ' . self::STATUS_CLOSED;
1627
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > 0';
1628
1629
        $resql = $this->db->query($sql);
1630
        if ($resql) {
1631
            // Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1632
            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

1632
            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...
1633
                $order = new CommandeFournisseur($this->db);
1634
                $order->fetch($this->origin_id);
1635
1636
                $order->loadReceptions(self::STATUS_CLOSED); // Fill $order->receptions = array(orderlineid => qty)
1637
1638
                $receptions_match_order = 1;
1639
                foreach ($order->lines as $line) {
1640
                    $lineid = $line->id;
1641
                    $qty = $line->qty;
1642
                    if (($line->product_type == 0 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && $order->receptions[$lineid] < $qty) {
1643
                        $receptions_match_order = 0;
1644
                        $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';
1645
                        dol_syslog($text);
1646
                        break;
1647
                    }
1648
                }
1649
                if ($receptions_match_order) {
1650
                    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');
1651
                    $order->Livraison($user, dol_now(), 'tot', 'Reception ' . $this->ref);
1652
                }
1653
            }
1654
1655
            $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

1655
            /** @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...
1656
            $this->status = self::STATUS_CLOSED;
1657
1658
            // If stock increment is done on closing
1659
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1660
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1661
1662
                $langs->load("agenda");
1663
1664
                // Loop on each product line to add a stock movement
1665
                // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1666
                $sql = "SELECT cd.fk_product, cd.subprice,";
1667
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1668
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1669
                $sql .= " ed.cost_price";
1670
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1671
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1672
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1673
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1674
1675
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1676
                $resql = $this->db->query($sql);
1677
1678
                if ($resql) {
1679
                    $cpt = $this->db->num_rows($resql);
1680
                    for ($i = 0; $i < $cpt; $i++) {
1681
                        $obj = $this->db->fetch_object($resql);
1682
1683
                        $qty = $obj->qty;
1684
1685
                        if ($qty <= 0) {
1686
                            continue;
1687
                        }
1688
                        dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
1689
1690
                        $mouvS = new MouvementStock($this->db);
1691
                        $mouvS->origin = &$this;
1692
                        $mouvS->setOrigin($this->element, $this->id);
1693
1694
                        if (empty($obj->batch)) {
1695
                            // line without 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), '', '', '', '', 0, $inventorycode);
1700
                            if ($result < 0) {
1701
                                $this->error = $mouvS->error;
1702
                                $this->errors = $mouvS->errors;
1703
                                $error++;
1704
                                break;
1705
                            }
1706
                        } else {
1707
                            // line with batch detail
1708
1709
                            // 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
1710
                            $inventorycode = '';
1711
                            $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);
1712
1713
                            if ($result < 0) {
1714
                                $this->error = $mouvS->error;
1715
                                $this->errors = $mouvS->errors;
1716
                                $error++;
1717
                                break;
1718
                            }
1719
                        }
1720
                    }
1721
                } else {
1722
                    $this->error = $this->db->lasterror();
1723
                    $error++;
1724
                }
1725
            }
1726
1727
            // Call trigger
1728
            if (!$error) {
1729
                $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1730
                if ($result < 0) {
1731
                    $error++;
1732
                }
1733
            }
1734
        } else {
1735
            dol_print_error($this->db);
1736
            $error++;
1737
        }
1738
1739
        if (!$error) {
1740
            $this->db->commit();
1741
            return 1;
1742
        } else {
1743
            $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

1743
            /** @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...
1744
            $this->status = self::STATUS_VALIDATED;
1745
            $this->db->rollback();
1746
            return -1;
1747
        }
1748
    }
1749
1750
    /**
1751
     *  Classify the reception as invoiced (used for example by trigger when WORKFLOW_RECEPTION_CLASSIFY_BILLED_INVOICE is on)
1752
     *
1753
     *  @return     int     Return integer <0 if ko, >0 if ok
1754
     */
1755
    public function setBilled()
1756
    {
1757
        global $user;
1758
        $error = 0;
1759
1760
        $this->db->begin();
1761
1762
        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

1762
        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...
1763
            // do not close if already closed
1764
            $this->setClosed();
1765
        }
1766
1767
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'reception SET billed=1';
1768
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > 0';
1769
1770
        $resql = $this->db->query($sql);
1771
        if ($resql) {
1772
            $this->billed = 1;
1773
1774
            // Call trigger
1775
            $result = $this->call_trigger('RECEPTION_BILLED', $user);
1776
            if ($result < 0) {
1777
                $this->billed = 0;
1778
                $error++;
1779
            }
1780
        } else {
1781
            $error++;
1782
            $this->errors[] = $this->db->lasterror;
1783
        }
1784
1785
        if (empty($error)) {
1786
            $this->db->commit();
1787
            return 1;
1788
        } else {
1789
            $this->db->rollback();
1790
            return -1;
1791
        }
1792
    }
1793
1794
    /**
1795
     *  Classify the reception as validated/opened
1796
     *
1797
     *  @return     int     Return integer <0 if ko, >0 if ok
1798
     */
1799
    public function reOpen()
1800
    {
1801
        global $conf, $langs, $user;
1802
1803
        $error = 0;
1804
1805
        $this->db->begin();
1806
1807
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'reception SET fk_statut=1, billed=0';
1808
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > 0';
1809
1810
        $resql = $this->db->query($sql);
1811
        if ($resql) {
1812
            $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

1812
            /** @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...
1813
            $this->status = self::STATUS_VALIDATED;
1814
            $this->billed = 0;
1815
1816
            // If stock increment is done on closing
1817
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1818
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1819
                $numref = $this->ref;
1820
                $langs->load("agenda");
1821
1822
                // Loop on each product line to add a stock movement
1823
                // TODO possibilite de receptionner a partir d'une propale ou autre origine
1824
                $sql = "SELECT ed.fk_product, cd.subprice,";
1825
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1826
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1827
                $sql .= " ed.cost_price";
1828
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1829
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1830
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1831
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1832
1833
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1834
                $resql = $this->db->query($sql);
1835
                if ($resql) {
1836
                    $cpt = $this->db->num_rows($resql);
1837
                    for ($i = 0; $i < $cpt; $i++) {
1838
                        $obj = $this->db->fetch_object($resql);
1839
1840
                        $qty = $obj->qty;
1841
1842
                        if ($qty <= 0) {
1843
                            continue;
1844
                        }
1845
1846
                        dol_syslog(get_class($this) . "::reopen reception movement index " . $i . " ed.rowid=" . $obj->rowid);
1847
1848
                        //var_dump($this->lines[$i]);
1849
                        $mouvS = new MouvementStock($this->db);
1850
                        $mouvS->origin = &$this;
1851
                        $mouvS->setOrigin($this->element, $this->id);
1852
1853
                        if (empty($obj->batch)) {
1854
                            // line without batch detail
1855
1856
                            // 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
1857
                            $inventorycode = '';
1858
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1859
1860
                            if ($result < 0) {
1861
                                $this->error = $mouvS->error;
1862
                                $this->errors = $mouvS->errors;
1863
                                $error++;
1864
                                break;
1865
                            }
1866
                        } else {
1867
                            // line with batch detail
1868
1869
                            // 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
1870
                            $inventorycode = '';
1871
                            $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);
1872
                            if ($result < 0) {
1873
                                $this->error = $mouvS->error;
1874
                                $this->errors = $mouvS->errors;
1875
                                $error++;
1876
                                break;
1877
                            }
1878
                        }
1879
                    }
1880
                } else {
1881
                    $this->error = $this->db->lasterror();
1882
                    $error++;
1883
                }
1884
            }
1885
1886
            if (!$error) {
1887
                // Call trigger
1888
                $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1889
                if ($result < 0) {
1890
                    $error++;
1891
                }
1892
            }
1893
1894
            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

1894
            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...
1895
                $commande = new CommandeFournisseur($this->db);
1896
                $commande->fetch($this->origin_id);
1897
                $result = $commande->setStatus($user, 4);
1898
                if ($result < 0) {
1899
                    $error++;
1900
                    $this->error = $commande->error;
1901
                    $this->errors = $commande->errors;
1902
                }
1903
            }
1904
        } else {
1905
            $error++;
1906
            $this->errors[] = $this->db->lasterror();
1907
        }
1908
1909
        if (!$error) {
1910
            $this->db->commit();
1911
            return 1;
1912
        } else {
1913
            $this->db->rollback();
1914
            return -1;
1915
        }
1916
    }
1917
1918
    /**
1919
     *  Set draft status
1920
     *
1921
     *  @param  User    $user           Object user that modify
1922
     *  @return int                     Return integer <0 if KO, >0 if OK
1923
     */
1924
    public function setDraft($user)
1925
    {
1926
        // phpcs:enable
1927
        global $conf, $langs;
1928
1929
        $error = 0;
1930
1931
        // Protection
1932
        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

1932
        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...
1933
            return 0;
1934
        }
1935
1936
        if (
1937
            !((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1938
            || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))
1939
        ) {
1940
            $this->error = 'Permission denied';
1941
            return -1;
1942
        }
1943
1944
        $this->db->begin();
1945
1946
        $sql = "UPDATE " . MAIN_DB_PREFIX . "reception";
1947
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
1948
        $sql .= " WHERE rowid = " . ((int) $this->id);
1949
1950
        dol_syslog(__METHOD__, LOG_DEBUG);
1951
        if ($this->db->query($sql)) {
1952
            // If stock increment is done on closing
1953
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1954
                require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1955
1956
                $langs->load("agenda");
1957
1958
                // Loop on each product line to add a stock movement
1959
                // TODO possibilite de receptionner a partir d'une propale ou autre origine
1960
                $sql = "SELECT cd.fk_product, cd.subprice,";
1961
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1962
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1963
                $sql .= " ed.cost_price";
1964
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1965
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1966
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1967
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1968
1969
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1970
                $resql = $this->db->query($sql);
1971
                if ($resql) {
1972
                    $cpt = $this->db->num_rows($resql);
1973
                    for ($i = 0; $i < $cpt; $i++) {
1974
                        $obj = $this->db->fetch_object($resql);
1975
1976
                        $qty = $obj->qty;
1977
1978
1979
                        if ($qty <= 0) {
1980
                            continue;
1981
                        }
1982
                        dol_syslog(get_class($this) . "::reopen reception movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
1983
1984
                        //var_dump($this->lines[$i]);
1985
                        $mouvS = new MouvementStock($this->db);
1986
                        $mouvS->origin = &$this;
1987
                        $mouvS->setOrigin($this->element, $this->id);
1988
1989
                        if (empty($obj->batch)) {
1990
                            // line without 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), '', '', '', '', 0, $inventorycode);
1995
                            if ($result < 0) {
1996
                                $this->error = $mouvS->error;
1997
                                $this->errors = $mouvS->errors;
1998
                                $error++;
1999
                                break;
2000
                            }
2001
                        } else {
2002
                            // line with batch detail
2003
2004
                            // 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
2005
                            $inventorycode = '';
2006
                            $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);
2007
                            if ($result < 0) {
2008
                                $this->error = $mouvS->error;
2009
                                $this->errors = $mouvS->errors;
2010
                                $error++;
2011
                                break;
2012
                            }
2013
                        }
2014
                    }
2015
                } else {
2016
                    $this->error = $this->db->lasterror();
2017
                    $error++;
2018
                }
2019
            }
2020
2021
            if (!$error) {
2022
                // Call trigger
2023
                $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2024
                if ($result < 0) {
2025
                    $error++;
2026
                }
2027
            }
2028
            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

2028
            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...
2029
                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

2029
                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...
2030
                    $this->fetch_origin();
2031
                    if ($this->origin_object->statut == 4) {  // If order source of reception is "partially received"
2032
                        // Check if there is no more reception validated.
2033
                        $this->origin_object->fetchObjectLinked();
2034
                        $setStatut = 1;
2035
                        if (!empty($this->origin_object->linkedObjects['reception'])) {
2036
                            foreach ($this->origin_object->linkedObjects['reception'] as $rcption) {
2037
                                if ($rcption->statut > 0) {
2038
                                    $setStatut = 0;
2039
                                    break;
2040
                                }
2041
                            }
2042
                            //var_dump($this->$origin->receptions);exit;
2043
                            if ($setStatut) {
2044
                                $this->origin_object->setStatut(3); // ordered
2045
                            }
2046
                        }
2047
                    }
2048
                }
2049
            }
2050
2051
            if (!$error) {
2052
                $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

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