Completed
Branch develop (9ce07b)
by
unknown
26:31
created

Reception::delete()   F

Complexity

Conditions 22
Paths 450

Size

Total Lines 149

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
nc 450
nop 1
dl 0
loc 149
rs 0.6111
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
/* Copyright (C) 2003-2008	Rodolphe Quiedeville	<[email protected]>
3
 * Copyright (C) 2005-2012	Regis Houssin			<[email protected]>
4
 * Copyright (C) 2007		Franky Van Liedekerke	<[email protected]>
5
 * Copyright (C) 2006-2012	Laurent Destailleur		<[email protected]>
6
 * Copyright (C) 2011-2017	Juanjo Menent			<[email protected]>
7
 * Copyright (C) 2013       Florian Henry		  	<[email protected]>
8
 * Copyright (C) 2014		Cedric GROSS			<[email protected]>
9
 * Copyright (C) 2014-2015  Marcos García           <[email protected]>
10
 * Copyright (C) 2014-2015  Francis Appels          <[email protected]>
11
 * Copyright (C) 2015       Claudio Aschieri        <[email protected]>
12
 * Copyright (C) 2016		Ferran Marcet			<[email protected]>
13
 * Copyright (C) 2018		Quentin Vial-Gouteyron  <[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 *  \file       htdocs/reception/class/reception.class.php
31
 *  \ingroup    reception
32
 *  \brief      Fichier de la classe de gestion des receptions
33
 */
34
35
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
36
require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
37
if (! empty($conf->propal->enabled)) require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
38
if (! empty($conf->commande->enabled)) require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
39
40
41
/**
42
 *	Class to manage receptions
43
 */
