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

Expedition::getUrlTrackingStatus()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 8
nop 1
dl 0
loc 28
rs 8.5386
c 0
b 0
f 0
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-2017  Francis Appels          <[email protected]>
11
 * Copyright (C) 2015       Claudio Aschieri        <[email protected]>
12
 * Copyright (C) 2016		Ferran Marcet			<[email protected]>
13
 * Copyright (C) 2018       Nicolas ZABOURI			<[email protected]>
14
 * Copyright (C) 2018       Frédéric France         <[email protected]>
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 3 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 */
29
30
/**
31
 *  \file       htdocs/expedition/class/expedition.class.php
32
 *  \ingroup    expedition
33
 *  \brief      Fichier de la classe de gestion des expeditions
34
 */
35
36
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
37
require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
38
if (! empty($conf->propal->enabled)) require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
39
if (! empty($conf->commande->enabled)) require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
40
if (! empty($conf->productbatch->enabled)) require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
41
42
43
/**
44
 *	Class to manage shipments
45
 */
46
class Expedition extends CommonObject
47
{
48
	/**
49
	 * @var string ID to identify managed object
50
	 */
51
	public $element="shipping";
52
53
	/**
54
	 * @var int Field with ID of parent key if this field has a parent
55
	 */
56
	public $fk_element="fk_expedition";
57
58
	/**
59
	 * @var string Name of table without prefix where object is stored
60
	 */
61
	public $table_element="expedition";
62
63
	/**
64
	 * @var int    Name of subtable line
65
	 */
66
	public $table_element_line="expeditiondet";
67
68
	/**
69
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
70
	 * @var int
71
	 */
72
	public $ismultientitymanaged = 1;
73
74
	/**
75
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
76
	 */
77
	public $picto = 'sending';
78
79
	public $socid;
80
81
	/**
82
	 * @var string Customer ref
83
	 */
84
	public $ref_customer;
85
86
	/**
87
	 * @var string internal ref
88
	 */
89
	public $ref_int;
90
91
	public $brouillon;
92
93
	/**
94
	 * @var int warehouse id
95
	 */
96
	public $entrepot_id;
97
	public $lines=array();
98
99
	/**
100
	 * @var string Tracking number
101
	 */
102
	public $tracking_number;
103
104
	/**
105
	 * @var string Tracking url
106
	 */
107
	public $tracking_url;
108
	public $billed;
109
110
	/**
111
	 * @var string name of pdf model
112
	 */
113
	public $model_pdf;
114
115
	public $trueWeight;
116
	public $weight_units;
117
	public $trueWidth;
118
	public $width_units;
119
	public $trueHeight;
120
	public $height_units;
121
	public $trueDepth;
122
	public $depth_units;
123
	// A denormalized value
124
	public $trueSize;
125
126
	public $date_delivery;		// Date delivery planed
127
128
	/**
129
	 * @deprecated
130
	 * @see date_shipping
131
	 */
132
	public $date;
133
134
	/**
135
	 * @deprecated
136
	 * @see date_shipping
137
	 */
138
	public $date_expedition;
139
140
	/**
141
	 * Effective delivery date
142
	 * @var int
143
	 */
144
	public $date_shipping;
145
146
	public $date_creation;
147
	public $date_valid;
148
149
	public $meths;
150
	public $listmeths;			// List of carriers
151
152
    /**
153
	 * Draft status
154
	 */
155
	const STATUS_DRAFT = 0;
156
157
	/**
158
	 * Validated status
159
	 */
160
	const STATUS_VALIDATED = 1;
161
162
	/**
163
	 * Closed status
164
	 */
165
	const STATUS_CLOSED = 2;
166
167
168
169
	/**
170
	 *	Constructor
171
	 *
172
	 *  @param		DoliDB		$db      Database handler
173
	 */
174
	function __construct($db)
175
	{
176
		global $conf;
177
178
		$this->db = $db;
179
		$this->lines = array();
180
		$this->products = array();
181
182
		// List of long language codes for status
183
		$this->statuts = array();
184
		$this->statuts[-1] = 'StatusSendingCanceled';
185
		$this->statuts[0]  = 'StatusSendingDraft';
186
		$this->statuts[1]  = 'StatusSendingValidated';
187
		$this->statuts[2]  = 'StatusSendingProcessed';
188
189
		// List of short language codes for status
190
		$this->statutshorts = array();
191
		$this->statutshorts[-1] = 'StatusSendingCanceledShort';
192
		$this->statutshorts[0]  = 'StatusSendingDraftShort';
193
		$this->statutshorts[1]  = 'StatusSendingValidatedShort';
194
		$this->statutshorts[2]  = 'StatusSendingProcessedShort';
195
196
		/* Status "billed" or not is managed by another field than status
197
		if (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))
198
		{
199
			$this->statuts[2]  = 'StatusSendingBilled';
200
			$this->statutshorts[2]  = 'StatusSendingBilledShort';
201
		}*/
202
	}
203
204
	/**
205
	 *	Return next contract ref
206
	 *
207
	 *	@param	Societe		$soc	Thirdparty object
208
	 *	@return string				Free reference for contract
209
	 */
210
	function getNextNumRef($soc)
211
	{
212
		global $langs, $conf;
213
		$langs->load("sendings");
214
215
		if (!empty($conf->global->EXPEDITION_ADDON_NUMBER))
216
		{
217
			$mybool = false;
218
219
			$file = $conf->global->EXPEDITION_ADDON_NUMBER.".php";
220
			$classname = $conf->global->EXPEDITION_ADDON_NUMBER;
221
222
			// Include file with class
223
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
224
225
			foreach ($dirmodels as $reldir) {
226
227
				$dir = dol_buildpath($reldir."core/modules/expedition/");
228
229
				// Load file with numbering class (if found)
230
				$mybool|=@include_once $dir.$file;
231
			}
232
233
			if (! $mybool)
234
			{
235
				dol_print_error('',"Failed to include file ".$file);
236
				return '';
237
			}
238
239
			$obj = new $classname();
240
			$numref = "";
241
			$numref = $obj->getNextValue($soc,$this);
242
243
			if ( $numref != "")
244
			{
245
				return $numref;
246
			}
247
			else
248
			{
249
				dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
250
				return "";
251
			}
252
		}
253
		else
254
		{
255
			print $langs->trans("Error")." ".$langs->trans("Error_EXPEDITION_ADDON_NUMBER_NotDefined");
256
			return "";
257
		}
258
	}
259
260
	/**
261
	 *  Create expedition en base
262
	 *
263
	 *  @param	User	$user       Objet du user qui cree
264
	 * 	@param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
265
	 *  @return int 				<0 si erreur, id expedition creee si ok
266
	 */
267
	function create($user, $notrigger=0)
268
	{
269
		global $conf, $hookmanager;
270
271
		$now=dol_now();
272
273
		require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php';
274
		$error = 0;
275
276
		// Clean parameters
277
		$this->brouillon = 1;
278
		$this->tracking_number = dol_sanitizeFileName($this->tracking_number);
279
		if (empty($this->fk_project)) $this->fk_project = 0;
280
281
		$this->user = $user;
282
283
284
		$this->db->begin();
285
286
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."expedition (";
287
		$sql.= "ref";
288
		$sql.= ", entity";
289
		$sql.= ", ref_customer";
290
		$sql.= ", ref_int";
291
		$sql.= ", date_creation";
292
		$sql.= ", fk_user_author";
293
		$sql.= ", date_expedition";
294
		$sql.= ", date_delivery";
295
		$sql.= ", fk_soc";
296
		$sql.= ", fk_projet";
297
		$sql.= ", fk_address";
298
		$sql.= ", fk_shipping_method";
299
		$sql.= ", tracking_number";
300
		$sql.= ", weight";
301
		$sql.= ", size";
302
		$sql.= ", width";
303
		$sql.= ", height";
304
		$sql.= ", weight_units";
305
		$sql.= ", size_units";
306
		$sql.= ", note_private";
307
		$sql.= ", note_public";
308
		$sql.= ", model_pdf";
309
		$sql.= ", fk_incoterms, location_incoterms";
310
		$sql.= ") VALUES (";
311
		$sql.= "'(PROV)'";
312
		$sql.= ", ".$conf->entity;
313
		$sql.= ", ".($this->ref_customer?"'".$this->db->escape($this->ref_customer)."'":"null");
314
		$sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
315
		$sql.= ", '".$this->db->idate($now)."'";
316
		$sql.= ", ".$user->id;
317
		$sql.= ", ".($this->date_expedition>0?"'".$this->db->idate($this->date_expedition)."'":"null");
318
		$sql.= ", ".($this->date_delivery>0?"'".$this->db->idate($this->date_delivery)."'":"null");
319
		$sql.= ", ".$this->socid;
320
		$sql.= ", ".$this->fk_project;
321
		$sql.= ", ".($this->fk_delivery_address>0?$this->fk_delivery_address:"null");
322
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:"null");
323
		$sql.= ", '".$this->db->escape($this->tracking_number)."'";
324
		$sql.= ", ".$this->weight;
325
		$sql.= ", ".$this->sizeS;	// TODO Should use this->trueDepth
326
		$sql.= ", ".$this->sizeW;	// TODO Should use this->trueWidth
327
		$sql.= ", ".$this->sizeH;	// TODO Should use this->trueHeight
328
		$sql.= ", ".$this->weight_units;
329
		$sql.= ", ".$this->size_units;
330
		$sql.= ", ".(!empty($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null");
331
		$sql.= ", ".(!empty($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null");
332
		$sql.= ", ".(!empty($this->model_pdf)?"'".$this->db->escape($this->model_pdf)."'":"null");
333
		$sql.= ", ".(int) $this->fk_incoterms;
334
		$sql.= ", '".$this->db->escape($this->location_incoterms)."'";
335
		$sql.= ")";
336
337
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
338
		$resql=$this->db->query($sql);
339
		if ($resql)
340
		{
341
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."expedition");
342
343
			$sql = "UPDATE ".MAIN_DB_PREFIX."expedition";
344
			$sql.= " SET ref = '(PROV".$this->id.")'";
345
			$sql.= " WHERE rowid = ".$this->id;
346
347
			dol_syslog(get_class($this)."::create", LOG_DEBUG);
348
			if ($this->db->query($sql))
349
			{
350
				// Insertion des lignes
351
				$num=count($this->lines);
352
				for ($i = 0; $i < $num; $i++)
353
				{
354
					if (! isset($this->lines[$i]->detail_batch))
355
					{	// no batch management
356
						if (! $this->create_line($this->lines[$i]->entrepot_id, $this->lines[$i]->origin_line_id, $this->lines[$i]->qty, $this->lines[$i]->array_options) > 0)
357
						{
358
							$error++;
359
						}
360
					}
361
					else
362
					{	// with batch management
363
						if (! $this->create_line_batch($this->lines[$i],$this->lines[$i]->array_options) > 0)
364
						{
365
							$error++;
366
						}
367
					}
368
				}
369
370
				if (! $error && $this->id && $this->origin_id)
371
				{
372
					$ret = $this->add_object_linked();
373
					if (!$ret)
374
					{
375
						$error++;
376
					}
377
				}
378
379
				// Actions on extra fields
380
				if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))
381
				{
382
					$result=$this->insertExtraFields();
383
					if ($result < 0)
384
					{
385
						$error++;
386
					}
387
				}
388
389
				if (! $error && ! $notrigger)
390
				{
391
					// Call trigger
392
					$result=$this->call_trigger('SHIPPING_CREATE',$user);
393
					if ($result < 0) { $error++; }
394
					// End call triggers
395
396
					if (! $error)
397
					{
398
						$this->db->commit();
399
						return $this->id;
400
					}
401
					else
402
					{
403
						foreach($this->errors as $errmsg)
404
						{
405
							dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
406
							$this->error.=($this->error?', '.$errmsg:$errmsg);
407
						}
408
						$this->db->rollback();
409
						return -1*$error;
410
					}
411
				}
412
				else
413
				{
414
					$error++;
415
					$this->error=$this->db->lasterror()." - sql=$sql";
416
					$this->db->rollback();
417
					return -3;
418
				}
419
			}
420
			else
421
			{
422
				$error++;
423
				$this->error=$this->db->lasterror()." - sql=$sql";
424
				$this->db->rollback();
425
				return -2;
426
			}
427
		}
428
		else
429
		{
430
			$error++;
431
			$this->error=$this->db->error()." - sql=$sql";
432
			$this->db->rollback();
433
			return -1;
434
		}
435
	}
436
437
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
438
	/**
439
	 * Create a expedition line
440
	 *
441
	 * @param 	int		$entrepot_id		Id of warehouse
442
	 * @param 	int		$origin_line_id		Id of source line
443
	 * @param 	int		$qty				Quantity
444
	 * @param	array	$array_options		extrafields array
445
	 * @return	int							<0 if KO, line_id if OK
446
	 */
447
	function create_line($entrepot_id, $origin_line_id, $qty,$array_options=0)
448
	{
449
        //phpcs:enable
450
		$expeditionline = new ExpeditionLigne($this->db);
451
		$expeditionline->fk_expedition = $this->id;
452
		$expeditionline->entrepot_id = $entrepot_id;
453
		$expeditionline->fk_origin_line = $origin_line_id;
454
		$expeditionline->qty = $qty;
455
		$expeditionline->array_options = $array_options;
456
457
		if (($lineId = $expeditionline->insert()) < 0)
458
		{
459
			$this->errors[]=$expeditionline->error;
460
		}
461
		return $lineId;
462
	}
463
464
465
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
466
	/**
467
	 * Create the detail (eat-by date) of the expedition line
468
	 *
469
	 * @param 	object		$line_ext		full line informations
470
	 * @param	array		$array_options		extrafields array
471
	 * @return	int							<0 if KO, >0 if OK
472
	 */
473
	function create_line_batch($line_ext,$array_options=0)
474
	{
475
        // phpcs:enable
476
		$error = 0;
477
		$stockLocationQty = array(); // associated array with batch qty in stock location
478
479
		$tab=$line_ext->detail_batch;
480
		// create stockLocation Qty array
481
		foreach ($tab as $detbatch)
482
		{
483
			if ($detbatch->entrepot_id)
484
			{
485
				$stockLocationQty[$detbatch->entrepot_id] += $detbatch->qty;
486
			}
487
		}
488
		// create shipment lines
489
		foreach ($stockLocationQty as $stockLocation => $qty)
490
		{
491
			if (($line_id = $this->create_line($stockLocation,$line_ext->origin_line_id,$qty,$array_options)) < 0)
492
			{
493
				$error++;
494
			}
495
			else
496
			{
497
				// create shipment batch lines for stockLocation
498
				foreach ($tab as $detbatch)
499
				{
500
					if ($detbatch->entrepot_id == $stockLocation){
501
						if (! ($detbatch->create($line_id) >0))		// Create an expeditionlinebatch
502
						{
503
							$error++;
504
						}
505
					}
506
				}
507
			}
508
		}
509
510
		if (! $error) return 1;
511
		else return -1;
512
	}
513
514
	/**
515
	 *	Get object and lines from database
516
	 *
517
	 *	@param	int		$id       	Id of object to load
518
	 * 	@param	string	$ref		Ref of object
519
	 * 	@param	string	$ref_ext	External reference of object
520
	 * 	@param	string	$ref_int	Internal reference of other object
521
	 *	@return int			        >0 if OK, 0 if not found, <0 if KO
522
	 */
523
	function fetch($id, $ref='', $ref_ext='', $ref_int='')
524
	{
525
		global $conf;
526
527
		// Check parameters
528
		if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
529
530
		$sql = "SELECT e.rowid, e.ref, e.fk_soc as socid, e.date_creation, e.ref_customer, e.ref_ext, e.ref_int, e.fk_user_author, e.fk_statut, e.fk_projet, e.billed";
531
		$sql.= ", e.weight, e.weight_units, e.size, e.size_units, e.width, e.height";
532
		$sql.= ", e.date_expedition as date_expedition, e.model_pdf, e.fk_address, e.date_delivery";
533
		$sql.= ", e.fk_shipping_method, e.tracking_number";
534
		$sql.= ", e.note_private, e.note_public";
535
		$sql.= ', e.fk_incoterms, e.location_incoterms';
536
		$sql.= ', i.libelle as libelle_incoterms';
537
		$sql.= ', s.libelle as shipping_method';
538
		$sql.= ", el.fk_source as origin_id, el.sourcetype as origin";
539
		$sql.= " FROM ".MAIN_DB_PREFIX."expedition as e";
540
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = e.rowid AND el.targettype = '".$this->db->escape($this->element)."'";
541
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON e.fk_incoterms = i.rowid';
542
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_shipment_mode as s ON e.fk_shipping_method = s.rowid';
543
		$sql.= " WHERE e.entity IN (".getEntity('expedition').")";
544
		if ($id)   	  $sql.= " AND e.rowid=".$id;
545
		if ($ref)     $sql.= " AND e.ref='".$this->db->escape($ref)."'";
546
		if ($ref_ext) $sql.= " AND e.ref_ext='".$this->db->escape($ref_ext)."'";
547
		if ($ref_int) $sql.= " AND e.ref_int='".$this->db->escape($ref_int)."'";
548
549
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
550
		$result = $this->db->query($sql);
551
		if ($result)
552
		{
553
			if ($this->db->num_rows($result))
554
			{
555
				$obj = $this->db->fetch_object($result);
556
557
				$this->id                   = $obj->rowid;
558
				$this->ref                  = $obj->ref;
559
				$this->socid                = $obj->socid;
560
				$this->ref_customer	    = $obj->ref_customer;
561
				$this->ref_ext		    = $obj->ref_ext;
562
				$this->ref_int		    = $obj->ref_int;
563
				$this->statut               = $obj->fk_statut;
564
				$this->user_author_id       = $obj->fk_user_author;
565
				$this->date_creation        = $this->db->jdate($obj->date_creation);
566
				$this->date                 = $this->db->jdate($obj->date_expedition);	// TODO deprecated
567
				$this->date_expedition      = $this->db->jdate($obj->date_expedition);	// TODO deprecated
568
				$this->date_shipping        = $this->db->jdate($obj->date_expedition);	// Date real
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_expedition) can also be of type string. However, the property $date_shipping 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...
569
				$this->date_delivery        = $this->db->jdate($obj->date_delivery);	// Date planed
570
				$this->fk_delivery_address  = $obj->fk_address;
571
				$this->modelpdf             = $obj->model_pdf;
572
				$this->shipping_method_id   = $obj->fk_shipping_method;
573
				$this->shipping_method	    = $obj->shipping_method;
574
				$this->tracking_number      = $obj->tracking_number;
575
				$this->origin               = ($obj->origin?$obj->origin:'commande'); // For compatibility
576
				$this->origin_id            = $obj->origin_id;
577
				$this->billed               = $obj->billed;
578
				$this->fk_project	    = $obj->fk_projet;
579
580
				$this->trueWeight           = $obj->weight;
581
				$this->weight_units         = $obj->weight_units;
582
583
				$this->trueWidth            = $obj->width;
584
				$this->width_units          = $obj->size_units;
585
				$this->trueHeight           = $obj->height;
586
				$this->height_units         = $obj->size_units;
587
				$this->trueDepth            = $obj->size;
588
				$this->depth_units          = $obj->size_units;
589
590
				$this->note_public          = $obj->note_public;
591
				$this->note_private         = $obj->note_private;
592
593
				// A denormalized value
594
				$this->trueSize             = $obj->size."x".$obj->width."x".$obj->height;
595
				$this->size_units           = $obj->size_units;
596
597
				//Incoterms
598
				$this->fk_incoterms         = $obj->fk_incoterms;
599
				$this->location_incoterms   = $obj->location_incoterms;
600
				$this->libelle_incoterms    = $obj->libelle_incoterms;
601
602
				$this->db->free($result);
603
604
				if ($this->statut == 0) $this->brouillon = 1;
605
606
				// Tracking url
607
				$this->getUrlTrackingStatus($obj->tracking_number);
608
609
				/*
610
				 * Thirparty
611
				 */
612
				$result=$this->fetch_thirdparty();
613
614
				// Retreive extrafields
615
				$this->fetch_optionals();
616
617
				/*
618
				 * Lines
619
				 */
620
				$result=$this->fetch_lines();
621
				if ($result < 0)
622
				{
623
					return -3;
624
				}
625
626
				return 1;
627
			}
628
			else
629
			{
630
				dol_syslog(get_class($this).'::Fetch no expedition found', LOG_ERR);
631
				$this->error='Delivery with id '.$id.' not found';
632
				return 0;
633
			}
634
		}
635
		else
636
		{
637
			$this->error=$this->db->error();
638
			return -1;
639
		}
640
	}
