Completed
Branch develop (c022b0)
by
unknown
40:00
created

Expedition::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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