44
class Reception extends CommonObject
45
{
46
	public $element="reception";
47
	public $fk_element="fk_reception";
48
	public $table_element="reception";
49
	public $table_element_line="commande_fournisseur_dispatch";
50
	protected $ismultientitymanaged = 1;	// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
51
    public $picto = 'reception';
52
53
	var $socid;
54
	var $ref_supplier;
55
	var $ref_int;
56
	var $brouillon;
57
	var $entrepot_id;
58
	var $lines=array();
59
	var $tracking_number;
60
	var $tracking_url;
61
	var $billed;
62
	var $model_pdf;
63
64
	var $trueWeight;
65
	var $weight_units;
66
	var $trueWidth;
67
	var $width_units;
68
	var $trueHeight;
69
	var $height_units;
70
	var $trueDepth;
71
	var $depth_units;
72
	// A denormalized value
73
	var $trueSize;
74
75
	var $date_delivery;		// Date delivery planed
76
77
78
	/**
79
	 * Effective delivery date
80
	 * @var int
81
	 */
82
	public $date_reception;
83
	var $date_creation;
84
	var $date_valid;
85
86
	var $meths;
87
	var $listmeths;			// List of carriers
88
89
90
	const STATUS_DRAFT = 0;
91
	const STATUS_VALIDATED = 1;
92
	const STATUS_CLOSED = 2;
93
94
95
96
	/**
97
	 *	Constructor
98
	 *
99
	 *  @param		DoliDB		$db      Database handler
100
	 */
101
	function __construct($db)
102
	{
103
		$this->db = $db;
104
		$this->lines = array();
105
		$this->products = array();
106
107
		// List of long language codes for status
108
		$this->statuts = array();
109
		$this->statuts[-1] = 'StatusReceptionCanceled';
110
		$this->statuts[0]  = 'StatusReceptionDraft';
111
		$this->statuts[1]  = 'StatusReceptionValidated';
112
		$this->statuts[2]  = 'StatusReceptionProcessed';
113
	}
114
115
	/**
116
	 *	Return next contract ref
117
	 *
118
	 *	@param	Societe		$soc	Thirdparty object
119
	 *	@return string				Free reference for contract
120
	 */
121
	function getNextNumRef($soc)
122
	{
123
		global $langs, $conf;
124
		$langs->load("receptions");
125
126
	    if (!empty($conf->global->RECEPTION_ADDON_NUMBER))
127
        {
128
			$mybool = false;
129
130
			$file = $conf->global->RECEPTION_ADDON_NUMBER.".php";
131
			$classname = $conf->global->RECEPTION_ADDON_NUMBER;
132
133
	        // Include file with class
134
	        $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
135
136
	        foreach ($dirmodels as $reldir) {
137
138
		        $dir = dol_buildpath($reldir."core/modules/reception/");
139
140
		        // Load file with numbering class (if found)
141
		        $mybool|=@include_once $dir.$file;
142
	        }
143
144
	        if (! $mybool)
145
	        {
146
		        dol_print_error('',"Failed to include file ".$file);
147
		        return '';
148
	        }
149
150
			$obj = new $classname();
151
152
			$numref = "";
153
			$numref = $obj->getNextValue($soc,$this);
154
155
			if ( $numref != "")
156
			{
157
				return $numref;
158
			}
159
			else
160
			{
161
				dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
162
				return "";
163
			}
164
        }
165
	    else
166
	    {
167
		    print $langs->trans("Error")." ".$langs->trans("Error_RECEPTION_ADDON_NUMBER_NotDefined");
168
		    return "";
169
	    }
170
	}
171
172
	/**
173
	 *  Create reception en base
174
	 *
175
	 *  @param	User	$user       Objet du user qui cree
176
	 *  @param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
177
	 *  @return int 				<0 si erreur, id reception creee si ok
178
	 */
179
	function create($user, $notrigger=0)
180
	{
181
		global $conf, $hookmanager;
182
183
		$now=dol_now();
184
185
		require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php';
186
		$error = 0;
187
188
		// Clean parameters
189
		$this->brouillon = 1;
190
		$this->tracking_number = dol_sanitizeFileName($this->tracking_number);
191
		if (empty($this->fk_project)) $this->fk_project = 0;
192
193
		$this->user = $user;
194
195
196
		$this->db->begin();
197
198
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."reception (";
199
		$sql.= "ref";
200
		$sql.= ", entity";
201
		$sql.= ", ref_supplier";
202
		$sql.= ", ref_int";
203
		$sql.= ", date_creation";
204
		$sql.= ", fk_user_author";
205
		$sql.= ", date_reception";
206
		$sql.= ", date_delivery";
207
		$sql.= ", fk_soc";
208
		$sql.= ", fk_projet";
209
		$sql.= ", fk_shipping_method";
210
		$sql.= ", tracking_number";
211
		$sql.= ", weight";
212
		$sql.= ", size";
213
		$sql.= ", width";
214
		$sql.= ", height";
215
		$sql.= ", weight_units";
216
		$sql.= ", size_units";
217
		$sql.= ", note_private";
218
		$sql.= ", note_public";
219
		$sql.= ", model_pdf";
220
		$sql.= ", fk_incoterms, location_incoterms";
221
		$sql.= ") VALUES (";
222
		$sql.= "'(PROV)'";
223
		$sql.= ", ".$conf->entity;
224
		$sql.= ", ".($this->ref_supplier?"'".$this->db->escape($this->ref_supplier)."'":"null");
225
		$sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
226
		$sql.= ", '".$this->db->idate($now)."'";
227
		$sql.= ", ".$user->id;
228
		$sql.= ", ".($this->date_reception>0?"'".$this->db->idate($this->date_reception)."'":"null");
229
		$sql.= ", ".($this->date_delivery>0?"'".$this->db->idate($this->date_delivery)."'":"null");
230
		$sql.= ", ".$this->socid;
231
		$sql.= ", ".$this->fk_project;
232
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:"null");
233
		$sql.= ", '".$this->db->escape($this->tracking_number)."'";
234
		$sql.= ", ".$this->weight;
235
		$sql.= ", ".$this->sizeS;	// TODO Should use this->trueDepth
236
		$sql.= ", ".$this->sizeW;	// TODO Should use this->trueWidth
237
		$sql.= ", ".$this->sizeH;	// TODO Should use this->trueHeight
238
		$sql.= ", ".$this->weight_units;
239
		$sql.= ", ".$this->size_units;
240
		$sql.= ", ".(!empty($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null");
241
		$sql.= ", ".(!empty($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null");
242
		$sql.= ", ".(!empty($this->model_pdf)?"'".$this->db->escape($this->model_pdf)."'":"null");
243
        $sql.= ", ".(int) $this->fk_incoterms;
244
        $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
245
		$sql.= ")";
246
247
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
248
249
		$resql=$this->db->query($sql);
250
251
		if ($resql)
252
		{
253
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."reception");
254
255
			$sql = "UPDATE ".MAIN_DB_PREFIX."reception";
256
			$sql.= " SET ref = '(PROV".$this->id.")'";
257
			$sql.= " WHERE rowid = ".$this->id;
258
259
			dol_syslog(get_class($this)."::create", LOG_DEBUG);
260
			if ($this->db->query($sql))
261
			{
262
				// Insertion des lignes
263
				$num=count($this->lines);
264
				for ($i = 0; $i < $num; $i++)
265
				{
266
					$this->lines[$i]->fk_reception = $this->id;
267
268
					if (! $this->lines[$i]->create($user) > 0)
269
					{
270
						$error++;
271
					}
272
				}
273
274
				if (! $error && $this->id && $this->origin_id)
275
				{
276
					$ret = $this->add_object_linked();
277
					if (!$ret)
278
					{
279
						$error++;
280
					}
281
				}
282
283
				// Actions on extra fields (by external module or standard code)
284
				// TODO le hook fait double emploi avec le trigger !!
285
				$hookmanager->initHooks(array('receptiondao'));
286
				$parameters=array('socid'=>$this->id);
287
				$reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
0 ignored issues
show
Bug introduced by
The variable $action does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
288
				if (empty($reshook))
289
				{
290
					if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
291
					{
292
						$result=$this->insertExtraFields();
293
						if ($result < 0)
294
						{
295
							$error++;
296
						}
297
					}
298
				}
299
				else if ($reshook < 0) $error++;
300
301
				if (! $error && ! $notrigger)
302
				{
303
                    // Call trigger
304
                    $result=$this->call_trigger('RECEPTION_CREATE',$user);
305
                    if ($result < 0) { $error++; }
306
                    // End call triggers
307
308
					if (! $error)
309
					{
310
						$this->db->commit();
311
						return $this->id;
312
					}
313
					else
314
					{
315
						foreach($this->errors as $errmsg)
316
						{
317
							dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
318
							$this->error.=($this->error?', '.$errmsg:$errmsg);
319
						}
320
						$this->db->rollback();
321
						return -1*$error;
322
					}
323
				}
324
				else
325
				{
326
					$error++;
327
					$this->error=$this->db->lasterror()." - sql=$sql";
328
					$this->db->rollback();
329
					return -3;
330
				}
331
			}
332
			else
333
			{
334
				$error++;
335
				$this->error=$this->db->lasterror()." - sql=$sql";
336
				$this->db->rollback();
337
				return -2;
338
			}
339
		}
340
		else
341
		{
342
			$error++;
343
			$this->error=$this->db->error()." - sql=$sql";
344
			$this->db->rollback();
345
			return -1;
346
		}
347
	}
348
349
350
351
	/**
352
	 *	Get object and lines from database
353
	 *
354
	 *	@param	int		$id       	Id of object to load
355
	 * 	@param	string	$ref		Ref of object
356
	 * 	@param	string	$ref_ext	External reference of object
357
     * 	@param	string	$ref_int	Internal reference of other object
358
	 *	@return int			        >0 if OK, 0 if not found, <0 if KO
359
	 */
360
	function fetch($id, $ref='', $ref_ext='', $ref_int='')
361
	{
362
		global $conf;
363
364
		// Check parameters
365
		if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
366
367
		$sql = "SELECT e.rowid, e.ref, e.fk_soc as socid, e.date_creation, e.ref_supplier, e.ref_ext, e.ref_int, e.fk_user_author, e.fk_statut";
368
		$sql.= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
369
		$sql.= ", e.date_reception as date_reception, e.model_pdf,  e.date_delivery";
370
		$sql.= ", e.fk_shipping_method, e.tracking_number";
371
		$sql.= ", el.fk_source as origin_id, el.sourcetype as origin";
372
		$sql.= ", e.note_private, e.note_public";
373
        $sql.= ', e.fk_incoterms, e.location_incoterms';
374
        $sql.= ', i.libelle as libelle_incoterms';
375
		$sql.= " FROM ".MAIN_DB_PREFIX."reception as e";
376
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
377
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
378
		$sql.= " WHERE e.entity IN (".getEntity('reception').")";
379
		if ($id)   	  $sql.= " AND e.rowid=".$id;
380
        if ($ref)     $sql.= " AND e.ref='".$this->db->escape($ref)."'";
381
        if ($ref_ext) $sql.= " AND e.ref_ext='".$this->db->escape($ref_ext)."'";
382
        if ($ref_int) $sql.= " AND e.ref_int='".$this->db->escape($ref_int)."'";
383
384
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
385
		$result = $this->db->query($sql);
386
		if ($result)
387
		{
388
			if ($this->db->num_rows($result))
389
			{
390
				$obj = $this->db->fetch_object($result);
391
392
				$this->id                   = $obj->rowid;
393
				$this->ref                  = $obj->ref;
394
				$this->socid                = $obj->socid;
395
				$this->ref_supplier			= $obj->ref_supplier;
396
				$this->ref_ext				= $obj->ref_ext;
397
				$this->ref_int				= $obj->ref_int;
398
				$this->statut               = $obj->fk_statut;
399
				$this->user_author_id       = $obj->fk_user_author;
400
				$this->date_creation        = $this->db->jdate($obj->date_creation);
401
				$this->date                 = $this->db->jdate($obj->date_reception);	// TODO deprecated
402
				$this->date_reception      = $this->db->jdate($obj->date_reception);	// TODO deprecated
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_reception) can also be of type string. However, the property $date_reception is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
403
				$this->date_reception        = $this->db->jdate($obj->date_reception);	// Date real
404
				$this->date_delivery        = $this->db->jdate($obj->date_delivery);	// Date planed
405
				$this->fk_delivery_address  = $obj->fk_address;
406
				$this->modelpdf             = $obj->model_pdf;
407
				$this->shipping_method_id	= $obj->fk_shipping_method;
408
				$this->tracking_number      = $obj->tracking_number;
409
				$this->origin               = ($obj->origin?$obj->origin:'commande'); // For compatibility
410
				$this->origin_id            = $obj->origin_id;
411
				$this->billed				= ($obj->fk_statut==2?1:0);
412
413
				$this->trueWeight           = $obj->weight;
414
				$this->weight_units         = $obj->weight_units;
415
416
				$this->trueWidth            = $obj->width;
417
				$this->width_units          = $obj->size_units;
418
				$this->trueHeight           = $obj->height;
419
				$this->height_units         = $obj->size_units;
420
				$this->trueDepth            = $obj->size;
421
				$this->depth_units          = $obj->size_units;
422
423
				$this->note_public          = $obj->note_public;
424
				$this->note_private         = $obj->note_private;
425
426
				// A denormalized value
427
				$this->trueSize           	= $obj->size."x".$obj->width."x".$obj->height;
428
				$this->size_units           = $obj->size_units;
429
430
				//Incoterms
431
				$this->fk_incoterms = $obj->fk_incoterms;
432
				$this->location_incoterms = $obj->location_incoterms;
433
				$this->libelle_incoterms = $obj->libelle_incoterms;
434
435
				$this->db->free($result);
436
437
				if ($this->statut == 0) $this->brouillon = 1;
438
439
				$file = $conf->reception->dir_output . "/" .get_exdir($this->id, 2, 0, 0, $this, 'reception') . "/" . $this->id.".pdf";
440
				$this->pdf_filename = $file;
441
442
				// Tracking url
443
				$this->getUrlTrackingStatus($obj->tracking_number);
444
445
				/*
446
				 * Thirparty
447
				 */
448
				$result=$this->fetch_thirdparty();
449
450
451
				// Retrieve all extrafields for reception
452
				// fetch optionals attributes and labels
453
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
454
				$extrafields=new ExtraFields($this->db);
455
				$extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
456
				$this->fetch_optionals($this->id,$extralabels);
457
458
				/*
459
				 * Lines
460
				 */
461
				$result=$this->fetch_lines();
462
				if ($result < 0)
463
				{
464
					return -3;
465
				}
466
467
				return 1;
468
			}
469
			else
470
			{
471
				dol_syslog(get_class($this).'::Fetch no reception found', LOG_ERR);
472
				$this->error='Delivery with id '.$id.' not found';
473
				return 0;
474
			}
475
		}
476
		else
477
		{
478
			$this->error=$this->db->error();
479
			return -1;
480
		}
481
	}
482
483
	/**
484
	 *  Validate object and update stock if option enabled
485
	 *
486
	 *  @param      User		$user       Object user that validate
487
     *  @param		int			$notrigger	1=Does not execute triggers, 0= execute triggers
488
	 *  @return     int						<0 if OK, >0 if KO
489
	 */
490
	function valid($user, $notrigger=0)
491
	{
492
		global $conf, $langs;
493
494
        require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
495
496
		dol_syslog(get_class($this)."::valid");
497
498
		// Protection
499
		if ($this->statut)
500
		{
501
			dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
502
			return 0;
503
		}
504
505
        if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->reception->creer))
506
       	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->reception->reception_advance->validate))))