641
642
	/**
643
	 *  Validate object and update stock if option enabled
644
	 *
645
	 *  @param      User		$user       Object user that validate
646
	 *  @param		int			$notrigger	1=Does not execute triggers, 0= execute triggers
647
	 *  @return     int						<0 if OK, >0 if KO
648
	 */
649
	function valid($user, $notrigger=0)
650
	{
651
		global $conf, $langs;
652
653
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
654
655
		dol_syslog(get_class($this)."::valid");
656
657
		// Protection
658
		if ($this->statut)
659
		{
660
			dol_syslog(get_class($this)."::valid no draft status", LOG_WARNING);
661
			return 0;
662
		}
663
664
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->creer))
665
	   	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->shipping_advance->validate))))
666
		{
667
			$this->error='Permission denied';
668
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
669
			return -1;
670
		}
671
672
		$this->db->begin();
673
674
		$error = 0;
675
676
		// Define new ref
677
		$soc = new Societe($this->db);
678
		$soc->fetch($this->socid);
679
680
		// Class of company linked to order
681
		$result=$soc->set_as_client();
682
683
		// Define new ref
684
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
685
		{
686
			$numref = $this->getNextNumRef($soc);
687
		}
688
		else
689
		{
690
			$numref = "EXP".$this->id;
691
		}
692
		$this->newref = $numref;
693
694
		$now=dol_now();
695
696
		// Validate
697
		$sql = "UPDATE ".MAIN_DB_PREFIX."expedition SET";
698
		$sql.= " ref='".$numref."'";
699
		$sql.= ", fk_statut = 1";
700
		$sql.= ", date_valid = '".$this->db->idate($now)."'";
701
		$sql.= ", fk_user_valid = ".$user->id;
702
		$sql.= " WHERE rowid = ".$this->id;
703
704
		dol_syslog(get_class($this)."::valid update expedition", LOG_DEBUG);
705
		$resql=$this->db->query($sql);
706
		if (! $resql)
707
		{
708
			$this->error=$this->db->lasterror();
709
			$error++;
710
		}
711
712
		// If stock increment is done on sending (recommanded choice)
713
		if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT))
714
		{
715
			require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
716
717
			$langs->load("agenda");
718
719
			// Loop on each product line to add a stock movement
720
			$sql = "SELECT cd.fk_product, cd.subprice,";
721
			$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
722
			$sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
723
			$sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
724
			$sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
725
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
726
			$sql.= " WHERE ed.fk_expedition = ".$this->id;
727
			$sql.= " AND cd.rowid = ed.fk_origin_line";
728
729
			dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
730
			$resql=$this->db->query($sql);
731
			if ($resql)
732
			{
733
				$cpt = $this->db->num_rows($resql);
734
				for ($i = 0; $i < $cpt; $i++)
735
				{
736
					$obj = $this->db->fetch_object($resql);
737
					if (empty($obj->edbrowid))
738
					{
739
						$qty = $obj->qty;
740
					}
741
					else
742
					{
743
						$qty = $obj->edbqty;
744
					}
745
					if ($qty <= 0) continue;
746
					dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
747
748
					//var_dump($this->lines[$i]);
749
					$mouvS = new MouvementStock($this->db);
750
					$mouvS->origin = &$this;
751
752
					if (empty($obj->edbrowid))
753
					{
754
						// line without batch detail
755
756
						// 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.
757
						$result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref));
758
						if ($result < 0) {
759
							$error++;
760
							$this->errors[]=$mouvS->error;
761
							$this->errors = array_merge($this->errors, $mouvS->errors);
762
							break;
763
						}
764
					}
765
					else
766
					{
767
						// line with batch detail
768
769
						// 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.
770
						// Note: ->fk_origin_stock = id into table llx_product_batch (may be rename into llx_product_stock_batch in another version)
771
						$result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
772
						if ($result < 0) {
773
							$error++;
774
							$this->errors[]=$mouvS->error;
775
							$this->errors = array_merge($this->errors, $mouvS->errors);
776
							break;
777
						}
778
					}
779
				}
