Passed
Pull Request — dev (#6)
by Rafael
79:24 queued 24:08
created

Reception::create()   F

Complexity

Conditions 33
Paths > 20000

Size

Total Lines 140
Code Lines 103

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

507
        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...
508
            dol_syslog(get_class($this) . "::valid no draft status", LOG_WARNING);
509
            return 0;
510
        }
511
512
        if (
513
            !((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
514
            || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))
515
        ) {
516
            $this->error = 'Permission denied';
517
            dol_syslog(get_class($this) . "::valid " . $this->error, LOG_ERR);
518
            return -1;
519
        }
520
521
        $this->db->begin();
522
523
        $error = 0;
524
525
        // Define new ref
526
        $soc = new Societe($this->db);
527
        $soc->fetch($this->socid);
528
529
530
        // Define new ref
531
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
532
            $numref = $this->getNextNumRef($soc);
533
        } else {
534
            $numref = $this->ref;
535
        }
536
537
        $this->newref = dol_sanitizeFileName($numref);
538
539
        $now = dol_now();
540
541
        // Validate
542
        $sql = "UPDATE " . MAIN_DB_PREFIX . "reception SET";
543
        $sql .= " ref='" . $this->db->escape($numref) . "'";
544
        $sql .= ", fk_statut = 1";
545
        $sql .= ", date_valid = '" . $this->db->idate($now) . "'";
546
        $sql .= ", fk_user_valid = " . $user->id;
547
        $sql .= " WHERE rowid = " . ((int) $this->id);
548
        dol_syslog(get_class($this) . "::valid update reception", LOG_DEBUG);
549
        $resql = $this->db->query($sql);
550
        if (!$resql) {
551
            $this->error = $this->db->lasterror();
552
            $error++;
553
        }
554
555
        // If stock increment is done on reception (recommended choice)
556
        if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
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;
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

587
                    /** @scrutinizer ignore-deprecated */ $mouvS->origin = &$this;

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...
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
730
        $status = CommandeFournisseur::STATUS_RECEIVED_PARTIALLY;
731
732
        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

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

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

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

Loading history...
733
            if (empty($this->origin_object)) {
734
                $this->fetch_origin();
735
                if ($this->origin_object instanceof CommonObject && empty($this->origin_object->lines)) {
736
                    $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

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

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

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

1066
        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...
1067
            require_once DOL_DOCUMENT_ROOT . "/product/stock/class/mouvementstock.class.php";
1068
1069
            $langs->load("agenda");
1070
1071
            // Loop on each product line to add a stock movement
1072
            $sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as receptiondet_batch_id";
1073
            $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1074
            $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1075
            $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1076
            $sql .= " AND cd.rowid = ed.fk_elementdet";
1077
1078
            dol_syslog(get_class($this) . "::delete select details", LOG_DEBUG);
1079
            $resql = $this->db->query($sql);
1080
            if ($resql) {
1081
                $cpt = $this->db->num_rows($resql);
1082
                for ($i = 0; $i < $cpt; $i++) {
1083
                    dol_syslog(get_class($this) . "::delete movement index " . $i);
1084
                    $obj = $this->db->fetch_object($resql);
1085
1086
                    $mouvS = new MouvementStock($this->db);
1087
                    // we do not log origin because it will be deleted
1088
                    $mouvS->origin = null;
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

1088
                    /** @scrutinizer ignore-deprecated */ $mouvS->origin = null;

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

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

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

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

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

1651
            /** @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...
1652
            $this->status = self::STATUS_CLOSED;
1653
1654
            // If stock increment is done on closing
1655
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1656
1657
                $langs->load("agenda");
1658
1659
                // Loop on each product line to add a stock movement
1660
                // TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1661
                $sql = "SELECT cd.fk_product, cd.subprice,";
1662
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1663
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1664
                $sql .= " ed.cost_price";
1665
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1666
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1667
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1668
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1669
1670
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1671
                $resql = $this->db->query($sql);
1672
1673
                if ($resql) {
1674
                    $cpt = $this->db->num_rows($resql);
1675
                    for ($i = 0; $i < $cpt; $i++) {
1676
                        $obj = $this->db->fetch_object($resql);
1677
1678
                        $qty = $obj->qty;
1679
1680
                        if ($qty <= 0) {
1681
                            continue;
1682
                        }
1683
                        dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
1684
1685
                        $mouvS = new MouvementStock($this->db);
1686
                        $mouvS->origin = &$this;
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

1686
                        /** @scrutinizer ignore-deprecated */ $mouvS->origin = &$this;

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...
1687
                        $mouvS->setOrigin($this->element, $this->id);
1688
1689
                        if (empty($obj->batch)) {
1690
                            // line without batch detail
1691
1692
                            // 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
1693
                            $inventorycode = '';
1694
                            $result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionClassifyClosedInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1695
                            if ($result < 0) {
1696
                                $this->error = $mouvS->error;
1697
                                $this->errors = $mouvS->errors;
1698
                                $error++;
1699
                                break;
1700
                            }
1701
                        } else {
1702
                            // line with batch detail
1703
1704
                            // 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
1705
                            $inventorycode = '';
1706
                            $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);
1707
1708
                            if ($result < 0) {
1709
                                $this->error = $mouvS->error;
1710
                                $this->errors = $mouvS->errors;
1711
                                $error++;
1712
                                break;
1713
                            }
1714
                        }
1715
                    }
1716
                } else {
1717
                    $this->error = $this->db->lasterror();
1718
                    $error++;
1719
                }
1720
            }