507
		{
508
			$this->error='Permission denied';
509
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
510
			return -1;
511
		}
512
513
		$this->db->begin();
514
515
		$error = 0;
516
517
		// Define new ref
518
		$soc = new Societe($this->db);
519
		$soc->fetch($this->socid);
520
521
522
		// Define new ref
523
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
524
		{
525
			$numref = $this->getNextNumRef($soc);
526
		}
527
		else {
528
			$numref = $this->ref;
529
		}
530
531
        $this->newref = $numref;
532
533
		$now=dol_now();
534
535
		// Validate
536
		$sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
537
		$sql.= " ref='".$numref."'";
538
		$sql.= ", fk_statut = 1";
539
		$sql.= ", date_valid = '".$this->db->idate($now)."'";
540
		$sql.= ", fk_user_valid = ".$user->id;
541
		$sql.= " WHERE rowid = ".$this->id;
542
		dol_syslog(get_class($this)."::valid update reception", LOG_DEBUG);
543
		$resql=$this->db->query($sql);
544
		if (! $resql)
545
		{
546
			$this->error=$this->db->lasterror();
547
			$error++;
548
		}
549
550
		// If stock increment is done on reception (recommanded choice)
551
		if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_RECEPTION))
552
		{
553
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
554
555
			$langs->load("agenda");
556
557
			// Loop on each product line to add a stock movement
558
			// TODO in future, reception lines may not be linked to order line
559
			$sql = "SELECT cd.fk_product, cd.subprice,";
560
			$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
561
			$sql.= " ed.eatby, ed.sellby, ed.batch";
562
			$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
563
			$sql.= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
564
			$sql.= " WHERE ed.fk_reception = ".$this->id;
565
			$sql.= " AND cd.rowid = ed.fk_commandefourndet";
566
567
568
569
			dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
570
			$resql=$this->db->query($sql);
571
			if ($resql)
572
			{
573
				$cpt = $this->db->num_rows($resql);
574
				for ($i = 0; $i < $cpt; $i++)
575
				{
576
					$obj = $this->db->fetch_object($resql);
577
578
					$qty = $obj->qty;
579
580
					if ($qty <= 0) continue;
581
					dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
582
583
					//var_dump($this->lines[$i]);
584
					$mouvS = new MouvementStock($this->db);
585
					$mouvS->origin = &$this;
586
587
					if (empty($obj->batch))
588
					{
589
						// line without batch detail
590
591
						// 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.
592
						$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ReceptionValidatedInDolibarr",$numref));
593
						if ($result < 0) {
594
							$error++;
595
							$this->errors[]=$mouvS->error;
596
							$this->errors = array_merge($this->errors, $mouvS->errors);
597
							break;
598
						}
599
					}
600
					else
601
					{
602
						// line with batch detail
603
604
						// 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.
605
					    // Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
606
						$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ReceptionValidatedInDolibarr",$numref), $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch);
607
						if ($result < 0) {
608
							$error++;
609
							$this->errors[]=$mouvS->error;
610
							$this->errors = array_merge($this->errors, $mouvS->errors);
611
							break;
612
						}
613
					}
614
				}
615
			}
616
			else
617
			{
618
				$this->db->rollback();
619
				$this->error=$this->db->error();
620
				return -2;
621
			}
622
		}
623
624
		// Change status of order to "reception in process"
625
		$ret = $this->setStatut(4, $this->origin_id, 'commande_fournisseur');
626
627
        if (! $ret)
628
		{
629
		    $error++;
630
		}
631
632
		if (! $error && ! $notrigger)
633
		{
634
            // Call trigger
635
            $result=$this->call_trigger('RECEPTION_VALIDATE',$user);
636
            if ($result < 0) { $error++; }
637
            // End call triggers
638
		}
639
640
		if (! $error)
641
		{
642
            $this->oldref = $this->ref;
643
644
			// Rename directory if dir was a temporary ref
645
			if (preg_match('/^[\(]?PROV/i', $this->ref))
646
			{
647
				// On renomme repertoire ($this->ref = ancienne ref, $numfa = nouvelle ref)
648
				// in order not to lose the attached files
649
				$oldref = dol_sanitizeFileName($this->ref);
650
				$newref = dol_sanitizeFileName($numref);
651
				$dirsource = $conf->reception->dir_output.'/'.$oldref;
652
				$dirdest = $conf->reception->dir_output.'/'.$newref;
653
				if (file_exists($dirsource))
654
				{
655
					dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
656
657
					if (@rename($dirsource, $dirdest))
658
					{
659
					    dol_syslog("Rename ok");
660
                        // Rename docs starting with $oldref with $newref
661
                        $listoffiles=dol_dir_list($conf->reception->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
662
                        foreach($listoffiles as $fileentry)
663
                        {
664
                        	$dirsource=$fileentry['name'];
665
                        	$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
666
                        	$dirsource=$fileentry['path'].'/'.$dirsource;
667
                        	$dirdest=$fileentry['path'].'/'.$dirdest;
668
                        	@rename($dirsource, $dirdest);
669
                        }
670
					}
671
				}
672
			}
673
		}
674
675
		// Set new ref and current status
676
		if (! $error)
677
		{
678
			$this->ref = $numref;
679
			$this->statut = 1;
680
		}
681
682
		if (! $error)
683
		{
684
			$this->db->commit();
685
			return 1;
686
		}
687
		else
688
		{
689
			foreach($this->errors as $errmsg)
690
			{
691
	            dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
692
	            $this->error.=($this->error?', '.$errmsg:$errmsg);
693
			}
694
			$this->db->rollback();
695
			return -1*$error;
696
		}
697
	}
698
699
700
701
	/**
702
	 * Add an reception line.
703
	 * If STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS is set, you can add a reception line, with no stock source defined
704
	 * If STOCK_MUST_BE_ENOUGH_FOR_RECEPTION is not set, you can add a reception line, even if not enough into stock
705
	 *
706
	 * @param 	int			$entrepot_id		Id of warehouse
707
	 * @param 	int			$id					Id of source line (supplier order line)
708
	 * @param 	int			$qty				Quantity
709
	 * @param	array		$array_options		extrafields array
710
	 * @param	string		$comment				Comment for stock movement
711
	 * @param	date		$eatby					eat-by date
712
	 * @param	date		$sellby					sell-by date
713
	 * @param	string		$batch					Lot number
714
	 * @return	int							<0 if KO, >0 if OK
715
	 */
716
	function addline($entrepot_id, $id, $qty, $array_options=0, $comment='', $eatby='', $sellby='', $batch='')
717
	{
718
		global $conf, $langs, $user;
719
720
		$num = count($this->lines);
721
		$line = new CommandeFournisseurDispatch($this->db);
722
723
		$line->fk_entrepot = $entrepot_id;
724
		$line->fk_commandefourndet = $id;
725
		$line->qty = $qty;
726
727
		$supplierorderline = new CommandeFournisseurLigne($this->db);
728
		$supplierorderline->fetch($id);
729
730
		if (! empty($conf->stock->enabled) && ! empty($supplierorderline->fk_product))
731
		{
732
			$fk_product = $supplierorderline->fk_product;
733
734
			if (! ($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_RECEPTIONS))
735
			{
736
			    $langs->load("errors");
737
				$this->error=$langs->trans("ErrorWarehouseRequiredIntoReceptionLine");
738
				return -1;
739
			}
740
		}
741
742
		// extrafields
743
		if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
744
			$line->array_options = $array_options;
745
746
		$line->fk_product = $fk_product;
0 ignored issues
show
Bug introduced by
The variable $fk_product does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
747
		$line->fk_commande = $supplierorderline->fk_commande ;
748
		$line->fk_user = $user->id ;
749
		$line->comment = $comment;
750
		$line->batch = $batch;
751
		$line->eatby = $eatby;
0 ignored issues
show
Documentation Bug introduced by
It seems like $eatby can also be of type object<date>. However, the property $eatby is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
752
		$line->sellby = $sellby;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sellby can also be of type object<date>. However, the property $sellby is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
753
		$line->status=1;
754
		$line->fk_reception=$this->id;
755
756
		$this->lines[$num] = $line;
757
	}