780
			}
781
			else
782
			{
783
				$this->db->rollback();
784
				$this->error=$this->db->error();
785
				return -2;
786
			}
787
		}
788
789
		// Change status of order to "shipment in process"
790
		$ret = $this->setStatut(Commande::STATUS_SHIPMENTONPROCESS, $this->origin_id, $this->origin);
791
792
		if (! $ret)
793
		{
794
			$error++;
795
		}
796
797
		if (! $error && ! $notrigger)
798
		{
799
			// Call trigger
800
			$result=$this->call_trigger('SHIPPING_VALIDATE',$user);
801
			if ($result < 0) { $error++; }
802
			// End call triggers
803
		}
804
805
		if (! $error)
806
		{
807
			$this->oldref = $this->ref;
808
809
			// Rename directory if dir was a temporary ref
810
			if (preg_match('/^[\(]?PROV/i', $this->ref))
811
			{
812
				// On renomme repertoire ($this->ref = ancienne ref, $numfa = nouvelle ref)
813
				// in order not to lose the attached files
814
				$oldref = dol_sanitizeFileName($this->ref);
815
				$newref = dol_sanitizeFileName($numref);
816
				$dirsource = $conf->expedition->dir_output.'/sending/'.$oldref;
817
				$dirdest = $conf->expedition->dir_output.'/sending/'.$newref;
818
				if (file_exists($dirsource))
819
				{
820
					dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
821
822
					if (@rename($dirsource, $dirdest))
823
					{
824
						dol_syslog("Rename ok");
825
						// Rename docs starting with $oldref with $newref
826
						$listoffiles=dol_dir_list($conf->expedition->dir_output.'/sending/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
827
						foreach($listoffiles as $fileentry)
828
						{
829
							$dirsource=$fileentry['name'];
830
							$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
831
							$dirsource=$fileentry['path'].'/'.$dirsource;
832
							$dirdest=$fileentry['path'].'/'.$dirdest;
833
							@rename($dirsource, $dirdest);
834
						}
835
					}
836
				}
837
			}
838
		}
839
840
		// Set new ref and current status
841
		if (! $error)
842
		{
843
			$this->ref = $numref;
844
			$this->statut = 1;
845
		}
846
847
		if (! $error)
848
		{
849
			$this->db->commit();
850
			return 1;
851
		}
852
		else
853
		{
854
			foreach($this->errors as $errmsg)
855
			{
856
				dol_syslog(get_class($this)."::valid ".$errmsg, LOG_ERR);
857
				$this->error.=($this->error?', '.$errmsg:$errmsg);
858
			}
859
			$this->db->rollback();
860
			return -1*$error;
861
		}
862
	}
863
864
865
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
866
	/**
867
	 *	Create a delivery receipt from a shipment
868
	 *
869
	 *	@param	User	$user       User
870
	 *  @return int  				<0 if KO, >=0 if OK
871
	 */
872
	function create_delivery($user)
873
	{
874
        // phpcs:enable
875
		global $conf;
876
877
		if ($conf->livraison_bon->enabled)
878
		{
879
			if ($this->statut == 1 || $this->statut == 2)
880
			{
881
				// Expedition validee
882
				include_once DOL_DOCUMENT_ROOT.'/livraison/class/livraison.class.php';
883
				$delivery = new Livraison($this->db);
884
				$result=$delivery->create_from_sending($user, $this->id);
885
				if ($result > 0)
886
				{
887
					return $result;
888
				}
889
				else
890
				{
891
					$this->error=$delivery->error;
892
					return $result;
893
				}
894
			}
895
			else return 0;
896
		}
897
		else return 0;
898
	}
899
900
	/**
901
	 * Add an expedition line.
902
	 * If STOCK_WAREHOUSE_NOT_REQUIRED_FOR_SHIPMENTS is set, you can add a shipment line, with no stock source defined
903
	 * If STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT is not set, you can add a shipment line, even if not enough into stock
904
	 *
905
	 * @param 	int		$entrepot_id		Id of warehouse
906
	 * @param 	int		$id					Id of source line (order line)
907
	 * @param 	int		$qty				Quantity
908
	 * @param	array	$array_options		extrafields array
909
	 * @return	int							<0 if KO, >0 if OK
910
	 */
911
	function addline($entrepot_id, $id, $qty,$array_options=0)
912
	{
913
		global $conf, $langs;
914
915
		$num = count($this->lines);
916
		$line = new ExpeditionLigne($this->db);
917
918
		$line->entrepot_id = $entrepot_id;
919
		$line->origin_line_id = $id;
920
		$line->qty = $qty;
921
922
		$orderline = new OrderLine($this->db);
923
		$orderline->fetch($id);
924
925
		if (! empty($conf->stock->enabled) && ! empty($orderline->fk_product))
926
		{
927
			$fk_product = $orderline->fk_product;
928
929
			if (! ($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_SHIPMENTS))
930
			{
931
				$langs->load("errors");
932
				$this->error=$langs->trans("ErrorWarehouseRequiredIntoShipmentLine");
933
				return -1;
934
			}
935
936
			if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT)
937
			{
938
				// Check must be done for stock of product into warehouse if $entrepot_id defined
939
				$product=new Product($this->db);
940
				$result=$product->fetch($fk_product);
941
942
				if ($entrepot_id > 0) {
943
					$product->load_stock('warehouseopen');
944
					$product_stock = $product->stock_warehouse[$entrepot_id]->real;
945
				}
946
				else
947
					$product_stock = $product->stock_reel;
948
949
				$product_type=$product->type;
950
				if ($product_type == 0 && $product_stock < $qty)
951
				{
952
					$langs->load("errors");
953
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $product->ref);
954
					$this->db->rollback();
955
					return -3;
956
				}
957
			}
958
		}
959
960
		// If product need a batch number, we should not have called this function but addline_batch instead.
961
		if (! empty($conf->productbatch->enabled) && ! empty($orderline->fk_product) && ! empty($orderline->product_tobatch))
962
		{
963
			$this->error='ADDLINE_WAS_CALLED_INSTEAD_OF_ADDLINEBATCH';
964
			return -4;
965
		}
966
967
		// extrafields
968
		if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
969
			$line->array_options = $array_options;
970
971
		$this->lines[$num] = $line;
972
	}
973
974
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
975
	/**
976
	 * Add a shipment line with batch record
977
	 *
978
	 * @param 	array		$dbatch		Array of value (key 'detail' -> Array, key 'qty' total quantity for line, key ix_l : original line index)
979
	 * @param	array		$array_options		extrafields array
980
	 * @return	int						<0 if KO, >0 if OK
981
	 */
982
	function addline_batch($dbatch,$array_options=0)
983
	{
984
        // phpcs:enable
985
		global $conf,$langs;
986
987
		$num = count($this->lines);
988
		if ($dbatch['qty']>0)
989
		{
990
			$line = new ExpeditionLigne($this->db);
991
			$tab=array();
992
			foreach ($dbatch['detail'] as $key=>$value)
993
			{
994
				if ($value['q']>0)
995
				{
996
					// $value['q']=qty to move
997
					// $value['id_batch']=id into llx_product_batch of record to move
998
					//var_dump($value);
999
1000
					$linebatch = new ExpeditionLineBatch($this->db);
1001
					$ret=$linebatch->fetchFromStock($value['id_batch']);	// load serial, sellby, eatby
1002
					if ($ret<0)
1003
					{
1004
						$this->error=$linebatch->error;
1005
						return -1;
1006
					}
1007
					$linebatch->qty=$value['q'];
1008
					$tab[]=$linebatch;
1009
1010
					if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT)
1011
					{
1012
						require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
1013
						$prod_batch = new Productbatch($this->db);
1014
						$prod_batch->fetch($value['id_batch']);
1015
1016
						if ($prod_batch->qty < $linebatch->qty)
1017
						{
1018
							$langs->load("errors");
1019
							$this->errors[]=$langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $prod_batch->fk_product);
1020
							dol_syslog(get_class($this)."::addline_batch error=Product ".$prod_batch->batch.": ".$this->errorsToString(), LOG_ERR);
1021
							$this->db->rollback();
1022
							return -1;
1023
						}
1024
					}
1025
1026
					//var_dump($linebatch);
1027
				}
1028
			}
1029
			$line->entrepot_id = $linebatch->entrepot_id;
0 ignored issues
show
Bug introduced by
The variable $linebatch 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...
1030
			$line->origin_line_id = $dbatch['ix_l'];
1031
			$line->qty = $dbatch['qty'];
1032
			$line->detail_batch=$tab;
1033
1034
			// extrafields
1035
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1036
				$line->array_options = $array_options;
1037
1038
			//var_dump($line);
1039
			$this->lines[$num] = $line;
1040
			return 1;
1041
		}
1042
	}
1043
1044
	/**
1045
	 *  Update database
1046
	 *
1047
	 *  @param	User	$user        	User that modify
1048
	 *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
1049
	 *  @return int 			       	<0 if KO, >0 if OK
1050
	 */
1051
	function update($user=null, $notrigger=0)