1721
1722
            // Call trigger
1723
            if (!$error) {
1724
                $result = $this->call_trigger('RECEPTION_CLOSED', $user);
1725
                if ($result < 0) {
1726
                    $error++;
1727
                }
1728
            }
1729
        } else {
1730
            dol_print_error($this->db);
1731
            $error++;
1732
        }
1733
1734
        if (!$error) {
1735
            $this->db->commit();
1736
            return 1;
1737
        } else {
1738
            $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

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

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

1807
            /** @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...
1808
            $this->status = self::STATUS_VALIDATED;
1809
            $this->billed = 0;
1810
1811
            // If stock increment is done on closing
1812
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE')) {
1813
                $numref = $this->ref;
1814
                $langs->load("agenda");
1815
1816
                // Loop on each product line to add a stock movement
1817
                // TODO possibilite de receptionner a partir d'une propale ou autre origine
1818
                $sql = "SELECT ed.fk_product, cd.subprice,";
1819
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1820
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1821
                $sql .= " ed.cost_price";
1822
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1823
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1824
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1825
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1826
1827
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1828
                $resql = $this->db->query($sql);
1829
                if ($resql) {
1830
                    $cpt = $this->db->num_rows($resql);
1831
                    for ($i = 0; $i < $cpt; $i++) {
1832
                        $obj = $this->db->fetch_object($resql);
1833
1834
                        $qty = $obj->qty;
1835
1836
                        if ($qty <= 0) {
1837
                            continue;
1838
                        }
1839
1840
                        dol_syslog(get_class($this) . "::reopen reception movement index " . $i . " ed.rowid=" . $obj->rowid);
1841
1842
                        //var_dump($this->lines[$i]);
1843
                        $mouvS = new MouvementStock($this->db);
1844
                        $mouvS->origin = &$this;
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

1844
                        /** @scrutinizer ignore-deprecated */ $mouvS->origin = &$this;

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...
1845
                        $mouvS->setOrigin($this->element, $this->id);
1846
1847
                        if (empty($obj->batch)) {
1848
                            // line without batch detail
1849
1850
                            // 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
1851
                            $inventorycode = '';
1852
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionUnClassifyCloseddInDolibarr", $numref), '', '', '', '', 0, $inventorycode);
1853
1854
                            if ($result < 0) {
1855
                                $this->error = $mouvS->error;
1856
                                $this->errors = $mouvS->errors;
1857
                                $error++;
1858
                                break;
1859
                            }
1860
                        } else {
1861
                            // line with batch detail
1862
1863
                            // 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
1864
                            $inventorycode = '';
1865
                            $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);
1866
                            if ($result < 0) {
1867
                                $this->error = $mouvS->error;
1868
                                $this->errors = $mouvS->errors;
1869
                                $error++;
1870
                                break;
1871
                            }
1872
                        }