758
759
760
    /**
761
     *  Update database
762
     *
763
     *  @param	User	$user        	User that modify
764
     *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
765
     *  @return int 			       	<0 if KO, >0 if OK
766
     */
767
    function update($user=null, $notrigger=0)
768
    {
769
    	global $conf;
770
		$error=0;
771
772
		// Clean parameters
773
774
		if (isset($this->ref)) $this->ref=trim($this->ref);
775
		if (isset($this->entity)) $this->entity=trim($this->entity);
776
		if (isset($this->ref_supplier)) $this->ref_supplier=trim($this->ref_supplier);
777
		if (isset($this->socid)) $this->socid=trim($this->socid);
778
		if (isset($this->fk_user_author)) $this->fk_user_author=trim($this->fk_user_author);
779
		if (isset($this->fk_user_valid)) $this->fk_user_valid=trim($this->fk_user_valid);
780
		if (isset($this->fk_delivery_address)) $this->fk_delivery_address=trim($this->fk_delivery_address);
0 ignored issues
show
Documentation Bug introduced by
The property $fk_delivery_address was declared of type integer, but trim($this->fk_delivery_address) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
781
		if (isset($this->shipping_method_id)) $this->shipping_method_id=trim($this->shipping_method_id);
0 ignored issues
show
Documentation Bug introduced by
The property $shipping_method_id was declared of type integer, but trim($this->shipping_method_id) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
782
		if (isset($this->tracking_number)) $this->tracking_number=trim($this->tracking_number);
783
		if (isset($this->statut)) $this->statut=(int) $this->statut;
784
		if (isset($this->trueDepth)) $this->trueDepth=trim($this->trueDepth);
785
		if (isset($this->trueWidth)) $this->trueWidth=trim($this->trueWidth);
786
		if (isset($this->trueHeight)) $this->trueHeight=trim($this->trueHeight);
787
		if (isset($this->size_units)) $this->size_units=trim($this->size_units);
788
		if (isset($this->weight_units)) $this->weight_units=trim($this->weight_units);
789
		if (isset($this->trueWeight)) $this->weight=trim($this->trueWeight);
790
		if (isset($this->note_private)) $this->note=trim($this->note_private);
791
		if (isset($this->note_public)) $this->note=trim($this->note_public);
792
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
793
794
795
		// Check parameters
796
		// Put here code to add control on parameters values
797
798
        // Update request
799
        $sql = "UPDATE ".MAIN_DB_PREFIX."reception SET";
800
801
		$sql.= " tms=".(dol_strlen($this->tms)!=0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
802
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
803
		$sql.= " ref_supplier=".(isset($this->ref_supplier)?"'".$this->db->escape($this->ref_supplier)."'":"null").",";
804
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
805
		$sql.= " date_creation=".(dol_strlen($this->date_creation)!=0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
806
		$sql.= " fk_user_author=".(isset($this->fk_user_author)?$this->fk_user_author:"null").",";
807
		$sql.= " date_valid=".(dol_strlen($this->date_valid)!=0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
808
		$sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->fk_user_valid:"null").",";
809
		$sql.= " date_reception=".(dol_strlen($this->date_reception)!=0 ? "'".$this->db->idate($this->date_reception)."'" : 'null').",";
810
		$sql.= " date_delivery=".(dol_strlen($this->date_delivery)!=0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
811
		$sql.= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0)?$this->shipping_method_id:"null").",";
812
		$sql.= " tracking_number=".(isset($this->tracking_number)?"'".$this->db->escape($this->tracking_number)."'":"null").",";
813
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
814
		$sql.= " height=".(($this->trueHeight != '')?$this->trueHeight:"null").",";
815
		$sql.= " width=".(($this->trueWidth != '')?$this->trueWidth:"null").",";
816
		$sql.= " size_units=".(isset($this->size_units)?$this->size_units:"null").",";
817
		$sql.= " size=".(($this->trueDepth != '')?$this->trueDepth:"null").",";
818
		$sql.= " weight_units=".(isset($this->weight_units)?$this->weight_units:"null").",";
819
		$sql.= " weight=".(($this->trueWeight != '')?$this->trueWeight:"null").",";
820
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
821
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
822
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
823
		$sql.= " entity=".$conf->entity;
824
825
        $sql.= " WHERE rowid=".$this->id;
826
827
		$this->db->begin();
828
829
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
830
        $resql = $this->db->query($sql);
831
    	if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
832
833
		if (! $error)
834
		{
835
			if (! $notrigger)
836
			{
837
                // Call trigger
838
                $result=$this->call_trigger('RECEPTION_MODIFY',$user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 767 can be null; however, CommonObject::call_trigger() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
839
                if ($result < 0) { $error++; }
840
                // End call triggers
841
	    	}
842
		}
843
844
        // Commit or rollback
845
		if ($error)
846
		{
847
			foreach($this->errors as $errmsg)
848
			{
849
	            dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
850
	            $this->error.=($this->error?', '.$errmsg:$errmsg);
851
			}
852
			$this->db->rollback();
853
			return -1*$error;
854
		}
855
		else
856
		{
857
			$this->db->commit();
858
			return 1;
859
		}
860
	}
861
862
	/**
863
	 * 	Delete reception.
864
	 *
865
	 *	@param	User	$user	Object user
866
	 * 	@return	int				>0 if OK, 0 if deletion done but failed to delete files, <0 if KO
867
	 */
868
	function delete(User $user)
869
	{
870
		global $conf, $langs, $user;
871
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
872
873
		$error=0;
874
		$this->error='';
875
876
877
		$this->db->begin();
878
879
		// Stock control
880
		if ($conf->stock->enabled && $conf->global->STOCK_CALCULATE_ON_RECEPTION && $this->statut > 0)
881
		{
882
			require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
883
884
			$langs->load("agenda");
885
886
			// Loop on each product line to add a stock movement
887
			$sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.eatby, ed.sellby, ed.batch, ed.rowid as commande_fournisseur_dispatch_id";
888
			$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
889
			$sql.= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
890
			$sql.= " WHERE ed.fk_reception = ".$this->id;
891
			$sql.= " AND cd.rowid = ed.fk_commandefourndet";
892
893
			dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
894
			$resql=$this->db->query($sql);
895
			if ($resql)
896
			{
897
				$cpt = $this->db->num_rows($resql);
898
				for ($i = 0; $i < $cpt; $i++)
899
				{
900
					dol_syslog(get_class($this)."::delete movement index ".$i);
901
					$obj = $this->db->fetch_object($resql);
902
903
					$mouvS = new MouvementStock($this->db);
904
					// we do not log origin because it will be deleted
905
					$mouvS->origin = null;
906
907
					$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
908
				}
909
			}
910
			else
911
			{
912
				$error++;$this->errors[]="Error ".$this->db->lasterror();
913
			}
914
		}
915
916
		if (! $error)
917
		{
918
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
919
			$sql.= " WHERE fk_reception = ".$this->id;
920
921
			if ( $this->db->query($sql) )
922
			{
923
				// Delete linked object
924
				$res = $this->deleteObjectLinked();
925
				if ($res < 0) $error++;
926
927
				if (! $error)
928
				{
929
					$sql = "DELETE FROM ".MAIN_DB_PREFIX."reception";
930
					$sql.= " WHERE rowid = ".$this->id;
931
932
					if ($this->db->query($sql))
933
					{
934
						// Call trigger
935
						$result=$this->call_trigger('RECEPTION_DELETE',$user);
936
						if ($result < 0) { $error++; }
937
						// End call triggers
938
939
						if (! empty($this->origin) && $this->origin_id > 0)
940
						{
941
						    $this->fetch_origin();
942
						    $origin=$this->origin;
943
						    if ($this->$origin->statut == 4)     // If order source of reception is "partially received"
944
						    {
945
                                // Check if there is no more reception. If not, we can move back status of order to "validated" instead of "reception in progress"
946
						        $this->$origin->loadReceptions();
947
						        //var_dump($this->$origin->receptions);exit;
948
						        if (count($this->$origin->receptions) <= 0)
949
						        {
950
                                    $this->$origin->setStatut(3); // ordered
951
						        }
952
						    }
953
						}
954
955
						if (! $error)
956
						{
957
							$this->db->commit();
958
959
							// We delete PDFs
960
							$ref = dol_sanitizeFileName($this->ref);
961
							if (! empty($conf->reception->dir_output))
962
							{
963
								$dir = $conf->reception->dir_output . '/' . $ref ;
964
								$file = $dir . '/' . $ref . '.pdf';
965
								if (file_exists($file))
966
								{
967
									if (! dol_delete_file($file))
968
									{
969
										return 0;
970
									}
971
								}
972
								if (file_exists($dir))
973
								{
974
									if (!dol_delete_dir_recursive($dir))
975
									{
976
										$this->error=$langs->trans("ErrorCanNotDeleteDir",$dir);
977
										return 0;
978
									}
979
								}
980
							}
981
982
							return 1;
983
						}
984
						else
985
						{
986
							$this->db->rollback();
987
							return -1;
988
						}
989
					}
990
					else
991
					{
992
						$this->error=$this->db->lasterror()." - sql=$sql";
993
						$this->db->rollback();
994
						return -3;
995
					}
996
				}
997
				else
998
				{
999
					$this->error=$this->db->lasterror()." - sql=$sql";
1000
					$this->db->rollback();
1001
					return -2;
1002
				}
1003
			}
1004
			else
1005
			{
1006
				$this->error=$this->db->lasterror()." - sql=$sql";
1007
				$this->db->rollback();
1008
				return -1;
1009
			}
1010
		}
1011
		else
1012
		{
1013
			$this->db->rollback();
1014
			return -1;
1015
		}
1016
	}
1017
1018
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1019
	/**
1020
	 *	Load lines
1021
	 *
1022
	 *	@return	int		>0 if OK, Otherwise if KO
1023
	 */
1024
	function fetch_lines()
1025
	{
1026
		// phpcs:enable
1027
		global $db;
1028
		dol_include_once('/fourn/class/fournisseur.commande.dispatch.class.php');
1029
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch WHERE fk_reception='.$this->id;
1030
		$resql = $db->query($sql);
1031
1032
		if(!empty($resql)){
1033
			$this->lines = array();
1034
			while ($obj = $resql->fetch_object()){
1035
				$line = new CommandeFournisseurDispatch($db);
1036
				$line->fetch($obj->rowid);
1037
				$line->fetch_product();
1038
				$sql_commfourndet = 'SELECT qty, ref,  label, tva_tx, vat_src_code, subprice, multicurrency_subprice, remise_percent FROM llx_commande_fournisseurdet WHERE rowid='.$line->fk_commandefourndet;
1039
				$resql_commfourndet = $db->query($sql_commfourndet);
1040
				if(!empty($resql_commfourndet)){
1041
					$obj = $db->fetch_object($resql_commfourndet);
1042
					$line->qty_asked = $obj->qty;
1043
					$line->description = $line->comment;
1044
					$line->desc =  $line->comment;
1045
					$line->tva_tx = $obj->tva_tx;
1046
					$line->vat_src_code = $obj->vat_src_code;
1047
					$line->subprice = $obj->subprice;
1048
					$line->multicurrency_subprice = $obj->multicurrency_subprice;
1049
					$line->remise_percent = $obj->remise_percent;
1050
					$line->label = !empty($obj->label)?$obj->label:$line->product->label;
1051
					$line->ref_supplier = $obj->ref;
1052
				}else {
1053
					$line->qty_asked = 0;
1054
					$line->description = '';
1055
					$line->label = $obj->label;
1056
				}
1057
1058
				$pu_ht=($line->subprice*$line->qty)*(100-$line->remise_percent)/100;
1059
				$tva = $pu_ht*$line->tva_tx/100;
1060
				$this->total_ht += $pu_ht;
1061
				$this->total_tva += $pu_ht*$line->tva_tx/100;
1062
1063
				$this->total_ttc += $pu_ht+$tva;
1064
1065
1066
				$this->lines[]=$line;
1067
			}
1068
1069
			return 1;
1070
		}
1071
		else {
1072
			return -1;
1073
		}
1074
	}
1075
1076
	/**
1077
     *	Return clicable link of object (with eventually picto)
1078
     *
1079
     *	@param      int			$withpicto      Add picto into link
1080
     *	@param      int			$option         Where point the link
1081
     *	@param      int			$max          	Max length to show
1082
     *	@param      int			$short			Use short labels
1083
     *  @param      int         $notooltip      1=No tooltip
1084
     *	@return     string          			String with URL
1085
     */
1086
	function getNomUrl($withpicto=0,$option=0,$max=0,$short=0,$notooltip=0)
1087
	{
1088
		global $langs;
1089
		$result='';
1090
        $label = '<u>' . $langs->trans("ShowReception") . '</u>';
1091
        $label .= '<br><b>' . $langs->trans('Ref') . ':</b> '.$this->ref;
1092
        $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.($this->ref_supplier ? $this->ref_supplier : $this->ref_client);
1093
1094
		$url = DOL_URL_ROOT.'/reception/card.php?id='.$this->id;
1095
1096
		if ($short) return $url;
1097
1098
		$linkclose='';
1099
		if (empty($notooltip))
1100
		{
1101
		    if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
0 ignored issues
show
Bug introduced by
The variable $conf does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1102
		    {
1103
		        $label=$langs->trans("ShowReception");
1104
		        $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1105
		    }
1106
		    $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1107
		    $linkclose.=' class="classfortooltip"';
1108
		}
1109
1110
        $linkstart = '<a href="'.$url.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
1111
		$linkend='</a>';
1112
1113
		$picto='sending';
1114
1115
		if ($withpicto) $result.=($linkstart.img_object(($notooltip?'':$label), $picto, ($notooltip?'':'class="classfortooltip"'), 0, 0, $notooltip?0:1).$linkend);
1116
		if ($withpicto && $withpicto != 2) $result.=' ';
1117
		$result.=$linkstart.$this->ref.$linkend;
1118
		return $result;
1119
	}
1120
1121
	/**
1122
     *	Return status label
1123
     *
1124
     *	@param      int		$mode      	0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
1125
     *	@return     string      		Libelle
1126
     */
1127
	function getLibStatut($mode=0)
1128
	{
1129
		return $this->LibStatut($this->statut,$mode);
1130
	}
1131
1132
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1133
	/**
1134
	 * Return label of a status
1135
	 *
1136
	 * @param      int		$statut		Id statut
1137
	 * @param      int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
1138
	 * @return     string				Label of status
1139
	 */
1140
	function LibStatut($statut,$mode)
1141
	{
1142
		// phpcs:enable
1143
		global $langs;
1144
1145
		if ($mode==0)
1146
		{
1147
			if ($statut==0) return $langs->trans($this->statuts[$statut]);
1148
			if ($statut==1)  return $langs->trans($this->statuts[$statut]);
1149
			if ($statut==2)  return $langs->trans($this->statuts[$statut]);
1150
		}
1151
		if ($mode==1)
1152
		{
1153
			if ($statut==0) return $langs->trans('StatusReceptionDraftShort');
1154
			if ($statut==1) return $langs->trans('StatusReceptionValidatedShort');
1155
			if ($statut==2) return $langs->trans('StatusReceptionProcessedShort');
1156
		}
1157
		if ($mode == 3)
1158
		{
1159
			if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]),'statut0');
1160
			if ($statut==1) return img_picto($langs->trans($this->statuts[$statut]),'statut4');
1161
			if ($statut==2) return img_picto($langs->trans('StatusReceptionProcessed'),'statut6');
1162
		}