1052
	{
1053
		global $conf;
1054
		$error=0;
1055
1056
		// Clean parameters
1057
1058
		if (isset($this->ref)) $this->ref=trim($this->ref);
1059
		if (isset($this->entity)) $this->entity=trim($this->entity);
1060
		if (isset($this->ref_customer)) $this->ref_customer=trim($this->ref_customer);
1061
		if (isset($this->socid)) $this->socid=trim($this->socid);
1062
		if (isset($this->fk_user_author)) $this->fk_user_author=trim($this->fk_user_author);
1063
		if (isset($this->fk_user_valid)) $this->fk_user_valid=trim($this->fk_user_valid);
1064
		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...
1065
		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...
1066
		if (isset($this->tracking_number)) $this->tracking_number=trim($this->tracking_number);
1067
		if (isset($this->statut)) $this->statut=(int) $this->statut;
1068
		if (isset($this->trueDepth)) $this->trueDepth=trim($this->trueDepth);
1069
		if (isset($this->trueWidth)) $this->trueWidth=trim($this->trueWidth);
1070
		if (isset($this->trueHeight)) $this->trueHeight=trim($this->trueHeight);
1071
		if (isset($this->size_units)) $this->size_units=trim($this->size_units);
1072
		if (isset($this->weight_units)) $this->weight_units=trim($this->weight_units);
1073
		if (isset($this->trueWeight)) $this->weight=trim($this->trueWeight);
1074
		if (isset($this->note_private)) $this->note=trim($this->note_private);
1075
		if (isset($this->note_public)) $this->note=trim($this->note_public);
1076
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1077
1078
1079
1080
		// Check parameters
1081
		// Put here code to add control on parameters values
1082
1083
		// Update request
1084
		$sql = "UPDATE ".MAIN_DB_PREFIX."expedition SET";
1085
1086
		$sql.= " tms=".(dol_strlen($this->tms)!=0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
1087
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1088
		$sql.= " ref_customer=".(isset($this->ref_customer)?"'".$this->db->escape($this->ref_customer)."'":"null").",";
1089
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1090
		$sql.= " date_creation=".(dol_strlen($this->date_creation)!=0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').",";
1091
		$sql.= " fk_user_author=".(isset($this->fk_user_author)?$this->fk_user_author:"null").",";
1092
		$sql.= " date_valid=".(dol_strlen($this->date_valid)!=0 ? "'".$this->db->idate($this->date_valid)."'" : 'null').",";
1093
		$sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->fk_user_valid:"null").",";
1094
		$sql.= " date_expedition=".(dol_strlen($this->date_expedition)!=0 ? "'".$this->db->idate($this->date_expedition)."'" : 'null').",";
1095
		$sql.= " date_delivery=".(dol_strlen($this->date_delivery)!=0 ? "'".$this->db->idate($this->date_delivery)."'" : 'null').",";
1096
		$sql.= " fk_address=".(isset($this->fk_delivery_address)?$this->fk_delivery_address:"null").",";
1097
		$sql.= " fk_shipping_method=".((isset($this->shipping_method_id) && $this->shipping_method_id > 0)?$this->shipping_method_id:"null").",";
1098
		$sql.= " tracking_number=".(isset($this->tracking_number)?"'".$this->db->escape($this->tracking_number)."'":"null").",";
1099
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1100
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1101
		$sql.= " height=".(($this->trueHeight != '')?$this->trueHeight:"null").",";
1102
		$sql.= " width=".(($this->trueWidth != '')?$this->trueWidth:"null").",";
1103
		$sql.= " size_units=".(isset($this->size_units)?$this->size_units:"null").",";
1104
		$sql.= " size=".(($this->trueDepth != '')?$this->trueDepth:"null").",";
1105
		$sql.= " weight_units=".(isset($this->weight_units)?$this->weight_units:"null").",";
1106
		$sql.= " weight=".(($this->trueWeight != '')?$this->trueWeight:"null").",";
1107
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1108
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1109
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1110
		$sql.= " entity=".$conf->entity;
1111
1112
		$sql.= " WHERE rowid=".$this->id;
1113
1114
		$this->db->begin();
1115
1116
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
1117
		$resql = $this->db->query($sql);
1118
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
1119
1120
		if (! $error)
1121
		{
1122
			if (! $notrigger)
1123
			{
1124
				// Call trigger
1125
				$result=$this->call_trigger('SHIPPING_MODIFY',$user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 1051 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...
1126
				if ($result < 0) { $error++; }
1127
				// End call triggers
1128
			}
1129
		}
1130
1131
		// Commit or rollback
1132
		if ($error)
1133
		{
1134
			foreach($this->errors as $errmsg)
1135
			{
1136
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1137
				$this->error.=($this->error?', '.$errmsg:$errmsg);
1138
			}
1139
			$this->db->rollback();
1140
			return -1*$error;
1141
		}
1142
		else
1143
		{
1144
			$this->db->commit();
1145
			return 1;
1146
		}
1147
	}
1148
1149
	/**
1150
	 * 	Delete shipment.
1151
	 * 	Warning, do not delete a shipment if a delivery is linked to (with table llx_element_element)
1152
	 *
1153
	 * 	@return	int		>0 if OK, 0 if deletion done but failed to delete files, <0 if KO
1154
	 */
1155
	function delete()
1156
	{
1157
		global $conf, $langs, $user;
1158
1159
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1160
		require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
1161
1162
		$error=0;
1163
		$this->error='';
1164
1165
		$this->db->begin();
1166
1167
		// Add a protection to refuse deleting if shipment has at least one delivery
1168
		$this->fetchObjectLinked($this->id, 'shipping', 0, 'delivery');	// Get deliveries linked to this shipment
1169
		if (count($this->linkedObjectsIds) > 0)
1170
		{
1171
			$this->error='ErrorThereIsSomeDeliveries';
1172
			$error++;
1173
		}
1174
1175
		if (! $error)
1176
		{
1177
			if (! $notrigger)
0 ignored issues
show
Bug introduced by
The variable $notrigger 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...
1178
			{
1179
				// Call trigger
1180
				$result=$this->call_trigger('SHIPPING_DELETE',$user);
1181
				if ($result < 0) { $error++; }
1182
				// End call triggers
1183
			}
1184
		}
1185
1186
		// Stock control
1187
		if (! $error && $conf->stock->enabled && $conf->global->STOCK_CALCULATE_ON_SHIPMENT && $this->statut > 0)
1188
		{
1189
			require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
1190
1191
			$langs->load("agenda");
1192
1193
			// Loop on each product line to add a stock movement
1194
			$sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.rowid as expeditiondet_id";
1195
			$sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
1196
			$sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
1197
			$sql.= " WHERE ed.fk_expedition = ".$this->id;
1198
			$sql.= " AND cd.rowid = ed.fk_origin_line";
1199
1200
			dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
1201
			$resql=$this->db->query($sql);
1202
			if ($resql)
1203
			{
1204
				$cpt = $this->db->num_rows($resql);
1205
				for ($i = 0; $i < $cpt; $i++)
1206
				{
1207
					dol_syslog(get_class($this)."::delete movement index ".$i);
1208
					$obj = $this->db->fetch_object($resql);
1209
1210
					$mouvS = new MouvementStock($this->db);
1211
					// we do not log origin because it will be deleted
1212
					$mouvS->origin = null;
1213
					// get lot/serial
1214
					$lotArray = null;
1215
					if ($conf->productbatch->enabled)
1216
					{
1217
						$lotArray = ExpeditionLineBatch::fetchAll($this->db,$obj->expeditiondet_id);
1218
						if (! is_array($lotArray))
1219
						{
1220
							$error++;$this->errors[]="Error ".$this->db->lasterror();
1221
						}
1222
					}
1223
					if (empty($lotArray)) {
1224
						// no lot/serial
1225
						// We increment stock of product (and sub-products)
1226
						// We use warehouse selected for each line
1227
						$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref));  // Price is set to 0, because we don't want to see WAP changed
1228
						if ($result < 0)
1229
						{
1230
							$error++;$this->errors=$this->errors + $mouvS->errors;
1231
							break;
1232
						}
1233
					}
1234
					else
1235
					{
1236
						// We increment stock of batches
1237
						// We use warehouse selected for each line
1238
						foreach($lotArray as $lot)
1239
						{
1240
							$result=$mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $lot->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref), $lot->eatby, $lot->sellby, $lot->batch);  // Price is set to 0, because we don't want to see WAP changed
1241
							if ($result < 0)
1242
							{
1243
								$error++;$this->errors=$this->errors + $mouvS->errors;
1244
								break;
1245
							}
1246
						}
1247
						if ($error) break; // break for loop incase of error
1248
					}
1249
				}
1250
			}
1251
			else
1252
			{
1253
				$error++;$this->errors[]="Error ".$this->db->lasterror();
1254
			}
1255
		}
1256
1257
		// delete batch expedition line
1258
		if (! $error && $conf->productbatch->enabled)
1259
		{
1260
			if (ExpeditionLineBatch::deletefromexp($this->db,$this->id) < 0)
1261
			{
1262
				$error++;$this->errors[]="Error ".$this->db->lasterror();
1263
			}
1264
		}
1265
1266
		if (! $error)
1267
		{
1268
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet";
1269
			$sql.= " WHERE fk_expedition = ".$this->id;
1270
1271
			if ( $this->db->query($sql) )
1272
			{
1273
				// Delete linked object
1274
				$res = $this->deleteObjectLinked();
1275
				if ($res < 0) $error++;
1276
1277
				if (! $error)
1278
				{
1279
					$sql = "DELETE FROM ".MAIN_DB_PREFIX."expedition";
1280
					$sql.= " WHERE rowid = ".$this->id;
1281
1282
					if ($this->db->query($sql))
1283
					{
1284
						if (! empty($this->origin) && $this->origin_id > 0)
1285
						{
1286
							$this->fetch_origin();
1287
							$origin=$this->origin;
1288
							if ($this->$origin->statut == Commande::STATUS_SHIPMENTONPROCESS)     // If order source of shipment is "shipment in progress"
1289
							{
1290
								// Check if there is no more shipment. If not, we can move back status of order to "validated" instead of "shipment in progress"
1291
								$this->$origin->loadExpeditions();
1292
								//var_dump($this->$origin->expeditions);exit;
1293
								if (count($this->$origin->expeditions) <= 0)
1294
								{
1295
									$this->$origin->setStatut(Commande::STATUS_VALIDATED);
1296
								}
1297
							}
1298
						}
1299
1300
						if (! $error)
1301
						{
1302
							$this->db->commit();
1303
1304
							// We delete PDFs
1305
							$ref = dol_sanitizeFileName($this->ref);
1306
							if (! empty($conf->expedition->dir_output))
1307
							{
1308
								$dir = $conf->expedition->dir_output . '/sending/' . $ref ;
1309
								$file = $dir . '/' . $ref . '.pdf';
1310
								if (file_exists($file))
1311
								{
1312
									if (! dol_delete_file($file))
1313
									{
1314
										return 0;
1315
									}
1316
								}
1317
								if (file_exists($dir))
1318
								{
1319
									if (!dol_delete_dir_recursive($dir))
1320
									{
1321
										$this->error=$langs->trans("ErrorCanNotDeleteDir",$dir);
1322
										return 0;
1323
									}
1324
								}
1325
							}
1326
1327
							return 1;
1328
						}
1329
						else
1330
						{
1331
							$this->db->rollback();
1332
							return -1;
1333
						}
1334
					}
1335
					else
1336
					{
1337
						$this->error=$this->db->lasterror()." - sql=$sql";
1338
						$this->db->rollback();
1339
						return -3;
1340
					}
1341
				}
1342
				else
1343
				{
1344
					$this->error=$this->db->lasterror()." - sql=$sql";
1345
					$this->db->rollback();
1346
					return -2;
1347
				}
1348
			}
1349
			else
1350
			{
1351
				$this->error=$this->db->lasterror()." - sql=$sql";
1352
				$this->db->rollback();
1353
				return -1;
1354
			}
1355
		}
1356
		else
1357
		{
1358
			$this->db->rollback();
1359
			return -1;
1360
		}
1361
	}
1362
1363
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1364
	/**
1365
	 *	Load lines
1366
	 *
1367
	 *	@return	int		>0 if OK, Otherwise if KO
1368
	 */
1369
	function fetch_lines()