1873
                    }
1874
                } else {
1875
                    $this->error = $this->db->lasterror();
1876
                    $error++;
1877
                }
1878
            }
1879
1880
            if (!$error) {
1881
                // Call trigger
1882
                $result = $this->call_trigger('RECEPTION_REOPEN', $user);
1883
                if ($result < 0) {
1884
                    $error++;
1885
                }
1886
            }
1887
1888
            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

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

1926
        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...
1927
            return 0;
1928
        }
1929
1930
        if (
1931
            !((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'creer'))
1932
            || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('reception', 'reception_advance', 'validate')))
1933
        ) {
1934
            $this->error = 'Permission denied';
1935
            return -1;
1936
        }
1937
1938
        $this->db->begin();
1939
1940
        $sql = "UPDATE " . MAIN_DB_PREFIX . "reception";
1941
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
1942
        $sql .= " WHERE rowid = " . ((int) $this->id);
1943
1944
        dol_syslog(__METHOD__, LOG_DEBUG);
1945
        if ($this->db->query($sql)) {
1946
            // If stock increment is done on closing
1947
            if (!$error && isModEnabled('stock') && getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION')) {
1948
1949
                $langs->load("agenda");
1950
1951
                // Loop on each product line to add a stock movement
1952
                // TODO possibilite de receptionner a partir d'une propale ou autre origine
1953
                $sql = "SELECT cd.fk_product, cd.subprice,";
1954
                $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
1955
                $sql .= " ed.eatby, ed.sellby, ed.batch,";
1956
                $sql .= " ed.cost_price";
1957
                $sql .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd,";
1958
                $sql .= " " . MAIN_DB_PREFIX . "receptiondet_batch as ed";
1959
                $sql .= " WHERE ed.fk_reception = " . ((int) $this->id);
1960
                $sql .= " AND cd.rowid = ed.fk_elementdet";
1961
1962
                dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
1963
                $resql = $this->db->query($sql);
1964
                if ($resql) {
1965
                    $cpt = $this->db->num_rows($resql);
1966
                    for ($i = 0; $i < $cpt; $i++) {
1967
                        $obj = $this->db->fetch_object($resql);
1968
1969
                        $qty = $obj->qty;
1970
1971
1972
                        if ($qty <= 0) {
1973
                            continue;
1974
                        }
1975
                        dol_syslog(get_class($this) . "::reopen reception movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
1976
1977
                        //var_dump($this->lines[$i]);
1978
                        $mouvS = new MouvementStock($this->db);
1979
                        $mouvS->origin = &$this;
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

1979
                        /** @scrutinizer ignore-deprecated */ $mouvS->origin = &$this;

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...
1980
                        $mouvS->setOrigin($this->element, $this->id);
1981
1982
                        if (empty($obj->batch)) {
1983
                            // line without batch detail
1984
1985
                            // 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
1986
                            $inventorycode = '';
1987
                            $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->cost_price, $langs->trans("ReceptionBackToDraftInDolibarr", $this->ref), '', '', '', '', 0, $inventorycode);
1988
                            if ($result < 0) {
1989
                                $this->error = $mouvS->error;
1990
                                $this->errors = $mouvS->errors;
1991
                                $error++;
1992
                                break;
1993
                            }
1994
                        } else {
1995
                            // line with batch detail
1996
1997
                            // 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
1998
                            $inventorycode = '';
1999
                            $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);
2000
                            if ($result < 0) {
2001
                                $this->error = $mouvS->error;
2002
                                $this->errors = $mouvS->errors;
2003
                                $error++;
2004
                                break;
2005
                            }
2006
                        }
2007
                    }
2008
                } else {
2009
                    $this->error = $this->db->lasterror();
2010
                    $error++;
2011
                }
2012
            }
2013
2014
            if (!$error) {
2015
                // Call trigger
2016
                $result = $this->call_trigger('RECEPTION_UNVALIDATE', $user);
2017
                if ($result < 0) {
2018
                    $error++;
2019
                }
2020
            }
2021
            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

2021
            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...
2022
                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

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

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