1163
		if ($mode == 4)
1164
		{
1165
			if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]),'statut0').' '.$langs->trans($this->statuts[$statut]);
1166
			if ($statut==1) return img_picto($langs->trans($this->statuts[$statut]),'statut4').' '.$langs->trans($this->statuts[$statut]);
1167
			if ($statut==2) return img_picto($langs->trans('StatusReceptionProcessed'),'statut6').' '.$langs->trans('StatusReceptionProcessed');
1168
		}
1169
		if ($mode == 5)
1170
		{
1171
			if ($statut==0) return $langs->trans('StatusReceptionDraftShort').' '.img_picto($langs->trans($this->statuts[$statut]),'statut0');
1172
			if ($statut==1) return $langs->trans('StatusReceptionValidatedShort').' '.img_picto($langs->trans($this->statuts[$statut]),'statut4');
1173
			if ($statut==2) return $langs->trans('StatusReceptionProcessedShort').' '.img_picto($langs->trans('StatusReceptionProcessedShort'),'statut6');
1174
		}
1175
	}
1176
1177
	/**
1178
     *  Initialise an instance with random values.
1179
     *  Used to build previews or test instances.
1180
     *	id must be 0 if object instance is a specimen.
1181
     *
1182
     *  @return	void
1183
	 */
1184
	function initAsSpecimen()