1370
	{
1371
        // phpcs:enable
1372
		global $conf, $mysoc;
1373
		// TODO: recuperer les champs du document associe a part
1374
1375
		$sql = "SELECT cd.rowid, cd.fk_product, cd.label as custom_label, cd.description, cd.qty as qty_asked, cd.product_type";
1376
		$sql.= ", cd.total_ht, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.total_tva";
1377
		$sql.= ", cd.vat_src_code, cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.info_bits, cd.price, cd.subprice, cd.remise_percent,cd.buy_price_ht as pa_ht";
1378
		$sql.= ", cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc";
1379
		$sql.= ", ed.rowid as line_id, ed.qty as qty_shipped, ed.fk_origin_line, ed.fk_entrepot";
1380
		$sql.= ", p.ref as product_ref, p.label as product_label, p.fk_product_type";
1381
		$sql.= ", p.weight, p.weight_units, p.length, p.length_units, p.surface, p.surface_units, p.volume, p.volume_units, p.tobatch as product_tobatch";
1382
		$sql.= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed, ".MAIN_DB_PREFIX."commandedet as cd";
1383
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product";
1384
		$sql.= " WHERE ed.fk_expedition = ".$this->id;
1385
		$sql.= " AND ed.fk_origin_line = cd.rowid";
1386
		$sql.= " ORDER BY cd.rang, ed.fk_origin_line";
1387
1388
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1389
		$resql = $this->db->query($sql);
1390
		if ($resql)
1391
		{
1392
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1393
1394
			$num = $this->db->num_rows($resql);
1395
			$i = 0;
1396
			$lineindex = 0;
1397
			$originline = 0;
1398
1399
			$this->total_ht = 0;
1400
			$this->total_tva = 0;
1401
			$this->total_ttc = 0;
1402
			$this->total_localtax1 = 0;
1403
			$this->total_localtax2 = 0;
1404
1405
			while ($i < $num)
1406
			{
1407
				$obj = $this->db->fetch_object($resql);
1408
1409
				if ($originline == $obj->fk_origin_line) {
1410
					$line->entrepot_id       = 0; // entrepod_id in details_entrepot
0 ignored issues
show
Bug introduced by
The variable $line 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...
1411
					$line->qty_shipped    	+= $obj->qty_shipped;
1412
				} else {
1413
					$line = new ExpeditionLigne($this->db);
1414
					$line->entrepot_id    	= $obj->fk_entrepot;
1415
					$line->qty_shipped    	= $obj->qty_shipped;
1416
				}
1417
1418
				$detail_entrepot              = new stdClass;
1419
				$detail_entrepot->entrepot_id = $obj->fk_entrepot;
1420
				$detail_entrepot->qty_shipped = $obj->qty_shipped;
1421
				$detail_entrepot->line_id     = $obj->line_id;
1422
				$line->details_entrepot[]     = $detail_entrepot;
1423
1424
				$line->line_id          = $obj->line_id;
1425
				$line->rowid            = $obj->line_id;    // TODO deprecated
1426
				$line->id               = $obj->line_id;
1427
1428
				$line->fk_origin     	= 'orderline';
1429
				$line->fk_origin_line 	= $obj->fk_origin_line;
1430
				$line->origin_line_id 	= $obj->fk_origin_line;	    // TODO deprecated
1431
1432
				$line->fk_expedition    = $this->id;                // id of parent
1433
1434
				$line->product_type     = $obj->product_type;
1435
				$line->fk_product     	= $obj->fk_product;
1436
				$line->fk_product_type	= $obj->fk_product_type;
1437
				$line->ref				= $obj->product_ref;		// TODO deprecated
1438
				$line->product_ref		= $obj->product_ref;
1439
				$line->product_label	= $obj->product_label;
1440
				$line->libelle        	= $obj->product_label;		// TODO deprecated
1441
				$line->product_tobatch  = $obj->product_tobatch;
1442
				$line->label			= $obj->custom_label;
1443
				$line->description    	= $obj->description;
1444
				$line->qty_asked      	= $obj->qty_asked;
1445
				$line->weight         	= $obj->weight;
1446
				$line->weight_units   	= $obj->weight_units;
1447
				$line->length         	= $obj->length;
1448
				$line->length_units   	= $obj->length_units;
1449
				$line->surface        	= $obj->surface;
1450
				$line->surface_units   	= $obj->surface_units;
1451
				$line->volume         	= $obj->volume;
1452
				$line->volume_units   	= $obj->volume_units;
1453
1454
				$line->pa_ht 			= $obj->pa_ht;
1455
1456
				// Local taxes
1457
				$localtax_array=array(0=>$obj->localtax1_type, 1=>$obj->localtax1_tx, 2=>$obj->localtax2_type, 3=>$obj->localtax2_tx);
1458
				$localtax1_tx = get_localtax($obj->tva_tx, 1, $this->thirdparty);
1459
				$localtax2_tx = get_localtax($obj->tva_tx, 2, $this->thirdparty);
1460
1461
				// For invoicing
1462
				$tabprice = calcul_price_total($obj->qty_shipped, $obj->subprice, $obj->remise_percent, $obj->tva_tx, $localtax1_tx, $localtax2_tx, 0, 'HT', $obj->info_bits, $obj->fk_product_type, $mysoc, $localtax_array);	// We force type to 0
1463
				$line->desc	         	= $obj->description;		// We need ->desc because some code into CommonObject use desc (property defined for other elements)
1464
				$line->qty 				= $line->qty_shipped;
1465
				$line->total_ht			= $tabprice[0];
1466
				$line->total_localtax1 	= $tabprice[9];
1467
				$line->total_localtax2 	= $tabprice[10];
1468
				$line->total_ttc	 	= $tabprice[2];
1469
				$line->total_tva	 	= $tabprice[1];
1470
				$line->vat_src_code	 	= $obj->vat_src_code;
1471
				$line->tva_tx 		 	= $obj->tva_tx;
1472
				$line->localtax1_tx 	= $obj->localtax1_tx;
1473
				$line->localtax2_tx 	= $obj->localtax2_tx;
1474
				$line->info_bits        = $obj->info_bits;
1475
				$line->price			= $obj->price;
1476
				$line->subprice			= $obj->subprice;
1477
				$line->remise_percent	= $obj->remise_percent;
1478
1479
				$this->total_ht+= $tabprice[0];
1480
				$this->total_tva+= $tabprice[1];
1481
				$this->total_ttc+= $tabprice[2];
1482
				$this->total_localtax1+= $tabprice[9];
1483
				$this->total_localtax2+= $tabprice[10];
1484
1485
				// Multicurrency
1486
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1487
				$this->multicurrency_code 		= $obj->multicurrency_code;
1488
				$this->multicurrency_subprice 	= $obj->multicurrency_subprice;
1489
				$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
1490
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1491
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1492
1493
				if ($originline != $obj->fk_origin_line)
1494
				{
1495
					$line->detail_batch = array();
1496
				}
1497
1498
				// Detail of batch
1499
				if (! empty($conf->productbatch->enabled) && $obj->line_id > 0 && $obj->product_tobatch > 0)
1500
				{
1501
					require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
1502
1503
					$newdetailbatch = ExpeditionLineBatch::fetchAll($this->db, $obj->line_id, $obj->fk_product);
1504
					if (is_array($newdetailbatch))
1505
					{
1506
						if ($originline != $obj->fk_origin_line)
1507
						{
1508
							$line->detail_batch = $newdetailbatch;
1509
						}
1510
						else
1511
						{
1512
							$line->detail_batch = array_merge($line->detail_batch, $newdetailbatch);
1513
						}
1514
					}
1515
				}
1516
1517
				if ($originline != $obj->fk_origin_line)
1518
				{
1519
					$this->lines[$lineindex] = $line;
1520
					$lineindex++;
1521
				}
1522
				else
1523
				{
1524
					$line->total_ht			+= $tabprice[0];
1525
					$line->total_localtax1 	+= $tabprice[9];
1526
					$line->total_localtax2 	+= $tabprice[10];
1527
					$line->total_ttc	 	+= $tabprice[2];
1528
					$line->total_tva	 	+= $tabprice[1];
1529
				}
1530
1531
				$i++;
1532
				$originline = $obj->fk_origin_line;
1533
			}
1534
			$this->db->free($resql);
1535
			return 1;
1536
		}
1537
		else
1538
		{
1539
			$this->error=$this->db->error();
1540
			return -3;
1541
		}
1542
	}
1543
1544
	/**
1545
	 *  Delete detail line
1546
	 *
1547
	 *  @param		User	$user			User making deletion
1548
	 *  @param		int		$lineid			Id of line to delete
1549
	 *  @return     int         			>0 if OK, <0 if KO
1550
	 */
1551
	function deleteline($user, $lineid)
1552
	{
1553
		global $user;
1554
1555
		if ($this->statut == self::STATUS_DRAFT)
1556
		{
1557
			$this->db->begin();
1558
1559
			$line=new ExpeditionLigne($this->db);
1560
1561
			// For triggers
1562
			$line->fetch($lineid);
1563
1564
			if ($line->delete($user) > 0)
1565
			{
1566
				//$this->update_price(1);
1567
1568
				$this->db->commit();
1569
				return 1;
1570
			}
1571
			else
1572
			{
1573
				$this->db->rollback();
1574
				return -1;
1575
			}
1576
		}
1577
		else
1578
		{
1579
			$this->error='ErrorDeleteLineNotAllowedByObjectStatus';
1580
			return -2;
1581
		}
1582
	}
1583
1584
1585
	/**
1586
	 *	Return clicable link of object (with eventually picto)
1587
	 *
1588
	 *	@param      int			$withpicto      			Add picto into link
1589
	 *	@param      string		$option         			Where the link point to
1590
	 *	@param      int			$max          				Max length to show
1591
	 *	@param      int			$short						Use short labels
1592
	 *  @param      int         $notooltip      			1=No tooltip
1593
	 *  @param      int     	$save_lastsearch_value		-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1594
	 *	@return     string          						String with URL
1595
	 */
1596
	function getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1)
1597
	{
1598
		global $langs;
1599
1600
		$result='';
1601
		$label = '<u>' . $langs->trans("ShowSending") . '</u>';
1602
		$label .= '<br><b>' . $langs->trans('Ref') . ':</b> '.$this->ref;
1603
		$label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
1604
1605
		$url = DOL_URL_ROOT.'/expedition/card.php?id='.$this->id;
1606
1607
		if ($short) return $url;
1608
1609
		if ($option !== 'nolink')
1610
		{
1611
			// Add param to save lastsearch_values or not
1612
			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1613
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1614
			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1615
		}
1616
1617
		$linkclose='';
1618
		if (empty($notooltip))
1619
		{
1620
			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...
1621
			{
1622
				$label=$langs->trans("ShowSending");
1623
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1624
			}
1625
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1626
			$linkclose.=' class="classfortooltip"';
1627
		}
1628
1629
		$linkstart = '<a href="'.$url.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
1630
		$linkend='</a>';
1631
1632
		$result .= $linkstart;
1633
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1634
		if ($withpicto != 2) $result.= $this->ref;
1635
		$result .= $linkend;
1636
1637
		return $result;
1638
	}
1639
1640
	/**
1641
	 *	Return status label
1642
	 *
1643
	 *	@param      int		$mode      	0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
1644
	 *	@return     string      		Libelle
1645
	 */
1646
	function getLibStatut($mode=0)
1647
	{
1648
		return $this->LibStatut($this->statut,$mode);
1649
	}
1650
1651
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1652
	/**
1653
	 * Return label of a status
1654
	 *
1655
	 * @param      int		$statut		Id statut
1656
	 * @param      int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
1657
	 * @return     string				Label of status
1658
	 */
1659
	function LibStatut($statut,$mode)
1660
	{
1661
        // phpcs:enable
1662
		global $langs;
1663
1664
		if ($mode==0)
1665
		{
1666
			if ($statut==0) return $langs->trans($this->statuts[$statut]);
1667
			elseif ($statut==1) return $langs->trans($this->statuts[$statut]);
1668
			elseif ($statut==2) return $langs->trans($this->statuts[$statut]);
1669
		}
1670
		elseif ($mode==1)
1671
		{
1672
			if ($statut==0) return $langs->trans($this->statutshorts[$statut]);
1673
			elseif ($statut==1) return $langs->trans($this->statutshorts[$statut]);
1674
			elseif ($statut==2) return $langs->trans($this->statutshorts[$statut]);
1675
		}
1676
		elseif ($mode == 3)
1677
		{
1678
			if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]),'statut0');
1679
			elseif ($statut==1) return img_picto($langs->trans($this->statuts[$statut]),'statut4');
1680
			elseif ($statut==2) return img_picto($langs->trans($this->statuts[$statut]),'statut6');
1681
		}
1682
		elseif ($mode == 4)
1683
		{
1684
			if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]),'statut0').' '.$langs->trans($this->statuts[$statut]);
1685
			elseif ($statut==1) return img_picto($langs->trans($this->statuts[$statut]),'statut4').' '.$langs->trans($this->statuts[$statut]);
1686
			elseif ($statut==2) return img_picto($langs->trans($this->statuts[$statut]),'statut6').' '.$langs->trans($this->statuts[$statut]);
1687
		}
1688
		elseif ($mode == 5)
1689
		{
1690
			if ($statut==0) return $langs->trans($this->statutshorts[$statut]).' '.img_picto($langs->trans($this->statuts[$statut]),'statut0');
1691
			elseif ($statut==1) return $langs->trans($this->statutshorts[$statut]).' '.img_picto($langs->trans($this->statuts[$statut]),'statut4');
1692
			elseif ($statut==2) return $langs->trans($this->statutshorts[$statut]).' '.img_picto($langs->trans($this->statuts[$statut]),'statut6');
1693
		}
1694
	}
1695
1696
	/**
1697
	 *  Initialise an instance with random values.
1698
	 *  Used to build previews or test instances.
1699
	 *	id must be 0 if object instance is a specimen.
1700
	 *
1701
	 *  @return	void
1702
	 */
1703
	function initAsSpecimen()