1185
	{
1186
		global $langs;
1187
		dol_include_once('/fourn/class/fournisseur.commande.dispatch.class.php');
1188
		$now=dol_now();
1189
1190
		dol_syslog(get_class($this)."::initAsSpecimen");
1191
1192
        // Load array of products prodids
1193
		$num_prods = 0;
1194
		$prodids = array();
1195
		$sql = "SELECT rowid";
1196
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
1197
		$sql.= " WHERE entity IN (".getEntity('product').")";
1198
		$resql = $this->db->query($sql);
1199
		if ($resql)
1200
		{
1201
			$num_prods = $this->db->num_rows($resql);
1202
			$i = 0;
1203
			while ($i < $num_prods)
1204
			{
1205
				$i++;
1206
				$row = $this->db->fetch_row($resql);
1207
				$prodids[$i] = $row[0];
1208
			}
1209
		}
1210
1211
		$order=new Commande($this->db);
1212
		$order->initAsSpecimen();
1213
1214
		// Initialise parametres
1215
		$this->id=0;
1216
		$this->ref = 'SPECIMEN';
1217
		$this->specimen=1;
1218
		$this->statut               = 1;
1219
		$this->livraison_id         = 0;
1220
		$this->date                 = $now;
1221
		$this->date_creation        = $now;
1222
		$this->date_valid           = $now;
1223
		$this->date_delivery        = $now;
1224
		$this->date_reception      = $now + 24*3600;
1225
1226
		$this->entrepot_id          = 0;
1227
		$this->fk_delivery_address  = 0;
1228
		$this->socid                = 1;
1229
1230
		$this->commande_id          = 0;
1231
		$this->commande             = $order;
1232
1233
        $this->origin_id            = 1;
1234
        $this->origin               = 'commande';
1235
1236
        $this->note_private			= 'Private note';
1237
        $this->note_public			= 'Public note';
1238
1239
		$nbp = 5;
1240
		$xnbp = 0;
1241
		while ($xnbp < $nbp)
1242
		{
1243
			$line=new CommandeFournisseurDispatch($this->db);
1244
			$line->desc=$langs->trans("Description")." ".$xnbp;
1245
			$line->libelle=$langs->trans("Description")." ".$xnbp;
1246
			$line->qty=10;
1247
1248
			$line->fk_product=$this->commande->lines[$xnbp]->fk_product;
1249
1250
			$this->lines[]=$line;
1251
			$xnbp++;
1252
		}
1253
	}
1254
1255
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1256
	/**
1257
	 *	Set the planned delivery date
1258
	 *
1259
	 *	@param      User			$user        		Objet utilisateur qui modifie
1260
	 *	@param      timestamp		$date_livraison     Date de livraison
1261
	 *	@return     int         						<0 if KO, >0 if OK
1262
	 */
1263
	function set_date_livraison($user, $date_livraison)
1264
	{
1265
		// phpcs:enable
1266
		if ($user->rights->reception->creer)
1267
		{
1268
			$sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1269
			$sql.= " SET date_delivery = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
1270
			$sql.= " WHERE rowid = ".$this->id;
1271
1272
			dol_syslog(get_class($this)."::set_date_livraison", LOG_DEBUG);
1273
			$resql=$this->db->query($sql);
1274
			if ($resql)
1275
			{
1276
				$this->date_delivery = $date_livraison;
1277
				return 1;
1278
			}
1279
			else
1280
			{
1281
				$this->error=$this->db->error();
1282
				return -1;
1283
			}
1284
		}
1285
		else
1286
		{
1287
			return -2;
1288
		}
1289
	}
1290
1291
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1292
	/**
1293
	 *	Fetch deliveries method and return an array. Load array this->meths(rowid=>label).
1294
	 *
1295
	 * 	@return	void
1296
	 */
1297
	function fetch_delivery_methods()
1298
	{
1299
		// phpcs:enable
1300
		global $langs;
1301
		$this->meths = array();
1302
1303
		$sql = "SELECT em.rowid, em.code, em.libelle";
1304
		$sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1305
		$sql.= " WHERE em.active = 1";
1306
		$sql.= " ORDER BY em.libelle ASC";
1307
1308
		$resql = $this->db->query($sql);
1309
		if ($resql)
1310
		{
1311
			while ($obj = $this->db->fetch_object($resql))
1312
			{
1313
				$label=$langs->trans('ReceptionMethod'.$obj->code);
1314
				$this->meths[$obj->rowid] = ($label != 'ReceptionMethod'.$obj->code?$label:$obj->libelle);
1315
			}
1316
		}
1317
	}
1318
1319
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1320
    /**
1321
     *  Fetch all deliveries method and return an array. Load array this->listmeths.
1322
     *
1323
     *  @param  int      $id     only this carrier, all if none
1324
     *  @return void
1325
     */
1326
    function list_delivery_methods($id='')
1327
    {
1328
		// phpcs:enable
1329
        global $langs;
1330
1331
        $this->listmeths = array();
1332
        $i=0;
1333
1334
        $sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1335
        $sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1336
        if ($id!='') $sql.= " WHERE em.rowid=".$id;
1337
1338
        $resql = $this->db->query($sql);
1339
        if ($resql)
1340
        {
1341
            while ($obj = $this->db->fetch_object($resql))
1342
            {
1343
                $this->listmeths[$i]['rowid'] = $obj->rowid;
1344
                $this->listmeths[$i]['code'] = $obj->code;
1345
                $label=$langs->trans('ReceptionMethod'.$obj->code);
1346
                $this->listmeths[$i]['libelle'] = ($label != 'ReceptionMethod'.$obj->code?$label:$obj->libelle);
1347
                $this->listmeths[$i]['description'] = $obj->description;
1348
                $this->listmeths[$i]['tracking'] = $obj->tracking;
1349
                $this->listmeths[$i]['active'] = $obj->active;
1350
                $i++;
1351
            }
1352
        }
1353
    }
1354
1355
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1356
    /**
1357
     *  Update/create delivery method.
1358
     *
1359
     *  @param	string      $id     id method to activate
1360
     *
1361
     *  @return void
1362
     */
1363
    function update_delivery_method($id='')
1364
    {
1365
		// phpcs:enable
1366
        if ($id=='')
1367
        {
1368
            $sql = "INSERT INTO ".MAIN_DB_PREFIX."c_shipment_mode (code, libelle, description, tracking)";
1369
            $sql.=" VALUES ('".$this->update['code']."','".$this->update['libelle']."','".$this->update['description']."','".$this->update['tracking']."')";
1370
            $resql = $this->db->query($sql);
1371
        }
1372
        else
1373
        {
1374
            $sql = "UPDATE ".MAIN_DB_PREFIX."c_shipment_mode SET";
1375
            $sql.= " code='".$this->db->escape($this->update['code'])."'";
1376
            $sql.= ",libelle='".$this->db->escape($this->update['libelle'])."'";
1377
            $sql.= ",description='".$this->db->escape($this->update['description'])."'";
1378
            $sql.= ",tracking='".$this->db->escape($this->update['tracking'])."'";
1379
            $sql.= " WHERE rowid=".$id;
1380
            $resql = $this->db->query($sql);
1381
        }
1382
        if ($resql < 0) dol_print_error($this->db,'');
1383
    }
1384
1385
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1386
    /**
1387
     *  Activate delivery method.
1388
     *
1389
     *  @param      int      $id     id method to activate
1390
     *
1391
     *  @return void
1392
     */
1393
    function activ_delivery_method($id)
1394
    {
1395
		// phpcs:enable
1396
        $sql = 'UPDATE '.MAIN_DB_PREFIX.'c_shipment_mode SET active=1';
1397
        $sql.= ' WHERE rowid='.$id;
1398
1399
        $resql = $this->db->query($sql);
1400
    }
1401
1402
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1403
    /**
1404
     *  DesActivate delivery method.
1405
     *
1406
     *  @param      int      $id     id method to desactivate
1407
     *
1408
     *  @return void
1409
     */
1410
    function disable_delivery_method($id)
1411
    {
1412
		// phpcs:enable
1413
        $sql = 'UPDATE '.MAIN_DB_PREFIX.'c_shipment_mode SET active=0';
1414
        $sql.= ' WHERE rowid='.$id;
1415
1416
        $resql = $this->db->query($sql);
1417
    }
1418
1419
1420
	/**
1421
	 * Forge an set tracking url
1422
	 *
1423
	 * @param	string	$value		Value
1424
	 * @return	void
1425
	 */
1426
	function getUrlTrackingStatus($value='')
1427
	{
1428
		if (! empty($this->shipping_method_id))
1429
		{
1430
			$sql = "SELECT em.code, em.tracking";
1431
			$sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1432
			$sql.= " WHERE em.rowid = ".$this->shipping_method_id;
1433
1434
			$resql = $this->db->query($sql);
1435
			if ($resql)
1436
			{
1437
				if ($obj = $this->db->fetch_object($resql))
1438
				{
1439
					$tracking = $obj->tracking;
1440
				}
1441
			}
1442
		}
1443
1444
		if (!empty($tracking) && !empty($value))
1445
		{
1446
			$url = str_replace('{TRACKID}', $value, $tracking);
1447
			$this->tracking_url = sprintf('<a target="_blank" href="%s">'.($value?$value:'url').'</a>',$url,$url);
1448
		}
1449
		else
1450
		{
1451
			$this->tracking_url = $value;
1452
		}
1453
	}
1454
1455
	/**
1456
	 *	Classify the reception as closed.
1457
	 *
1458
	 *	@return     int     <0 if KO, >0 if OK
1459
	 */
1460
	function setClosed()
1461
	{
1462
		global $conf,$langs,$user;
1463
1464
		$error=0;
1465
1466
		$this->db->begin();
1467
1468
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut='.self::STATUS_CLOSED;
1469
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
1470
1471
		$resql=$this->db->query($sql);
1472
		if ($resql)
1473
		{
1474
			// Set order billed if 100% of order is received (qty in reception lines match qty in order lines)
1475
			if ($this->origin == 'order_supplier' && $this->origin_id > 0)
1476
			{
1477
				$order = new CommandeFournisseur($this->db);
1478
				$order->fetch($this->origin_id);
1479
1480
				$order->loadReceptions(self::STATUS_CLOSED);		// Fill $order->receptions = array(orderlineid => qty)
1481
1482
				$receptions_match_order = 1;
1483
				foreach($order->lines as $line)
1484
				{
1485
					$lineid = $line->id;
1486
					$qty = $line->qty;
1487
					if (($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) && $order->receptions[$lineid] < $qty)
1488
					{
1489
						$receptions_match_order = 0;
1490
						$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';
1491
						dol_syslog($text);
1492
						break;
1493
					}
1494
				}
1495
				if ($receptions_match_order)
1496
				{
1497
					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');
1498
					$order->Livraison($user, dol_now(), 'tot', 'Reception '.$this->ref);
1499
				}
1500
			}
1501
1502
			$this->statut=self::STATUS_CLOSED;
1503
1504
1505
			// If stock increment is done on closing
1506
			if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE))
1507
			{
1508
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1509
1510
				$langs->load("agenda");
1511
1512
				// Loop on each product line to add a stock movement
1513
				// TODO possibilite de receptionner a partir d'une propale ou autre origine ?
1514
				$sql = "SELECT cd.fk_product, cd.subprice,";
1515
				$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
1516
				$sql.= " ed.eatby, ed.sellby, ed.batch";
1517
				$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1518
				$sql.= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1519
				$sql.= " WHERE ed.fk_reception = ".$this->id;
1520
				$sql.= " AND cd.rowid = ed.fk_commandefourndet";
1521
1522
				dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1523
				$resql=$this->db->query($sql);
1524
1525
				if ($resql)
1526
				{
1527
					$cpt = $this->db->num_rows($resql);
1528
					for ($i = 0; $i < $cpt; $i++)
1529
					{
1530
						$obj = $this->db->fetch_object($resql);
1531
1532
						$qty = $obj->qty;
1533
1534
						if ($qty <= 0) continue;
1535
						dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1536
1537
						$mouvS = new MouvementStock($this->db);
1538
						$mouvS->origin = &$this;
1539
1540
						if (empty($obj->batch))
1541
						{
1542
							// line without batch detail
1543
1544
							// 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
1545
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ReceptionClassifyClosedInDolibarr",$numref));
0 ignored issues
show
Bug introduced by
The variable $numref does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1546
							if ($result < 0) {
1547
							    $this->error = $mouvS->error;
1548
							    $this->errors = $mouvS->errors;
1549
								$error++; break;
1550
							}
1551
						}
1552
						else
1553
						{
1554
							// line with batch detail
1555
1556
							// 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
1557
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ReceptionClassifyClosedInDolibarr",$numref),  $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch);
1558
1559
							if ($result < 0) {
1560
							    $this->error = $mouvS->error;
1561
							    $this->errors = $mouvS->errors;
1562
							    $error++; break;
1563
							}
1564
						}
1565
					}
1566
				}
1567
				else
1568
				{
1569
					$this->error=$this->db->lasterror();
1570
					$error++;
1571
				}
1572
			}
1573
1574
			// Call trigger
1575
			if (! $error)
1576
			{
1577
    			$result=$this->call_trigger('RECEPTION_CLOSED',$user);
1578
    			if ($result < 0) {
1579
    			    $error++;
1580
    			}
1581
			}
1582
		}
1583
		else
1584
		{
1585
			dol_print_error($this->db);
1586
            $error++;
1587
		}
1588
1589
		if (! $error)
1590
		{
1591
		    $this->db->commit();
1592
		    return 1;
1593
		}
1594
		else
1595
		{
1596
		    $this->db->rollback();
1597
		    return -1;
1598
		}
1599
	}
1600
1601
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1602
	/**
1603
	 *	Classify the reception as invoiced (used when WORKFLOW_BILL_ON_RECEPTION is on)
1604
	 *
1605
	 *	@return     int     <0 if ko, >0 if ok
1606
	 */
1607
	function set_billed()
1608
	{
1609
		// phpcs:enable
1610
	    global $user;
1611
		$error=0;
1612
1613
		$this->db->begin();
1614
1615
		$this->setClosed();
1616
1617
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET  billed=1';
1618
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
1619
1620
		$resql=$this->db->query($sql);
1621
		if ($resql)
1622
		{
1623
			$this->statut=2;
1624
			$this->billed=1;
1625
1626
			// Call trigger
1627
			$result=$this->call_trigger('RECEPTION_BILLED',$user);
1628
			if ($result < 0) {
1629
				$error++;
1630
			}
1631
		} else {
1632
			$error++;
1633
			$this->errors[]=$this->db->lasterror;
1634
		}
1635
1636
		if (empty($error)) {
1637
			$this->db->commit();
1638
			return 1;
1639
		}
1640
		else
1641
		{
1642
			$this->db->rollback();
1643
			return -1;
1644
		}
1645
	}
1646
1647
	/**
1648
	 *	Classify the reception as validated/opened
1649
	 *
1650
	 *	@return     int     <0 if ko, >0 if ok
1651
	 */
1652
	function reOpen()
1653
	{
1654
		global $conf,$langs,$user;
1655
1656
		$error=0;
1657
1658
		$this->db->begin();
1659
1660
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'reception SET fk_statut=1, billed=0';
1661
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
1662
1663
		$resql=$this->db->query($sql);
1664
		if ($resql)
1665
		{
1666
			$this->statut=1;
1667
			$this->billed=0;
1668
1669
			// If stock increment is done on closing
1670
			if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE))
1671
			{
1672
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1673
				$numref = $this->ref;
1674
				$langs->load("agenda");
1675
1676
				// Loop on each product line to add a stock movement
1677
				// TODO possibilite de receptionner a partir d'une propale ou autre origine
1678
				$sql = "SELECT ed.fk_product, cd.subprice,";
1679
				$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
1680
				$sql.= " ed.eatby, ed.sellby, ed.batch";
1681
				$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1682
				$sql.= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1683
				$sql.= " WHERE ed.fk_reception = ".$this->id;
1684
				$sql.= " AND cd.rowid = ed.fk_commandefourndet";
1685
1686
				dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1687
				$resql=$this->db->query($sql);
1688
				if ($resql)
1689
				{
1690
					$cpt = $this->db->num_rows($resql);
1691
					for ($i = 0; $i < $cpt; $i++)
1692
					{
1693
						$obj = $this->db->fetch_object($resql);
1694
1695
						$qty = $obj->qty;
1696
1697
						if ($qty <= 0) continue;
1698
1699
						dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid);
1700
1701
						//var_dump($this->lines[$i]);
1702
						$mouvS = new MouvementStock($this->db);
1703
						$mouvS->origin = &$this;
1704
1705
						if (empty($obj->batch))
1706
						{
1707
							// line without batch detail
1708
1709
							// We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
1710
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ReceptionUnClassifyCloseddInDolibarr",$numref));
1711
							if ($result < 0) {
1712
							    $this->error = $mouvS->error;
1713
							    $this->errors = $mouvS->errors;
1714
								$error++; break;
1715
							}
1716
						}
1717
						else
1718
						{
1719
							// line with batch detail
1720
1721
							// 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
1722
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ReceptionUnClassifyCloseddInDolibarr",$numref),  $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
1723
							if ($result < 0) {
1724
							    $this->error = $mouvS->error;
1725
							    $this->errors = $mouvS->errors;
1726
							    $error++; break;
1727
							}
1728
						}
1729
					}
1730
				}