1704
	{
1705
		global $langs;
1706
1707
		$now=dol_now();
1708
1709
		dol_syslog(get_class($this)."::initAsSpecimen");
1710
1711
		// Load array of products prodids
1712
		$num_prods = 0;
1713
		$prodids = array();
1714
		$sql = "SELECT rowid";
1715
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
1716
		$sql.= " WHERE entity IN (".getEntity('product').")";
1717
		$resql = $this->db->query($sql);
1718
		if ($resql)
1719
		{
1720
			$num_prods = $this->db->num_rows($resql);
1721
			$i = 0;
1722
			while ($i < $num_prods)
1723
			{
1724
				$i++;
1725
				$row = $this->db->fetch_row($resql);
1726
				$prodids[$i] = $row[0];
1727
			}
1728
		}
1729
1730
		$order=new Commande($this->db);
1731
		$order->initAsSpecimen();
1732
1733
		// Initialise parametres
1734
		$this->id=0;
1735
		$this->ref = 'SPECIMEN';
1736
		$this->specimen=1;
1737
		$this->statut               = 1;
1738
		$this->livraison_id         = 0;
1739
		$this->date                 = $now;
1740
		$this->date_creation        = $now;
1741
		$this->date_valid           = $now;
1742
		$this->date_delivery        = $now;
1743
		$this->date_expedition      = $now + 24*3600;
1744
1745
		$this->entrepot_id          = 0;
1746
		$this->fk_delivery_address  = 0;
1747
		$this->socid                = 1;
1748
1749
		$this->commande_id          = 0;
1750
		$this->commande             = $order;
1751
1752
		$this->origin_id            = 1;
1753
		$this->origin               = 'commande';
1754
1755
		$this->note_private			= 'Private note';
1756
		$this->note_public			= 'Public note';
1757
1758
		$nbp = 5;
1759
		$xnbp = 0;
1760
		while ($xnbp < $nbp)
1761
		{
1762
			$line=new ExpeditionLigne($this->db);
1763
			$line->desc=$langs->trans("Description")." ".$xnbp;
1764
			$line->libelle=$langs->trans("Description")." ".$xnbp;
1765
			$line->qty=10;
1766
			$line->qty_asked=5;
1767
			$line->qty_shipped=4;
1768
			$line->fk_product=$this->commande->lines[$xnbp]->fk_product;
1769
1770
			$this->lines[]=$line;
1771
			$xnbp++;
1772
		}
1773
	}
1774
1775
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1776
	/**
1777
	 *	Set the planned delivery date
1778
	 *
1779
	 *	@param      User			$user        		Objet utilisateur qui modifie
1780
	 *	@param      timestamp		$date_livraison     Date de livraison
1781
	 *	@return     int         						<0 if KO, >0 if OK
1782
	 */
1783
	function set_date_livraison($user, $date_livraison)
1784
	{
1785
        // phpcs:enable
1786
		if ($user->rights->expedition->creer)
1787
		{
1788
			$sql = "UPDATE ".MAIN_DB_PREFIX."expedition";
1789
			$sql.= " SET date_delivery = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
1790
			$sql.= " WHERE rowid = ".$this->id;
1791
1792
			dol_syslog(get_class($this)."::set_date_livraison", LOG_DEBUG);
1793
			$resql=$this->db->query($sql);
1794
			if ($resql)
1795
			{
1796
				$this->date_delivery = $date_livraison;
1797
				return 1;
1798
			}
1799
			else
1800
			{
1801
				$this->error=$this->db->error();
1802
				return -1;
1803
			}
1804
		}
1805
		else
1806
		{
1807
			return -2;
1808
		}
1809
	}
1810
1811
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1812
	/**
1813
	 *	Fetch deliveries method and return an array. Load array this->meths(rowid=>label).
1814
	 *
1815
	 * 	@return	void
1816
	 */
1817
	function fetch_delivery_methods()
1818
	{
1819
        // phpcs:enable
1820
		global $langs;
1821
		$this->meths = array();
1822
1823
		$sql = "SELECT em.rowid, em.code, em.libelle";
1824
		$sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1825
		$sql.= " WHERE em.active = 1";
1826
		$sql.= " ORDER BY em.libelle ASC";
1827
1828
		$resql = $this->db->query($sql);
1829
		if ($resql)
1830
		{
1831
			while ($obj = $this->db->fetch_object($resql))
1832
			{
1833
				$label=$langs->trans('SendingMethod'.$obj->code);
1834
				$this->meths[$obj->rowid] = ($label != 'SendingMethod'.$obj->code?$label:$obj->libelle);
1835
			}
1836
		}
1837
	}
1838
1839
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1840
	/**
1841
	 *  Fetch all deliveries method and return an array. Load array this->listmeths.
1842
	 *
1843
	 *  @param  id      $id     only this carrier, all if none
1844
	 *  @return void
1845
	 */
1846
	function list_delivery_methods($id='')
1847
	{
1848
        // phpcs:enable
1849
		global $langs;
1850
1851
		$this->listmeths = array();
1852
		$i=0;
1853
1854
		$sql = "SELECT em.rowid, em.code, em.libelle, em.description, em.tracking, em.active";
1855
		$sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1856
		if ($id!='') $sql.= " WHERE em.rowid=".$id;
1857
1858
		$resql = $this->db->query($sql);
1859
		if ($resql)
1860
		{
1861
			while ($obj = $this->db->fetch_object($resql))
1862
			{
1863
				$this->listmeths[$i]['rowid'] = $obj->rowid;
1864
				$this->listmeths[$i]['code'] = $obj->code;
1865
				$label=$langs->trans('SendingMethod'.$obj->code);
1866
				$this->listmeths[$i]['libelle'] = ($label != 'SendingMethod'.$obj->code?$label:$obj->libelle);
1867
				$this->listmeths[$i]['description'] = $obj->description;
1868
				$this->listmeths[$i]['tracking'] = $obj->tracking;
1869
				$this->listmeths[$i]['active'] = $obj->active;
1870
				$i++;
1871
			}
1872
		}
1873
	}
1874
1875
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1876
	/**
1877
	 *  Update/create delivery method.
1878
	 *
1879
	 *  @param	string      $id     id method to activate
1880
	 *
1881
	 *  @return void
1882
	 */
1883
	function update_delivery_method($id='')
1884
	{
1885
        // phpcs:enable
1886
		if ($id=='')
1887
		{
1888
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."c_shipment_mode (code, libelle, description, tracking)";
1889
			$sql.=" VALUES ('".$this->db->escape($this->update['code'])."','".$this->db->escape($this->update['libelle'])."','".$this->db->escape($this->update['description'])."','".$this->db->escape($this->update['tracking'])."')";
1890
			$resql = $this->db->query($sql);
1891
		}
1892
		else
1893
		{
1894
			$sql = "UPDATE ".MAIN_DB_PREFIX."c_shipment_mode SET";
1895
			$sql.= " code='".$this->db->escape($this->update['code'])."'";
1896
			$sql.= ",libelle='".$this->db->escape($this->update['libelle'])."'";
1897
			$sql.= ",description='".$this->db->escape($this->update['description'])."'";
1898
			$sql.= ",tracking='".$this->db->escape($this->update['tracking'])."'";
1899
			$sql.= " WHERE rowid=".$id;
1900
			$resql = $this->db->query($sql);
1901
		}
1902
		if ($resql < 0) dol_print_error($this->db,'');
1903
	}
1904
1905
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1906
	/**
1907
	 *  Activate delivery method.
1908
	 *
1909
	 *  @param      id      $id     id method to activate
1910
	 *
1911
	 *  @return void
1912
	 */
1913
	function activ_delivery_method($id)
1914
	{
1915
        // phpcs:enable
1916
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'c_shipment_mode SET active=1';
1917
		$sql.= ' WHERE rowid='.$id;
1918
1919
		$resql = $this->db->query($sql);
1920
	}
1921
1922
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1923
	/**
1924
	 *  DesActivate delivery method.
1925
	 *
1926
	 *  @param      id      $id     id method to desactivate
1927
	 *
1928
	 *  @return void
1929
	 */
1930
	function disable_delivery_method($id)
1931
	{
1932
        // phpcs:enable
1933
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'c_shipment_mode SET active=0';
1934
		$sql.= ' WHERE rowid='.$id;
1935
1936
		$resql = $this->db->query($sql);
1937
	}
1938
1939
1940
	/**
1941
	 * Forge an set tracking url
1942
	 *
1943
	 * @param	string	$value		Value
1944
	 * @return	void
1945
	 */
1946
	function getUrlTrackingStatus($value='')
1947
	{
1948
		if (! empty($this->shipping_method_id))
1949
		{
1950
			$sql = "SELECT em.code, em.tracking";
1951
			$sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em";
1952
			$sql.= " WHERE em.rowid = ".$this->shipping_method_id;
1953
1954
			$resql = $this->db->query($sql);
1955
			if ($resql)
1956
			{
1957
				if ($obj = $this->db->fetch_object($resql))
1958
				{
1959
					$tracking = $obj->tracking;
1960
				}
1961
			}
1962
		}
1963
1964
		if (!empty($tracking) && !empty($value))
1965
		{
1966
			$url = str_replace('{TRACKID}', $value, $tracking);
1967
			$this->tracking_url = sprintf('<a target="_blank" href="%s">'.($value?$value:'url').'</a>',$url,$url);
1968
		}
1969
		else
1970
		{
1971
			$this->tracking_url = $value;
1972
		}
1973
	}
1974
1975
	/**
1976
	 *	Classify the shipping as closed.
1977
	 *
1978
	 *	@return     int     <0 if KO, >0 if OK
1979
	 */
1980
	function setClosed()
1981
	{
1982
		global $conf,$langs,$user;
1983
1984
		$error=0;
1985
1986
		$this->db->begin();
1987
1988
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut='.self::STATUS_CLOSED;
1989
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
1990
1991
		$resql=$this->db->query($sql);
1992
		if ($resql)
1993
		{
1994
			// Set order billed if 100% of order is shipped (qty in shipment lines match qty in order lines)
1995
			if ($this->origin == 'commande' && $this->origin_id > 0)
1996
			{
1997
				$order = new Commande($this->db);
1998
				$order->fetch($this->origin_id);
1999
2000
				$order->loadExpeditions(self::STATUS_CLOSED);		// Fill $order->expeditions = array(orderlineid => qty)
2001
2002
				$shipments_match_order = 1;
2003
				foreach($order->lines as $line)
2004
				{
2005
					$lineid = $line->id;
2006
					$qty = $line->qty;
2007
					if (($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) && $order->expeditions[$lineid] != $qty)
2008
					{
2009
						$shipments_match_order = 0;
2010
						$text='Qty for order line id '.$lineid.' is '.$qty.'. However in the shipments with status Expedition::STATUS_CLOSED='.self::STATUS_CLOSED.' we have qty = '.$order->expeditions[$lineid].', so we can t close order';
2011
						dol_syslog($text);
2012
						break;
2013
					}
2014
				}
2015
				if ($shipments_match_order)
2016
				{
2017
					dol_syslog("Qty for the ".count($order->lines)." lines of order have same value for shipments with status Expedition::STATUS_CLOSED=".self::STATUS_CLOSED.', so we close order');
2018
					$order->cloture($user);
2019
				}
2020
			}
2021
2022
			$this->statut=self::STATUS_CLOSED;
2023
2024
2025
			// If stock increment is done on closing
2026
			if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE))
2027
			{
2028
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2029
2030
				$langs->load("agenda");
2031
2032
				// Loop on each product line to add a stock movement
2033
				// TODO possibilite d'expedier a partir d'une propale ou autre origine ?
2034
				$sql = "SELECT cd.fk_product, cd.subprice,";
2035
				$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
2036
				$sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
2037
				$sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
2038
				$sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
2039
				$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
2040
				$sql.= " WHERE ed.fk_expedition = ".$this->id;
2041
				$sql.= " AND cd.rowid = ed.fk_origin_line";
2042
2043
				dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2044
				$resql=$this->db->query($sql);
2045
				if ($resql)
2046
				{
2047
					$cpt = $this->db->num_rows($resql);
2048
					for ($i = 0; $i < $cpt; $i++)
2049
					{
2050
						$obj = $this->db->fetch_object($resql);
2051
						if (empty($obj->edbrowid))
2052
						{
2053
							$qty = $obj->qty;
2054
						}
2055
						else
2056
						{
2057
							$qty = $obj->edbqty;
2058
						}
2059
						if ($qty <= 0) continue;
2060
						dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
2061
2062
						$mouvS = new MouvementStock($this->db);
2063
						$mouvS->origin = &$this;
2064
2065
						if (empty($obj->edbrowid))
2066
						{
2067
							// line without batch detail
2068
2069
							// 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
2070
							$result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr",$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...
2071
							if ($result < 0) {
2072
								$this->error = $mouvS->error;
2073
								$this->errors = $mouvS->errors;
2074
								$error++; break;
2075
							}
2076
						}
2077
						else
2078
						{
2079
							// line with batch detail
2080
2081
							// 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
2082
							$result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr",$numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
2083
							if ($result < 0) {
2084
								$this->error = $mouvS->error;
2085
								$this->errors = $mouvS->errors;
2086
								$error++; break;
2087
							}
2088
						}
2089
					}
2090
				}
2091
				else
2092
				{
2093
					$this->error=$this->db->lasterror();
2094
					$error++;
2095
				}
2096
			}