1731
				else
1732
				{
1733
					$this->error=$this->db->lasterror();
1734
					$error++;
1735
				}
1736
			}
1737
1738
			if (! $error)
1739
			{
1740
    			// Call trigger
1741
    			$result=$this->call_trigger('RECEPTION_REOPEN',$user);
1742
    			if ($result < 0) {
1743
    				$error++;
1744
    			}
1745
   			}
1746
1747
			if($this->origin == 'order_supplier'){
1748
				$commande = new CommandeFournisseur($this->db);
1749
				$commande->fetch($this->origin_id);
1750
				$commande->setStatus($user,4);
1751
			}
1752
		} else {
1753
			$error++;
1754
			$this->errors[]=$this->db->lasterror();
1755
		}
1756
1757
		if (! $error)
1758
		{
1759
			$this->db->commit();
1760
			return 1;
1761
		}
1762
		else
1763
		{
1764
			$this->db->rollback();
1765
			return -1;
1766
		}
1767
	}
1768
1769
1770
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1771
	 /**
1772
     *	Set draft status
1773
     *
1774
     *	@param	User	$user			Object user that modify
1775
     *	@return	int						<0 if KO, >0 if OK
1776
     */
1777
    function set_draft($user)
1778
    {
1779
		// phpcs:enable
1780
        global $conf,$langs;
1781
1782
        $error=0;
1783
1784
        // Protection
1785
        if ($this->statut <= self::STATUS_DRAFT)
1786
        {
1787
            return 0;
1788
        }
1789
1790
        if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->reception->creer))
1791
       	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->reception->reception_advance->validate))))
1792
        {
1793
            $this->error='Permission denied';
1794
            return -1;
1795
        }
1796
1797
        $this->db->begin();
1798
1799
        $sql = "UPDATE ".MAIN_DB_PREFIX."reception";
1800
        $sql.= " SET fk_statut = ".self::STATUS_DRAFT;
1801
        $sql.= " WHERE rowid = ".$this->id;
1802
1803
        dol_syslog(get_class($this)."::set_draft", LOG_DEBUG);
1804
        if ($this->db->query($sql))
1805
        {
1806
            // If stock increment is done on closing
1807
			if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_RECEPTION))
1808
			{
1809
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1810
1811
				$langs->load("agenda");
1812
1813
				// Loop on each product line to add a stock movement
1814
				// TODO possibilite de receptionner a partir d'une propale ou autre origine
1815
				$sql = "SELECT cd.fk_product, cd.subprice,";
1816
				$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
1817
				$sql.= " ed.eatby, ed.sellby, ed.batch";
1818
				$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd,";
1819
				$sql.= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as ed";
1820
				$sql.= " WHERE ed.fk_reception = ".$this->id;
1821
				$sql.= " AND cd.rowid = ed.fk_commandefourndet";
1822
1823
				dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
1824
				$resql=$this->db->query($sql);
1825
				if ($resql)
1826
				{
1827
					$cpt = $this->db->num_rows($resql);
1828
					for ($i = 0; $i < $cpt; $i++)
1829
					{
1830
						$obj = $this->db->fetch_object($resql);
1831
1832
						$qty = $obj->qty;
1833
1834
1835
						if ($qty <= 0) continue;
1836
						dol_syslog(get_class($this)."::reopen reception movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
1837
1838
						//var_dump($this->lines[$i]);
1839
						$mouvS = new MouvementStock($this->db);
1840
						$mouvS->origin = &$this;
1841
1842
						if (empty($obj->batch))
1843
						{
1844
							// line without batch detail
1845
1846
							// 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
1847
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ReceptionBackToDraftInDolibarr",$this->ref));
1848
							if ($result < 0) {
1849
							    $this->error = $mouvS->error;
1850
							    $this->errors = $mouvS->errors;
1851
								$error++;
1852
								break;
1853
							}
1854
						}
1855
						else
1856
						{
1857
							// line with batch detail
1858
1859
							// 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
1860
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ReceptionBackToDraftInDolibarr",$this->ref),  $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch);
1861
							if ($result < 0) {
1862
							    $this->error = $mouvS->error;
1863
							    $this->errors = $mouvS->errors;
1864
							    $error++; break;
1865
							}
1866
						}
1867
					}
1868
				}
1869
				else
1870
				{
1871
					$this->error=$this->db->lasterror();
1872
					$error++;
1873
				}
1874
			}
1875
1876
            if (!$error) {
1877
            	// Call trigger
1878
            	$result=$this->call_trigger('RECEPTION_UNVALIDATE',$user);
1879
            	if ($result < 0) $error++;
1880
            }
1881
			if ($this->origin == 'order_supplier')
1882
			{
1883
				if (!empty($this->origin) && $this->origin_id > 0)
1884
				{
1885
					$this->fetch_origin();
1886
					$origin = $this->origin;
1887
					if ($this->$origin->statut == 4)  // If order source of reception is "partially received"
1888
					{
1889
						// Check if there is no more reception validated.
1890
						$this->$origin->fetchObjectLinked();
1891
						$setStatut = 1;
1892
						if (!empty($this->$origin->linkedObjects['reception']))
1893
						{
1894
							foreach ($this->$origin->linkedObjects['reception'] as $rcption)
1895
							{
1896
								if ($rcption->statut > 0)
1897
								{
1898
									$setStatut = 0;
1899
									break;
1900
								}
1901
							}
1902
							//var_dump($this->$origin->receptions);exit;
1903
							if ($setStatut)
1904
							{
1905
								$this->$origin->setStatut(3); // ordered
1906
							}
1907
						}
1908
					}
1909
				}
1910
			}
1911
1912
			if (!$error) {
1913
           		$this->statut=self::STATUS_DRAFT;
1914
            	$this->db->commit();
1915
            	return 1;
1916
            }else {
1917
            	$this->db->rollback();
1918
            	return -1;
1919
            }
1920
        }
1921
        else
1922
        {
1923
            $this->error=$this->db->error();
1924
            $this->db->rollback();
1925
            return -1;
1926
        }
1927
    }
1928
1929
	/**
1930
	 *  Create a document onto disk according to template module.
1931
	 *
1932
	 *  @param	    string		$modele			Force the model to using ('' to not force)
1933
	 *  @param		Translate	$outputlangs	object lang to use for translations
1934
	 *  @param      int			$hidedetails    Hide details of lines
1935
	 *  @param      int			$hidedesc       Hide description
1936
	 *  @param      int			$hideref        Hide ref
1937
	 *  @return     int         				0 if KO, 1 if OK
1938
	 */
1939
	public function generateDocument($modele, $outputlangs,$hidedetails=0, $hidedesc=0, $hideref=0)
1940
	{
1941
		global $conf,$langs;
1942
1943
		$langs->load("receptions");
1944
1945
		if (! dol_strlen($modele))
1946
		{
1947
			$modele = 'squille';
1948
1949
			if ($this->modelpdf) {
1950
				$modele = $this->modelpdf;
1951
			} elseif (! empty($conf->global->RECEPTION_ADDON_PDF)) {
1952
				$modele = $conf->global->RECEPTION_ADDON_PDF;
1953
			}
1954
		}
1955
1956
		$modelpath = "core/modules/reception/doc/";
1957
1958
		$this->fetch_origin();
1959
1960
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1961
	}
1962
1963
	/**
1964
	 * Function used to replace a thirdparty id with another one.
1965
	 *
1966
	 * @param DoliDB $db Database handler
1967
	 * @param int $origin_id Old thirdparty id
1968
	 * @param int $dest_id New thirdparty id
1969
	 * @return bool
1970
	 */
1971
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1972
	{
1973
		$tables = array('reception');
1974
1975
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1976
	}
1977
}