2097
2098
			// Call trigger
2099
			if (! $error)
2100
			{
2101
				$result=$this->call_trigger('SHIPPING_CLOSED',$user);
2102
				if ($result < 0) {
2103
					$error++;
2104
				}
2105
			}
2106
		}
2107
		else
2108
		{
2109
			dol_print_error($this->db);
2110
			$error++;
2111
		}
2112
2113
		if (! $error)
2114
		{
2115
			$this->db->commit();
2116
			return 1;
2117
		}
2118
		else
2119
		{
2120
			$this->db->rollback();
2121
			return -1;
2122
		}
2123
	}
2124
2125
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2126
	/**
2127
	 *	Classify the shipping as invoiced (used when WORKFLOW_BILL_ON_SHIPMENT is on)
2128
	 *
2129
	 *	@return     int     <0 if ko, >0 if ok
2130
	 */
2131
	function set_billed()
2132
	{
2133
        // phpcs:enable
2134
		global $user;
2135
		$error=0;
2136
2137
		$this->db->begin();
2138
2139
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=2, billed=1';    // TODO Update only billed
2140
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
2141
2142
		$resql=$this->db->query($sql);
2143
		if ($resql)
2144
		{
2145
			$this->statut=2;
2146
			$this->billed=1;
2147
2148
			// Call trigger
2149
			$result=$this->call_trigger('SHIPPING_BILLED',$user);
2150
			if ($result < 0) {
2151
				$error++;
2152
			}
2153
		} else {
2154
			$error++;
2155
			$this->errors[]=$this->db->lasterror;
2156
		}
2157
2158
		if (empty($error)) {
2159
			$this->db->commit();
2160
			return 1;
2161
		}
2162
		else
2163
		{
2164
			$this->db->rollback();
2165
			return -1;
2166
		}
2167
	}
2168
2169
	/**
2170
	 *	Classify the shipping as validated/opened
2171
	 *
2172
	 *	@return     int     <0 if KO, 0 if already open, >0 if OK
2173
	 */
2174
	function reOpen()
2175
	{
2176
		global $conf,$langs,$user;
2177
2178
		$error=0;
2179
2180
		// Protection. This avoid to move stock later when we should not
2181
		if ($this->statut == self::STATUS_VALIDATED)
2182
		{
2183
			return 0;
2184
		}
2185
2186
		$this->db->begin();
2187
2188
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=1';
2189
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0';
2190
2191
		$resql=$this->db->query($sql);
2192
		if ($resql)
2193
		{
2194
			$this->statut=1;
2195
			$this->billed=0;
2196
2197
			// If stock increment is done on closing
2198
			if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE))
2199
			{
2200
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
2201
2202
				$langs->load("agenda");
2203
2204
				// Loop on each product line to add a stock movement
2205
				// TODO possibilite d'expedier a partir d'une propale ou autre origine
2206
				$sql = "SELECT cd.fk_product, cd.subprice,";
2207
				$sql.= " ed.rowid, ed.qty, ed.fk_entrepot,";
2208
				$sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
2209
				$sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
2210
				$sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed";
2211
				$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
2212
				$sql.= " WHERE ed.fk_expedition = ".$this->id;
2213
				$sql.= " AND cd.rowid = ed.fk_origin_line";
2214
2215
				dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
2216
				$resql=$this->db->query($sql);
2217
				if ($resql)
2218
				{
2219
					$cpt = $this->db->num_rows($resql);
2220
					for ($i = 0; $i < $cpt; $i++)
2221
					{
2222
						$obj = $this->db->fetch_object($resql);
2223
						if (empty($obj->edbrowid))
2224
						{
2225
							$qty = $obj->qty;
2226
						}
2227
						else
2228
						{
2229
							$qty = $obj->edbqty;
2230
						}
2231
						if ($qty <= 0) continue;
2232
						dol_syslog(get_class($this)."::reopen expedition movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
2233
2234
						//var_dump($this->lines[$i]);
2235
						$mouvS = new MouvementStock($this->db);
2236
						$mouvS->origin = &$this;
2237
2238
						if (empty($obj->edbrowid))
2239
						{
2240
							// line without batch detail
2241
2242
							// 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
2243
							$result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr",$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...
2244
							if ($result < 0) {
2245
								$this->error = $mouvS->error;
2246
								$this->errors = $mouvS->errors;
2247
								$error++; break;
2248
							}
2249
						}
2250
						else
2251
						{
2252
							// line with batch detail
2253
2254
							// 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
2255
							$result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr",$numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
2256
							if ($result < 0) {
2257
								$this->error = $mouvS->error;
2258
								$this->errors = $mouvS->errors;
2259
								$error++; break;
2260
							}
2261
						}
2262
					}
2263
				}
2264
				else
2265
				{
2266
					$this->error=$this->db->lasterror();
2267
					$error++;
2268
				}
2269
			}
2270
2271
			if (! $error)
2272
			{
2273
				// Call trigger
2274
				$result=$this->call_trigger('SHIPPING_REOPEN',$user);
2275
				if ($result < 0) {
2276
					$error++;
2277
				}
2278
   			}
2279
		} else {
2280
			$error++;
2281
			$this->errors[]=$this->db->lasterror();
2282
		}
2283
2284
		if (! $error)
2285
		{
2286
			$this->db->commit();
2287
			return 1;
2288
		}
2289
		else
2290
		{
2291
			$this->db->rollback();
2292
			return -1;
2293
		}
2294
	}
2295
2296
	/**
2297
	 *  Create a document onto disk according to template module.
2298
	 *
2299
	 *  @param	    string		$modele			Force the model to using ('' to not force)
2300
	 *  @param		Translate	$outputlangs	object lang to use for translations
2301
	 *  @param      int			$hidedetails    Hide details of lines
2302
	 *  @param      int			$hidedesc       Hide description
2303
	 *  @param      int			$hideref        Hide ref
2304
     *  @param      null|array  $moreparams     Array to provide more information
2305
	 *  @return     int         				0 if KO, 1 if OK
2306
	 */
2307
	public function generateDocument($modele, $outputlangs,$hidedetails=0, $hidedesc=0, $hideref=0,$moreparams=null)
2308
	{
2309
		global $conf,$langs;
2310
2311
		$langs->load("sendings");
2312
2313
		if (! dol_strlen($modele)) {
2314
2315
			$modele = 'rouget';
2316
2317
			if ($this->modelpdf) {
2318
				$modele = $this->modelpdf;
2319
			} elseif (! empty($conf->global->EXPEDITION_ADDON_PDF)) {
2320
				$modele = $conf->global->EXPEDITION_ADDON_PDF;
2321
			}
2322
		}
2323
2324
		$modelpath = "core/modules/expedition/doc/";
2325
2326
		$this->fetch_origin();
2327
2328
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref,$moreparams);
2329
	}
2330
2331
	/**
2332
	 * Function used to replace a thirdparty id with another one.
2333
	 *
2334
	 * @param DoliDB $db Database handler
2335
	 * @param int $origin_id Old thirdparty id
2336
	 * @param int $dest_id New thirdparty id
2337
	 * @return bool
2338
	 */
2339
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2340
	{
2341
		$tables = array(
2342
			'expedition'
2343
		);
2344
2345
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2346
	}
2347
}
2348
2349
2350
/**
2351
 * Classe de gestion des lignes de bons d'expedition
2352
 */
2353
class ExpeditionLigne extends CommonObjectLine
2354
{
2355
	/**
2356
	 * @var string ID to identify managed object
2357
	 */
2358
	public $element='expeditiondet';
2359
2360
	/**
2361
	 * @var string Name of table without prefix where object is stored
2362
	 */
2363
	public $table_element='expeditiondet';
2364
2365
	/**
2366
	 * @deprecated
2367
	 * @see fk_origin_line
2368
	 */
2369
	public $origin_line_id;
2370
2371
	/**
2372
     * @var int ID
2373
     */
2374
	public $fk_origin_line;
2375
2376
	/**
2377
	 * @var int Id of shipment
2378
	 */
2379
	public $fk_expedition;
2380
2381
	/**
2382
     * @var DoliDB Database handler.
2383
     */
2384
    public $db;
2385
2386
    /**
2387
     * @var float qty asked From llx_expeditiondet
2388
     */
2389
    public $qty;
2390
2391
    /**
2392
     * @var float qty shipped
2393
     */
2394
    public $qty_shipped;
2395
2396
    /**
2397
     * @var int Id of product
2398
     */
2399
    public $fk_product;
2400
    public $detail_batch;
2401
2402
    /**
2403
     * @var int Id of warehouse
2404
     */
2405
	public $entrepot_id;
2406
2407
2408
    /**
2409
     * @var float qty asked From llx_commandedet or llx_propaldet
2410
     */
2411
	public $qty_asked;
2412
2413
    /**
2414
     * @deprecated
2415
     * @see product_ref
2416
     */
2417
    public $ref;
2418
2419
	/**
2420
	 * @var string product ref
2421
	 */
2422
	public $product_ref;
2423
2424
	/**
2425
	 * @deprecated
2426
	 * @see product_label
2427
	 */
2428
	public $libelle;
2429
2430
    /**
2431
     * @var string product label
2432
     */
2433
	public $product_label;
2434
2435
    /**
2436
     * @var string product description
2437
     * @deprecated
2438
     * @see product_desc
2439
     */
2440
    public $desc;
2441
2442
    /**
2443
     * @var string product description
2444
     */
2445
	public $product_desc;
2446
2447
    /**
2448
     * @var float weight
2449
     */
2450
    public $weight;
2451
    public $weight_units;
2452
2453
    /**
2454
     * @var float weight
2455
     */
2456
    public $length;
2457
    public $length_units;
2458
2459
    /**
2460
     * @var float weight
2461
     */
2462
    public $surface;
2463
    public $surface_units;
2464
2465
    /**
2466
     * @var float weight
2467
     */
2468
    public $volume;
2469
    public $volume_units;
2470
2471
	// Invoicing
2472
	public $remise_percent;
2473
    public $tva_tx;
2474
2475
    /**
2476
     * @var float total without tax
2477
     */
2478
    public $total_ht;
2479
2480
    /**
2481
     * @var float total with tax
2482
     */
2483
    public $total_ttc;
2484
2485
    /**
2486
     * @var float total vat
2487
     */
2488
    public $total_tva;
2489
2490
    /**
2491
     * @var float total localtax 1
2492
     */
2493
    public $total_localtax1;
2494
2495
    /**
2496
     * @var float total localtax 2
2497
     */
2498
    public $total_localtax2;
2499
2500
2501
    /**
2502
     *	Constructor
2503
     *
2504
     *  @param		DoliDB		$db      Database handler
2505
     */
2506
	function __construct($db)
2507
	{
2508
		$this->db=$db;
2509
	}
2510
2511
	/**
2512
	 *  Load line expedition
2513
	 *
2514
	 *  @param  int		$rowid          Id line order
2515
	 *  @return	int						<0 if KO, >0 if OK
2516
	 */
2517
	function fetch($rowid)
2518
	{
2519
		$sql = 'SELECT ed.rowid, ed.fk_expedition, ed.fk_entrepot, ed.fk_origin_line, ed.qty, ed.rang';
2520
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as ed';
2521
		$sql.= ' WHERE ed.rowid = '.$rowid;
2522
		$result = $this->db->query($sql);
2523
		if ($result)
2524
		{
2525
			$objp = $this->db->fetch_object($result);
2526
			$this->id				= $objp->rowid;
2527
			$this->fk_expedition	= $objp->fk_expedition;
2528
			$this->entrepot_id		= $objp->fk_entrepot;
2529
			$this->fk_origin_line	= $objp->fk_origin_line;
2530
			$this->qty				= $objp->qty;
2531
			$this->rang				= $objp->rang;
2532
2533
			$this->db->free($result);
2534
2535
			return 1;
2536
		}
2537
		else
2538
		{
2539
			$this->errors[] = $this->db->lasterror();
2540
			$this->error = $this->db->lasterror();
2541
			return -1;
2542
		}
2543
	}
2544
2545
	/**
2546
	 *	Insert line into database
2547
	 *
2548
	 *	@param      User	$user			User that modify
2549
	 *	@param      int		$notrigger		1 = disable triggers
2550
	 *	@return     int						<0 if KO, line id >0 if OK
2551
	 */
2552
	function insert($user=null, $notrigger=0)
2553
	{
2554
		global $langs, $conf;
2555
2556
		$error=0;
2557
2558
		// Check parameters
2559
		if (empty($this->fk_expedition) || empty($this->fk_origin_line) || ! is_numeric($this->qty))
2560
		{
2561
			$this->error = 'ErrorMandatoryParametersNotProvided';
2562
			return -1;
2563
		}
2564
		// Clean parameters
2565
		if (empty($this->entrepot_id)) $this->entrepot_id='null';
0 ignored issues
show
Documentation Bug introduced by
The property $entrepot_id was declared of type integer, but 'null' 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...
2566
2567
		$this->db->begin();
2568
2569
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."expeditiondet (";
2570
		$sql.= "fk_expedition";
2571
		$sql.= ", fk_entrepot";
2572
		$sql.= ", fk_origin_line";
2573
		$sql.= ", qty";
2574
		$sql.= ") VALUES (";
2575
		$sql.= $this->fk_expedition;
2576
		$sql.= ", ".$this->entrepot_id;
2577
		$sql.= ", ".$this->fk_origin_line;
2578
		$sql.= ", ".$this->qty;
2579
		$sql.= ")";
2580
2581
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
2582
		$resql = $this->db->query($sql);
2583
		if ($resql)
2584
		{
2585
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."expeditiondet");
2586
2587
			if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))
2588
			{
2589
				$result=$this->insertExtraFields();
2590
				if ($result < 0)
2591
				{
2592
					$error++;
2593
				}
2594
			}
2595
2596
			if (! $error && ! $notrigger)
2597
			{
2598
				// Call trigger
2599
				$result=$this->call_trigger('LINESHIPPING_INSERT',$user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 2552 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...
2600
				if ($result < 0)
2601
				{
2602
					$error++;
2603
				}
2604
				// End call triggers
2605
			}
2606
2607
			if (! $error) {
2608
				$this->db->commit();
2609
				return $this->id;
2610
			}
2611
2612
			foreach($this->errors as $errmsg)
2613
			{
2614
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
2615
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2616
			}
2617
2618
			$this->db->rollback();
2619
			return -1*$error;
2620
		}
2621
		else
2622
		{
2623
			$error++;
2624
		}
2625
	}
2626
2627
	/**
2628
	 * 	Delete shipment line.
2629
	 *
2630
	 *	@param		User	$user			User that modify
2631
	 *	@param		int		$notrigger		0=launch triggers after, 1=disable triggers
2632
	 * 	@return		int		>0 if OK, <0 if KO
2633
	 */
2634
	function delete($user = null, $notrigger = 0)
2635
	{
2636
		global $conf;
2637
2638
		$error=0;
2639
2640
		$this->db->begin();
2641
2642
		// delete batch expedition line
2643
		if ($conf->productbatch->enabled)
2644
		{
2645
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet_batch";
2646
			$sql.= " WHERE fk_expeditiondet = ".$this->id;
2647
2648
			if (!$this->db->query($sql))
2649
			{
2650
				$this->errors[]=$this->db->lasterror()." - sql=$sql";
2651
				$error++;
2652
			}
2653
		}
2654
2655
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet";
2656
		$sql.= " WHERE rowid = ".$this->id;
2657
2658
		if (! $error && $this->db->query($sql))
2659
		{
2660
			// Remove extrafields
2661
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2662
			{
2663
				$result=$this->deleteExtraFields();
2664
				if ($result < 0)
2665
				{
2666
					$this->errors[]=$this->error;
2667
					$error++;
2668
				}
2669
			}
2670
			if (! $error && ! $notrigger)
2671
			{
2672
				// Call trigger
2673
				$result=$this->call_trigger('LINESHIPPING_DELETE',$user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 2634 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...
2674
				if ($result < 0)
2675
				{
2676
					$this->errors[]=$this->error;
2677
					$error++;
2678
				}
2679
				// End call triggers
2680
			}
2681
		}
2682
		else
2683
		{
2684
			$this->errors[]=$this->db->lasterror()." - sql=$sql";
2685
			$error++;
2686
		}
2687
2688
		if (! $error) {
2689
			$this->db->commit();
2690
			return 1;
2691
		}
2692
		else
2693
		{
2694
			foreach($this->errors as $errmsg)
2695
			{
2696
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
2697
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2698
			}
2699
			$this->db->rollback();
2700
			return -1*$error;
2701
		}
2702
	}
2703
2704
	/**
2705
	 *  Update a line in database
2706
	 *
2707
	 *	@param		User	$user			User that modify
2708
	 *	@param		int		$notrigger		1 = disable triggers
2709
	 *  @return		int					< 0 if KO, > 0 if OK
2710
	 */
2711
	function update($user = null, $notrigger = 0)
2712
	{
2713
		global $conf;
2714
2715
		$error=0;
2716
2717
		dol_syslog(get_class($this)."::update id=$this->id, entrepot_id=$this->entrepot_id, product_id=$this->fk_product, qty=$this->qty");
2718
2719
		$this->db->begin();
2720
2721
		// Clean parameters
2722
		if (empty($this->qty)) $this->qty=0;
2723
		$qty=price2num($this->qty);
2724
		$remainingQty = 0;
2725
		$batch = null;
2726
		$batch_id = null;
2727
		$expedition_batch_id = null;
2728
		if (is_array($this->detail_batch)) 	// array of ExpeditionLineBatch
2729
		{
2730
			if (count($this->detail_batch) > 1)
2731
			{
2732
				dol_syslog(get_class($this).'::update only possible for one batch', LOG_ERR);
2733
				$this->errors[]='ErrorBadParameters';
2734
				$error++;
2735
			}
2736
			else
2737
			{
2738
				$batch = $this->detail_batch[0]->batch;
2739
				$batch_id = $this->detail_batch[0]->fk_origin_stock;
2740
				$expedition_batch_id = $this->detail_batch[0]->id;
2741
				if ($this->entrepot_id != $this->detail_batch[0]->entrepot_id)
2742
				{
2743
					dol_syslog(get_class($this).'::update only possible for batch of same warehouse', LOG_ERR);
2744
					$this->errors[]='ErrorBadParameters';
2745
					$error++;
2746
				}
2747
				$qty = price2num($this->detail_batch[0]->qty);
2748
			}
2749
		}
2750
		else if (! empty($this->detail_batch))
2751
		{
2752
			$batch = $this->detail_batch->batch;
2753
			$batch_id = $this->detail_batch->fk_origin_stock;
2754
			$expedition_batch_id = $this->detail_batch->id;
2755
			if ($this->entrepot_id != $this->detail_batch->entrepot_id)
2756
			{
2757
				dol_syslog(get_class($this).'::update only possible for batch of same warehouse', LOG_ERR);
2758
				$this->errors[]='ErrorBadParameters';
2759
				$error++;
2760
			}
2761
			$qty = price2num($this->detail_batch->qty);
2762
		}
2763
2764
		// check parameters
2765
		if (! isset($this->id) || ! isset($this->entrepot_id))
2766
		{
2767
			dol_syslog(get_class($this).'::update missing line id and/or warehouse id', LOG_ERR);
2768
			$this->errors[]='ErrorMandatoryParametersNotProvided';
2769
			$error++;
2770
			return -1;
2771
		}
2772
2773
		// update lot
2774
2775
		if (! empty($batch) && $conf->productbatch->enabled)
2776
		{
2777
			dol_syslog(get_class($this)."::update expedition batch id=$expedition_batch_id, batch_id=$batch_id, batch=$batch");
2778
2779
			if (empty($batch_id) || empty($this->fk_product)) {
2780
				dol_syslog(get_class($this).'::update missing fk_origin_stock (batch_id) and/or fk_product', LOG_ERR);
2781
				$this->errors[]='ErrorMandatoryParametersNotProvided';
2782
				$error++;
2783
			}
2784
2785
			// fetch remaining lot qty
2786
			require_once DOL_DOCUMENT_ROOT.'/expedition/class/expeditionbatch.class.php';
2787
			if (! $error && ($lotArray = ExpeditionLineBatch::fetchAll($this->db, $this->id)) < 0)
2788
			{
2789
				$this->errors[]=$this->db->lasterror()." - ExpeditionLineBatch::fetchAll";
2790
				$error++;
2791
			}
2792
			else
2793
			{
2794
				// caculate new total line qty
2795
				foreach ($lotArray as $lot)
0 ignored issues
show
Bug introduced by
The variable $lotArray 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...
2796
				{
2797
					if ($expedition_batch_id != $lot->id)
2798
					{
2799
						$remainingQty += $lot->qty;
2800
					}
2801
				}
2802
				$qty += $remainingQty;
2803
2804
				//fetch lot details
2805
2806
				// fetch from product_lot
2807
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
2808
				$lot = new Productlot($this->db);
2809
				if ($lot->fetch(0,$this->fk_product,$batch) < 0)
2810
				{
2811
					$this->errors[] = $lot->errors;
2812
					$error++;
2813
				}
2814
				if (! $error && ! empty($expedition_batch_id))
2815
				{
2816
					// delete lot expedition line
2817
					$sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet_batch";
2818
					$sql.= " WHERE fk_expeditiondet = ".$this->id;
2819
					$sql.= " AND rowid = ".$expedition_batch_id;
2820
2821
					if (!$this->db->query($sql))
2822
					{
2823
						$this->errors[]=$this->db->lasterror()." - sql=$sql";
2824
						$error++;
2825
					}
2826
				}
2827
				if (! $error && $this->detail_batch->qty > 0)
2828
				{
2829
					// create lot expedition line
2830
					if (isset($lot->id))
2831
					{
2832
						$shipmentLot = new ExpeditionLineBatch($this->db);
2833
						$shipmentLot->batch = $lot->batch;
2834
						$shipmentLot->eatby = $lot->eatby;
2835
						$shipmentLot->sellby = $lot->sellby;
2836
						$shipmentLot->entrepot_id = $this->detail_batch->entrepot_id;
2837
						$shipmentLot->qty = $this->detail_batch->qty;
2838
						$shipmentLot->fk_origin_stock = $batch_id;
2839
						if ($shipmentLot->create($this->id) < 0)
2840
						{
2841
							$this->errors[]=$shipmentLot->errors;
2842
							$error++;
2843
						}
2844
					}
2845
				}
2846
			}
2847
		}
2848
		if (! $error)
2849
		{
2850
			// update line
2851
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
2852
			$sql.= " fk_entrepot = ".($this->entrepot_id > 0 ? $this->entrepot_id : 'null');
2853
			$sql.= " , qty = ".$qty;
2854
			$sql.= " WHERE rowid = ".$this->id;
2855
2856
			if (!$this->db->query($sql))
2857
			{
2858
				$this->errors[]=$this->db->lasterror()." - sql=$sql";
2859
				$error++;
2860
			}
2861
		}
2862
2863
		if (! $error)
2864
		{
2865
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2866
			{
2867
				$result=$this->insertExtraFields();
2868
				if ($result < 0)
2869
				{
2870
					$this->errors[]=$this->error;
2871
					$error++;
2872
				}
2873
			}
2874
		}
2875
2876
		if (! $error && ! $notrigger)
2877
		{
2878
			// Call trigger
2879
			$result=$this->call_trigger('LINESHIPPING_UPDATE',$user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 2711 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...
2880
			if ($result < 0)
2881
			{
2882
				$this->errors[]=$this->error;
2883
				$error++;
2884
			}
2885
			// End call triggers
2886
		}
2887
		if (!$error) {
2888
			$this->db->commit();
2889
			return 1;
2890
		}
2891
		else
2892
		{
2893
			foreach($this->errors as $errmsg)
2894
			{
2895
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2896
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2897
			}
2898
			$this->db->rollback();
2899
			return -1*$error;
2900
		}
2901
	}
2902
}
2903