Passed
Pull Request — master (#2)
by
unknown
26:19
created

Commande   F

Complexity

Total Complexity 622

Size/Duplication

Total Lines 3815
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1891
dl 0
loc 3815
rs 0.8
c 0
b 0
f 0
wmc 622

48 Methods

Rating   Name   Duplication   Size   Complexity  
B classifyUnBilled() 0 47 7
A stock_array() 0 31 4
A getNbOfShipments() 0 26 3
F fetch() 0 139 15
B cloture() 0 51 9
F addline() 0 210 29
B loadExpeditions() 0 37 7
D update() 0 85 43
A getLibStatut() 0 3 1
C liste_array() 0 58 15
A hasDelay() 0 11 3
C set_draft() 0 71 15
A getLinesArray() 0 3 1
B info() 0 47 6
F updateline() 0 180 24
B load_board() 0 51 7
B insert_discount() 0 68 5
D getNomUrl() 0 68 28
A showDelay() 0 9 3
A getNbOfProductsLines() 0 8 3
B classifyBilled() 0 48 9
D LibStatut() 0 78 94
C cancel() 0 70 12
B set_remise_absolue() 0 54 11
F valid() 0 150 28
B set_reopen() 0 50 8
B set_date_livraison() 0 54 11
B fetch_lines() 0 107 4
C createFromClone() 0 75 12
B set_remise() 0 54 11
A getNbOfServicesLines() 0 8 3
C createFromProposal() 0 116 11
B set_ref_client() 0 53 11
B add_product() 0 51 8
B set_date() 0 54 11
B demand_reason() 0 61 10
A getLabelSource() 0 8 2
A nb_expedition() 0 17 2
A getNextNumRef() 0 46 5
A generateDocument() 0 20 4
B availability() 0 60 10
D delete() 0 118 27
A load_state_board() 0 34 5
B deleteline() 0 66 6
A replaceThirdparty() 0 7 1
F create() 0 292 71
B initAsSpecimen() 0 78 6
A __construct() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like Commande often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Commande, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* Copyright (C) 2003-2006 Rodolphe Quiedeville <[email protected]>
3
 * Copyright (C) 2004-2012 Laurent Destailleur  <[email protected]>
4
 * Copyright (C) 2005-2014 Regis Houssin        <[email protected]>
5
 * Copyright (C) 2006      Andre Cianfarani     <[email protected]>
6
 * Copyright (C) 2010-2016 Juanjo Menent        <[email protected]>
7
 * Copyright (C) 2011      Jean Heimburger      <[email protected]>
8
 * Copyright (C) 2012-2014 Christophe Battarel  <[email protected]>
9
 * Copyright (C) 2012      Cedric Salvador      <[email protected]>
10
 * Copyright (C) 2013      Florian Henry		<[email protected]>
11
 * Copyright (C) 2014-2015 Marcos García        <[email protected]>
12
 * Copyright (C) 2018      Nicolas ZABOURI	<[email protected]>
13
 * Copyright (C) 2016-2018 Ferran Marcet        <[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 *  \file       htdocs/commande/class/commande.class.php
31
 *  \ingroup    commande
32
 *  \brief      Fichier des classes de commandes
33
 */
34
include_once DOL_DOCUMENT_ROOT .'/core/class/commonorder.class.php';
35
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobjectline.class.php';
36
require_once DOL_DOCUMENT_ROOT .'/product/class/product.class.php';
37
require_once DOL_DOCUMENT_ROOT .'/margin/lib/margins.lib.php';
38
require_once DOL_DOCUMENT_ROOT .'/multicurrency/class/multicurrency.class.php';
39
40
/**
41
 *  Class to manage customers orders
42
 */
43
class Commande extends CommonOrder
44
{
45
	/**
46
	 * @var string ID to identify managed object
47
	 */
48
	public $element='commande';
49
50
	/**
51
	 * @var string Name of table without prefix where object is stored
52
	 */
53
	public $table_element='commande';
54
55
	/**
56
	 * @var int    Name of subtable line
57
	 */
58
	public $table_element_line = 'commandedet';
59
60
	public $class_element_line = 'OrderLine';
61
62
	/**
63
	 * @var int Field with ID of parent key if this field has a parent
64
	 */
65
	public $fk_element = 'fk_commande';
66
67
	/**
68
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
69
	 */
70
	public $picto = 'order';
71
72
	/**
73
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
74
	 * @var int
75
	 */
76
	public $ismultientitymanaged = 1;
77
78
	/**
79
	 * 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
80
	 * @var integer
81
	 */
82
	public $restrictiononfksoc = 1;
83
84
	/**
85
	 * {@inheritdoc}
86
	 */
87
	protected $table_ref_field = 'ref';
88
89
	/**
90
	 * Client ID
91
	 * @var int
92
	 */
93
	public $socid;
94
95
	public $ref_client;
96
	public $ref_int;
97
	public $contactid;
98
99
	/**
100
	 * Status of the order
101
	 * @var int
102
	 */
103
	public $statut;
104
105
	/**
106
	 * Billed
107
	 * @var int
108
	 */
109
	public $billed;		// billed or not
110
111
    /**
112
     * @var int Draft Status of the order
113
     */
114
    public $brouillon;
115
    public $cond_reglement_code;
116
117
	/**
118
     * @var int ID
119
     */
120
	public $fk_account;
121
122
	/**
123
	 * It holds the label of the payment mode. Use it in case translation cannot be found.
124
	 * @var string
125
	 */
126
	public $mode_reglement;
127
128
	/**
129
	 * Payment mode id
130
	 * @var int
131
	 */
132
	public $mode_reglement_id;
133
134
	/**
135
	 * Payment mode code
136
	 * @var string
137
	 */
138
	public $mode_reglement_code;
139
140
	/**
141
	 * Availability delivery time id
142
	 * @var int
143
	 */
144
	public $availability_id;
145
146
	/**
147
	 * Availability delivery time code
148
	 * @var string
149
	 */
150
	public $availability_code;
151
152
	/**
153
	 * Label of availability delivery time. Use it in case translation cannot be found.
154
	 * @var string
155
	 */
156
	public $availability;
157
158
	public $demand_reason_id;   // Source reason. Why we receive order (after a phone campaign, ...)
159
	public $demand_reason_code;
160
	public $date;				// Date commande
161
162
	/**
163
	 * @deprecated
164
	 * @see date
165
	 */
166
	public $date_commande;
167
168
	public $date_livraison;	    // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
169
170
	/**
171
     * @var int ID
172
     */
173
	public $fk_remise_except;
174
175
	public $remise_percent;
176
	public $remise_absolue;
177
	public $info_bits;
178
	public $rang;
179
	public $special_code;
180
	public $source;			    // Order mode. How we received order (by phone, by email, ...)
181
	public $extraparams=array();
182
183
	public $linked_objects=array();
184
185
	public $user_author_id;
186
	public $user_valid;
187
188
	/**
189
	 * @var OrderLine[]
190
	 */
191
	public $lines = array();
192
193
	// Multicurrency
194
	/**
195
     * @var int ID
196
     */
197
	public $fk_multicurrency;
198
199
	public $multicurrency_code;
200
	public $multicurrency_tx;
201
	public $multicurrency_total_ht;
202
	public $multicurrency_total_tva;
203
	public $multicurrency_total_ttc;
204
205
	public $oldcopy;
206
207
	/**
208
	 * ERR Not enough stock
209
	 */
210
	const STOCK_NOT_ENOUGH_FOR_ORDER = -3;
211
212
	/**
213
	 * Canceled status
214
	 */
215
	const STATUS_CANCELED = -1;
216
	/**
217
	 * Draft status
218
	 */
219
	const STATUS_DRAFT = 0;
220
	/**
221
	 * Validated status
222
	 */
223
	const STATUS_VALIDATED = 1;
224
	/**
225
	 * Shipment on process
226
	 */
227
	const STATUS_SHIPMENTONPROCESS = 2;
228
	const STATUS_ACCEPTED = 2;				// For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
229
230
	/**
231
	 * Closed (Sent, billed or not)
232
	 */
233
	const STATUS_CLOSED = 3;
234
235
236
	/**
237
	 *	Constructor
238
	 *
239
	 *  @param		DoliDB		$db      Database handler
240
	 */
241
	function __construct($db)
242
	{
243
		$this->db = $db;
244
245
		$this->remise = 0;
246
		$this->remise_percent = 0;
247
248
		$this->products = array();
249
	}
250
251
	/**
252
	 *  Returns the reference to the following non used Order depending on the active numbering module
253
	 *  defined into COMMANDE_ADDON
254
	 *
255
	 *  @param	Societe		$soc  	Object thirdparty
256
	 *  @return string      		Order free reference
257
	 */
258
	function getNextNumRef($soc)
259
	{
260
		global $langs, $conf;
261
		$langs->load("order");
262
263
		if (! empty($conf->global->COMMANDE_ADDON))
264
		{
265
			$mybool=false;
266
267
			$file = $conf->global->COMMANDE_ADDON.".php";
268
			$classname = $conf->global->COMMANDE_ADDON;
269
270
			// Include file with class
271
			$dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']);
272
			foreach ($dirmodels as $reldir)
273
			{
274
				$dir = dol_buildpath($reldir."core/modules/commande/");
275
276
				// Load file with numbering class (if found)
277
				$mybool|=@include_once $dir.$file;
278
			}
279
280
            if ($mybool === false)
281
            {
282
                dol_print_error('',"Failed to include file ".$file);
283
                return '';
284
            }
285
286
			$obj = new $classname();
287
			$numref = $obj->getNextValue($soc,$this);
288
289
			if ($numref != "")
290
			{
291
				return $numref;
292
			}
293
			else
294
			{
295
				$this->error=$obj->error;
296
				//dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
297
				return "";
298
			}
299
		}
300
		else
301
		{
302
			print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
303
			return "";
304
		}
305
	}
306
307
308
	/**
309
	 *	Validate order
310
	 *
311
	 *	@param		User	$user     		User making status change
312
	 *	@param		int		$idwarehouse	Id of warehouse to use for stock decrease
313
	 *  @param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
314
	 *	@return  	int						<=0 if OK, 0=Nothing done, >0 if KO
315
	 */
316
	function valid($user, $idwarehouse=0, $notrigger=0)
317
	{
318
		global $conf,$langs;
319
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
320
321
		$error=0;
322
323
		// Protection
324
		if ($this->statut == self::STATUS_VALIDATED)
325
		{
326
			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
327
			return 0;
328
		}
329
330
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
331
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate))))
332
		{
333
			$this->error='NotEnoughPermissions';
334
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
335
			return -1;
336
		}
337
338
		$now=dol_now();
339
340
		$this->db->begin();
341
342
		// Definition du nom de module de numerotation de commande
343
		$soc = new Societe($this->db);
344
		$soc->fetch($this->socid);
345
346
		// Class of company linked to order
347
		$result=$soc->set_as_client();
348
349
		// Define new ref
350
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
351
		{
352
			$num = $this->getNextNumRef($soc);
353
		}
354
		else
355
		{
356
			$num = $this->ref;
357
		}
358
		$this->newref = $num;
359
360
		// Validate
361
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
362
		$sql.= " SET ref = '".$num."',";
363
		$sql.= " fk_statut = ".self::STATUS_VALIDATED.",";
364
		$sql.= " date_valid='".$this->db->idate($now)."',";
365
		$sql.= " fk_user_valid = ".$user->id;
366
		$sql.= " WHERE rowid = ".$this->id;
367
368
		dol_syslog(get_class($this)."::valid()", LOG_DEBUG);
369
		$resql=$this->db->query($sql);
370
		if (! $resql)
371
		{
372
			dol_print_error($this->db);
373
			$this->error=$this->db->lasterror();
374
			$error++;
375
		}
376
377
		if (! $error)
378
		{
379
			// If stock is incremented on validate order, we must increment it
380
			if ($result >= 0 && ! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
381
			{
382
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
383
				$langs->load("agenda");
384
385
				// Loop on each line
386
				$cpt=count($this->lines);
387
				for ($i = 0; $i < $cpt; $i++)
388
				{
389
					if ($this->lines[$i]->fk_product > 0)
390
					{
391
						$mouvP = new MouvementStock($this->db);
392
						$mouvP->origin = &$this;
393
						// We decrement stock of product (and sub-products)
394
						$result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr",$num));
395
						if ($result < 0)
396
						{
397
							$error++;
398
							$this->error=$mouvP->error;
399
						}
400
					}
401
					if ($error) break;
402
				}
403
			}
404
		}
405
406
		if (! $error && ! $notrigger)
407
		{
408
			// Call trigger
409
			$result=$this->call_trigger('ORDER_VALIDATE',$user);
410
			if ($result < 0) $error++;
411
			// End call triggers
412
		}
413
414
		if (! $error)
415
		{
416
			$this->oldref = $this->ref;
417
418
			// Rename directory if dir was a temporary ref
419
			if (preg_match('/^[\(]?PROV/i', $this->ref))
420
			{
421
				// On renomme repertoire ($this->ref = ancienne ref, $num = nouvelle ref)
422
				// in order not to lose the attachments
423
				$oldref = dol_sanitizeFileName($this->ref);
424
				$newref = dol_sanitizeFileName($num);
425
				$dirsource = $conf->commande->dir_output.'/'.$oldref;
426
				$dirdest = $conf->commande->dir_output.'/'.$newref;
427
				if (file_exists($dirsource))
428
				{
429
					dol_syslog(get_class($this)."::valid() rename dir ".$dirsource." into ".$dirdest);
430
431
					if (@rename($dirsource, $dirdest))
432
					{
433
						dol_syslog("Rename ok");
434
						// Rename docs starting with $oldref with $newref
435
						$listoffiles=dol_dir_list($conf->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
436
						foreach($listoffiles as $fileentry)
437
						{
438
							$dirsource=$fileentry['name'];
439
							$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
440
							$dirsource=$fileentry['path'].'/'.$dirsource;
441
							$dirdest=$fileentry['path'].'/'.$dirdest;
442
							@rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

442
							/** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
443
						}
444
					}
445
				}
446
			}
447
		}
448
449
		// Set new ref and current status
450
		if (! $error)
451
		{
452
			$this->ref = $num;
453
			$this->statut = self::STATUS_VALIDATED;
454
            $this->brouillon = 0;
455
		}
456
457
		if (! $error)
458
		{
459
			$this->db->commit();
460
			return 1;
461
		}
462
		else
463
		{
464
			$this->db->rollback();
465
			return -1;
466
		}
467
	}
468
469
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
470
	/**
471
	 *	Set draft status
472
	 *
473
	 *	@param	User	$user			Object user that modify
474
	 *	@param	int		$idwarehouse	Warehouse ID to use for stock change (Used only if option STOCK_CALCULATE_ON_VALIDATE_ORDER is on)
475
	 *	@return	int						<0 if KO, >0 if OK
476
	 */
477
	function set_draft($user, $idwarehouse=-1)
478
	{
479
        //phpcs:enable
480
		global $conf,$langs;
481
482
		$error=0;
483
484
		// Protection
485
		if ($this->statut <= self::STATUS_DRAFT)
486
		{
487
			return 0;
488
		}
489
490
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
491
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate))))
492
		{
493
			$this->error='Permission denied';
494
			return -1;
495
		}
496
497
		$this->db->begin();
498
499
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
500
		$sql.= " SET fk_statut = ".self::STATUS_DRAFT;
501
		$sql.= " WHERE rowid = ".$this->id;
502
503
		dol_syslog(get_class($this)."::set_draft", LOG_DEBUG);
504
		if ($this->db->query($sql))
505
		{
506
			// If stock is decremented on validate order, we must reincrement it
507
			if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
508
			{
509
				$result = 0;
510
511
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
512
				$langs->load("agenda");
513
514
				$num=count($this->lines);
515
				for ($i = 0; $i < $num; $i++)
516
				{
517
					if ($this->lines[$i]->fk_product > 0)
518
					{
519
						$mouvP = new MouvementStock($this->db);
520
						$mouvP->origin = &$this;
521
						// We increment stock of product (and sub-products)
522
						$result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr",$this->ref));
523
						if ($result < 0) { $error++; $this->error=$mouvP->error; break; }
524
					}
525
				}
526
			}
527
528
			if (!$error) {
529
				// Call trigger
530
				$result=$this->call_trigger('ORDER_UNVALIDATE',$user);
531
				if ($result < 0) $error++;
532
			}
533
534
			if (!$error) {
535
				$this->statut=self::STATUS_DRAFT;
536
				$this->db->commit();
537
				return 1;
538
			}else {
539
				$this->db->rollback();
540
				return -1;
541
			}
542
		}
543
		else
544
		{
545
			$this->error=$this->db->error();
546
			$this->db->rollback();
547
			return -1;
548
		}
549
	}
550
551
552
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
553
	/**
554
	 *	Tag the order as validated (opened)
555
	 *	Function used when order is reopend after being closed.
556
	 *
557
	 *	@param      User	$user       Object user that change status
558
	 *	@return     int         		<0 if KO, 0 if nothing is done, >0 if OK
559
	 */
560
	function set_reopen($user)
561
	{
562
        // phpcs:enable
563
		$error=0;
564
565
		if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED)
566
		{
567
			dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
568
			return 0;
569
		}
570
571
		$this->db->begin();
572
573
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
574
		$sql.= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0';
575
		$sql.= ' WHERE rowid = '.$this->id;
576
577
		dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
578
		$resql = $this->db->query($sql);
579
		if ($resql)
580
		{
581
			// Call trigger
582
			$result=$this->call_trigger('ORDER_REOPEN',$user);
583
			if ($result < 0) $error++;
584
			// End call triggers
585
		}
586
		else
587
		{
588
			$error++;
589
			$this->error=$this->db->lasterror();
590
			dol_print_error($this->db);
591
		}
592
593
		if (! $error)
594
		{
595
			$this->statut = self::STATUS_VALIDATED;
596
			$this->billed = 0;
597
598
			$this->db->commit();
599
			return 1;
600
		}
601
		else
602
		{
603
			foreach($this->errors as $errmsg)
604
			{
605
				dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
606
				$this->error.=($this->error?', '.$errmsg:$errmsg);
607
			}
608
			$this->db->rollback();
609
			return -1*$error;
610
		}
611
	}
612
613
	/**
614
	 *  Close order
615
	 *
616
	 * 	@param      User	$user       Objet user that close
617
	 *  @param		int		$notrigger	1=Does not execute triggers, 0=Execute triggers
618
	 *	@return		int					<0 if KO, >0 if OK
619
	 */
620
	function cloture($user, $notrigger=0)
621
	{
622
		global $conf;
623
624
		$error=0;
625
626
		if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
627
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate)))
628
		{
629
			$this->db->begin();
630
631
			$now=dol_now();
632
633
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
634
			$sql.= ' SET fk_statut = '.self::STATUS_CLOSED.',';
635
			$sql.= ' fk_user_cloture = '.$user->id.',';
636
			$sql.= " date_cloture = '".$this->db->idate($now)."'";
637
			$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
638
639
			if ($this->db->query($sql))
640
			{
641
				if (! $notrigger)
642
				{
643
					// Call trigger
644
					$result=$this->call_trigger('ORDER_CLOSE',$user);
645
					if ($result < 0) $error++;
646
					// End call triggers
647
				}
648
649
				if (! $error)
650
				{
651
					$this->statut=self::STATUS_CLOSED;
652
653
					$this->db->commit();
654
					return 1;
655
				}
656
				else
657
				{
658
					$this->db->rollback();
659
					return -1;
660
				}
661
			}
662
			else
663
			{
664
				$this->error=$this->db->lasterror();
665
666
				$this->db->rollback();
667
				return -1;
668
			}
669
		}
670
		return 0;
671
	}
672
673
	/**
674
	 * 	Cancel an order
675
	 * 	If stock is decremented on order validation, we must reincrement it
676
	 *
677
	 *	@param	int		$idwarehouse	Id warehouse to use for stock change.
678
	 *	@return	int						<0 if KO, >0 if OK
679
	 */
680
	function cancel($idwarehouse=-1)
681
	{
682
		global $conf,$user,$langs;
683
684
		$error=0;
685
686
		$this->db->begin();
687
688
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
689
		$sql.= " SET fk_statut = ".self::STATUS_CANCELED;
690
		$sql.= " WHERE rowid = ".$this->id;
691
		$sql.= " AND fk_statut = ".self::STATUS_VALIDATED;
692
693
		dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
694
		if ($this->db->query($sql))
695
		{
696
			// If stock is decremented on validate order, we must reincrement it
697
			if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
698
			{
699
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
700
				$langs->load("agenda");
701
702
				$num=count($this->lines);
703
				for ($i = 0; $i < $num; $i++)
704
				{
705
					if ($this->lines[$i]->fk_product > 0)
706
					{
707
						$mouvP = new MouvementStock($this->db);
708
						// We increment stock of product (and sub-products)
709
						$result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderCanceledInDolibarr",$this->ref));  // price is 0, we don't want WAP to be changed
710
						if ($result < 0)
711
						{
712
							$error++;
713
							$this->error=$mouvP->error;
714
							break;
715
						}
716
					}
717
				}
718
			}
719
720
			if (! $error)
721
			{
722
				// Call trigger
723
				$result=$this->call_trigger('ORDER_CANCEL',$user);
724
				if ($result < 0) $error++;
725
				// End call triggers
726
			}
727
728
			if (! $error)
729
			{
730
				$this->statut=self::STATUS_CANCELED;
731
				$this->db->commit();
732
				return 1;
733
			}
734
			else
735
			{
736
				foreach($this->errors as $errmsg)
737
				{
738
					dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
739
					$this->error.=($this->error?', '.$errmsg:$errmsg);
740
				}
741
				$this->db->rollback();
742
				return -1*$error;
743
			}
744
		}
745
		else
746
		{
747
			$this->error=$this->db->error();
748
			$this->db->rollback();
749
			return -1;
750
		}
751
	}
752
753
	/**
754
	 *	Create order
755
	 *	Note that this->ref can be set or empty. If empty, we will use "(PROV)"
756
	 *
757
	 *	@param		User	$user 		Objet user that make creation
758
	 *	@param		int	    $notrigger	Disable all triggers
759
	 *	@return 	int			        <0 if KO, >0 if OK
760
	 */
761
	function create($user, $notrigger=0)
762
	{
763
		global $conf,$langs;
764
		$error=0;
765
766
		// Clean parameters
767
		$this->brouillon = 1;		// set command as draft
768
769
		// $date_commande is deprecated
770
		$date = ($this->date_commande ? $this->date_commande : $this->date);
771
772
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
773
		if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
774
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
775
		if (empty($this->fk_multicurrency))
776
		{
777
			$this->multicurrency_code = $conf->currency;
778
			$this->fk_multicurrency = 0;
779
			$this->multicurrency_tx = 1;
780
		}
781
782
		dol_syslog(get_class($this)."::create user=".$user->id);
783
784
		// Check parameters
785
		if (! empty($this->ref))	// We check that ref is not already used
786
		{
787
			$result=self::isExistingObject($this->element, 0, $this->ref);	// Check ref is not yet used
788
			if ($result > 0)
789
			{
790
				$this->error='ErrorRefAlreadyExists';
791
				dol_syslog(get_class($this)."::create ".$this->error,LOG_WARNING);
792
				$this->db->rollback();
793
				return -1;
794
			}
795
		}
796
797
		$soc = new Societe($this->db);
798
		$result=$soc->fetch($this->socid);
799
		if ($result < 0)
800
		{
801
			$this->error="Failed to fetch company";
802
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
803
			return -2;
804
		}
805
		if (! empty($conf->global->COMMANDE_REQUIRE_SOURCE) && $this->source < 0)
806
		{
807
			$this->error=$langs->trans("ErrorFieldRequired",$langs->trans("Source"));
808
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
809
			return -1;
810
		}
811
812
		$now=dol_now();
813
814
		$this->db->begin();
815
816
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
817
		$sql.= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client, ref_int";
818
		$sql.= ", model_pdf, fk_cond_reglement, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
819
		$sql.= ", fk_shipping_method";
820
		$sql.= ", fk_warehouse";
821
		$sql.= ", remise_absolue, remise_percent";
822
		$sql.= ", fk_incoterms, location_incoterms";
823
		$sql.= ", entity";
824
		$sql.= ", fk_multicurrency";
825
		$sql.= ", multicurrency_code";
826
		$sql.= ", multicurrency_tx";
827
		$sql.= ")";
828
		$sql.= " VALUES ('(PROV)', ".$this->socid.", '".$this->db->idate($now)."', ".$user->id;
829
		$sql.= ", ".($this->fk_project>0?$this->fk_project:"null");
830
		$sql.= ", '".$this->db->idate($date)."'";
831
		$sql.= ", ".($this->source>=0 && $this->source != '' ?$this->db->escape($this->source):'null');
832
		$sql.= ", '".$this->db->escape($this->note_private)."'";
833
		$sql.= ", '".$this->db->escape($this->note_public)."'";
834
		$sql.= ", ".($this->ref_ext?"'".$this->db->escape($this->ref_ext)."'":"null");
835
		$sql.= ", ".($this->ref_client?"'".$this->db->escape($this->ref_client)."'":"null");
836
		$sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
837
		$sql.= ", '".$this->db->escape($this->modelpdf)."'";
838
		$sql.= ", ".($this->cond_reglement_id>0?$this->cond_reglement_id:"null");
839
		$sql.= ", ".($this->mode_reglement_id>0?$this->mode_reglement_id:"null");
840
		$sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
841
		$sql.= ", ".($this->availability_id>0?$this->availability_id:"null");
842
		$sql.= ", ".($this->demand_reason_id>0?$this->demand_reason_id:"null");
843
		$sql.= ", ".($this->date_livraison?"'".$this->db->idate($this->date_livraison)."'":"null");
844
		$sql.= ", ".($this->fk_delivery_address>0?$this->fk_delivery_address:'NULL');
845
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
846
		$sql.= ", ".($this->warehouse_id>0?$this->warehouse_id:'NULL');
847
		$sql.= ", ".($this->remise_absolue>0?$this->db->escape($this->remise_absolue):'NULL');
848
		$sql.= ", ".($this->remise_percent>0?$this->db->escape($this->remise_percent):0);
849
		$sql.= ", ".(int) $this->fk_incoterms;
850
		$sql.= ", '".$this->db->escape($this->location_incoterms)."'";
851
		$sql.= ", ".$conf->entity;
852
		$sql.= ", ".(int) $this->fk_multicurrency;
853
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
854
		$sql.= ", ".(double) $this->multicurrency_tx;
855
		$sql.= ")";
856
857
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
858
		$resql=$this->db->query($sql);
859
		if ($resql)
860
		{
861
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
862
863
			if ($this->id)
864
			{
865
				$fk_parent_line=0;
866
				$num=count($this->lines);
867
868
				/*
869
				 *  Insert products details into db
870
				 */
871
				for ($i=0;$i<$num;$i++)
872
				{
873
					$line = $this->lines[$i];
874
875
					// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
876
					//if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
877
					if (! is_object($line)) $line = (object) $line;
878
879
					// Reset fk_parent_line for no child products and special product
880
					if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
881
						$fk_parent_line = 0;
882
					}
883
884
					// Complete vat rate with code
885
					$vatrate = $line->tva_tx;
886
					if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
887
888
					$result = $this->addline(
889
						$line->desc,
890
						$line->subprice,
891
						$line->qty,
892
						$vatrate,
893
						$line->localtax1_tx,
894
						$line->localtax2_tx,
895
						$line->fk_product,
896
						$line->remise_percent,
897
						$line->info_bits,
898
						$line->fk_remise_except,
899
						'HT',
900
						0,
901
						$line->date_start,
902
						$line->date_end,
903
						$line->product_type,
904
						$line->rang,
905
						$line->special_code,
906
						$fk_parent_line,
907
						$line->fk_fournprice,
908
						$line->pa_ht,
909
						$line->label,
910
						$line->array_options,
911
						$line->fk_unit,
912
						$this->element,
913
						$line->id
914
					);
915
					if ($result < 0)
916
					{
917
						if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER)
918
						{
919
							$this->error=$this->db->lasterror();
920
							dol_print_error($this->db);
921
						}
922
						$this->db->rollback();
923
						return -1;
924
					}
925
					// Defined the new fk_parent_line
926
					if ($result > 0 && $line->product_type == 9) {
927
						$fk_parent_line = $result;
928
					}
929
				}
930
931
				// update ref
932
				$initialref='(PROV'.$this->id.')';
933
				if (! empty($this->ref)) $initialref=$this->ref;
934
935
				$sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".$this->id;
936
				if ($this->db->query($sql))
937
				{
938
					if ($this->id)
939
					{
940
						$this->ref = $initialref;
941
942
						if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
943
						{
944
							$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
945
						}
946
947
						// Add object linked
948
						if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
949
						{
950
							foreach($this->linked_objects as $origin => $tmp_origin_id)
951
							{
952
								if (is_array($tmp_origin_id))       // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
953
								{
954
									foreach($tmp_origin_id as $origin_id)
955
									{
956
										$ret = $this->add_object_linked($origin, $origin_id);
957
										if (! $ret)
958
										{
959
											$this->error=$this->db->lasterror();
960
											$error++;
961
										}
962
									}
963
								}
964
								else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
965
								{
966
									$origin_id = $tmp_origin_id;
967
									$ret = $this->add_object_linked($origin, $origin_id);
968
									if (! $ret)
969
									{
970
										$this->error=$this->db->lasterror();
971
										$error++;
972
									}
973
								}
974
							}
975
						}
976
977
						if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id))   // Get contact from origin object
978
						{
979
							$originforcontact = $this->origin;
980
							$originidforcontact = $this->origin_id;
981
							if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
982
							{
983
								require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
984
								$exp = new Expedition($this->db);
985
								$exp->fetch($this->origin_id);
986
								$exp->fetchObjectLinked();
987
								if (count($exp->linkedObjectsIds['commande']) > 0)
988
								{
989
									foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
990
									{
991
										$originforcontact = 'commande';
992
										if (is_object($value)) $originidforcontact = $value->id;
993
										else $originidforcontact = $value;
994
										break; // We take first one
995
									}
996
								}
997
							}
998
999
							$sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
1000
							$sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
1001
1002
							$resqlcontact = $this->db->query($sqlcontact);
1003
							if ($resqlcontact)
1004
							{
1005
								while($objcontact = $this->db->fetch_object($resqlcontact))
1006
								{
1007
									//print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1008
									$this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source);    // May failed because of duplicate key or because code of contact type does not exists for new object
1009
								}
1010
							}
1011
							else dol_print_error($resqlcontact);
1012
						}
1013
					}
1014
1015
					if (! $error)
1016
					{
1017
						$result=$this->insertExtraFields();
1018
						if ($result < 0) $error++;
1019
					}
1020
1021
					if (! $error && ! $notrigger)
1022
					{
1023
						// Call trigger
1024
						$result=$this->call_trigger('ORDER_CREATE',$user);
1025
						if ($result < 0) $error++;
1026
						// End call triggers
1027
					}
1028
1029
					if (! $error)
1030
					{
1031
						$this->db->commit();
1032
						return $this->id;
1033
					}
1034
					else
1035
					{
1036
						$this->db->rollback();
1037
						return -1*$error;
1038
					}
1039
				}
1040
				else
1041
				{
1042
					$this->error=$this->db->lasterror();
1043
					$this->db->rollback();
1044
					return -1;
1045
				}
1046
			}
1047
		}
1048
		else
1049
		{
1050
			dol_print_error($this->db);
1051
			$this->db->rollback();
1052
			return -1;
1053
		}
1054
	}
1055
1056
1057
	/**
1058
	 *	Load an object from its id and create a new one in database
1059
	 *
1060
	 *	@param		int			$socid			Id of thirdparty
1061
	 *	@return		int							New id of clone
1062
	 */
1063
	function createFromClone($socid=0)
1064
	{
1065
		global $user,$hookmanager;
1066
1067
		$error=0;
1068
1069
		$this->context['createfromclone'] = 'createfromclone';
1070
1071
		$this->db->begin();
1072
1073
		// get lines so they will be clone
1074
		foreach($this->lines as $line)
1075
			$line->fetch_optionals();
1076
1077
			// Load source object
1078
			$objFrom = clone $this;
1079
1080
			// Change socid if needed
1081
			if (! empty($socid) && $socid != $this->socid)
1082
			{
1083
				$objsoc = new Societe($this->db);
1084
1085
				if ($objsoc->fetch($socid)>0)
1086
				{
1087
					$this->socid 				= $objsoc->id;
1088
					$this->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1089
					$this->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1090
					$this->fk_project			= 0;
1091
					$this->fk_delivery_address	= 0;
1092
				}
1093
1094
				// TODO Change product price if multi-prices
1095
			}
1096
1097
			$this->id=0;
1098
			$this->ref = '';
1099
			$this->statut=self::STATUS_DRAFT;
1100
1101
			// Clear fields
1102
			$this->user_author_id     = $user->id;
1103
			$this->user_valid         = '';
1104
			$this->date				  = dol_now();
1105
			$this->date_commande	  = dol_now();
1106
			$this->date_creation      = '';
1107
			$this->date_validation    = '';
1108
			$this->ref_client         = '';
1109
1110
			// Create clone
1111
			$result=$this->create($user);
1112
			if ($result < 0) $error++;
1113
1114
			if (! $error)
1115
			{
1116
				// Hook of thirdparty module
1117
				if (is_object($hookmanager))
1118
				{
1119
					$parameters=array('objFrom'=>$objFrom);
1120
					$action='';
1121
					$reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
1122
					if ($reshook < 0) $error++;
1123
				}
1124
			}
1125
1126
			unset($this->context['createfromclone']);
1127
1128
			// End
1129
			if (! $error)
1130
			{
1131
				$this->db->commit();
1132
				return $this->id;
1133
			}
1134
			else
1135
			{
1136
				$this->db->rollback();
1137
				return -1;
1138
			}
1139
	}
1140
1141
1142
	/**
1143
	 *  Load an object from a proposal and create a new order into database
1144
	 *
1145
	 *  @param      Object			$object 	        Object source
1146
	 *  @param		User			$user				User making creation
1147
	 *  @return     int             					<0 if KO, 0 if nothing done, 1 if OK
1148
	 */
1149
	function createFromProposal($object, User $user)
1150
	{
1151
		global $conf, $hookmanager;
1152
1153
		dol_include_once('/core/class/extrafields.class.php');
1154
1155
		$error=0;
1156
1157
1158
		$this->date_commande = dol_now();
1159
		$this->source = 0;
1160
1161
		$num=count($object->lines);
1162
		for ($i = 0; $i < $num; $i++)
1163
		{
1164
			$line = new OrderLine($this->db);
1165
1166
			$line->libelle           = $object->lines[$i]->libelle;
0 ignored issues
show
Deprecated Code introduced by
The property CommonOrderLine::$libelle has been deprecated: Use product_label ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1166
			/** @scrutinizer ignore-deprecated */ $line->libelle           = $object->lines[$i]->libelle;

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

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

Loading history...
1167
			$line->label             = $object->lines[$i]->label;
1168
			$line->desc              = $object->lines[$i]->desc;
1169
			$line->price             = $object->lines[$i]->price;
1170
			$line->subprice          = $object->lines[$i]->subprice;
1171
			$line->vat_src_code      = $object->lines[$i]->vat_src_code;
1172
			$line->tva_tx            = $object->lines[$i]->tva_tx;
1173
			$line->localtax1_tx      = $object->lines[$i]->localtax1_tx;
1174
			$line->localtax2_tx      = $object->lines[$i]->localtax2_tx;
1175
			$line->qty               = $object->lines[$i]->qty;
1176
			$line->fk_remise_except  = $object->lines[$i]->fk_remise_except;
1177
			$line->remise_percent    = $object->lines[$i]->remise_percent;
1178
			$line->fk_product        = $object->lines[$i]->fk_product;
1179
			$line->info_bits         = $object->lines[$i]->info_bits;
1180
			$line->product_type      = $object->lines[$i]->product_type;
1181
			$line->rang              = $object->lines[$i]->rang;
1182
			$line->special_code      = $object->lines[$i]->special_code;
1183
			$line->fk_parent_line    = $object->lines[$i]->fk_parent_line;
1184
			$line->fk_unit			 = $object->lines[$i]->fk_unit;
1185
1186
			$line->date_start      	= $object->lines[$i]->date_start;
1187
			$line->date_end    		= $object->lines[$i]->date_end;
1188
1189
			$line->fk_fournprice	= $object->lines[$i]->fk_fournprice;
1190
			$marginInfos			= getMarginInfos($object->lines[$i]->subprice, $object->lines[$i]->remise_percent, $object->lines[$i]->tva_tx, $object->lines[$i]->localtax1_tx, $object->lines[$i]->localtax2_tx, $object->lines[$i]->fk_fournprice, $object->lines[$i]->pa_ht);
1191
			$line->pa_ht			= $marginInfos[0];
1192
			$line->marge_tx			= $marginInfos[1];
1193
			$line->marque_tx		= $marginInfos[2];
1194
1195
			// get extrafields from original line
1196
			$object->lines[$i]->fetch_optionals();
1197
			foreach($object->lines[$i]->array_options as $options_key => $value)
1198
				$line->array_options[$options_key] = $value;
1199
1200
				$this->lines[$i] = $line;
1201
		}
1202
1203
		$this->socid                = $object->socid;
1204
		$this->fk_project           = $object->fk_project;
1205
		$this->cond_reglement_id    = $object->cond_reglement_id;
1206
		$this->mode_reglement_id    = $object->mode_reglement_id;
1207
		$this->fk_account           = $object->fk_account;
1208
		$this->availability_id      = $object->availability_id;
1209
		$this->demand_reason_id     = $object->demand_reason_id;
1210
		$this->date_livraison       = $object->date_livraison;
1211
		$this->shipping_method_id   = $object->shipping_method_id;
1212
		$this->warehouse_id         = $object->warehouse_id;
1213
		$this->fk_delivery_address  = $object->fk_delivery_address;
1214
		$this->contact_id           = $object->contactid;
1215
		$this->ref_client           = $object->ref_client;
1216
		$this->note_private         = $object->note_private;
1217
		$this->note_public          = $object->note_public;
1218
1219
		$this->origin				= $object->element;
1220
		$this->origin_id			= $object->id;
1221
1222
		// get extrafields from original line
1223
		$object->fetch_optionals($object->id);
1224
1225
		$e = new ExtraFields($this->db);
1226
		$element_extrafields = $e->fetch_name_optionals_label($this->element);
1227
1228
		foreach($object->array_options as $options_key => $value) {
1229
			if(array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)){
1230
				$this->array_options[$options_key] = $value;
1231
			}
1232
		}
1233
		// Possibility to add external linked objects with hooks
1234
		$this->linked_objects[$this->origin] = $this->origin_id;
1235
		if (is_array($object->other_linked_objects) && ! empty($object->other_linked_objects))
1236
		{
1237
			$this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1238
		}
1239
1240
		$ret = $this->create($user);
1241
1242
		if ($ret > 0)
1243
		{
1244
			// Actions hooked (by external module)
1245
			$hookmanager->initHooks(array('orderdao'));
1246
1247
			$parameters=array('objFrom'=>$object);
1248
			$action='';
1249
			$reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
1250
			if ($reshook < 0) $error++;
1251
1252
			if (! $error)
1253
			{
1254
				// Ne pas passer par la commande provisoire
1255
				if ($conf->global->COMMANDE_VALID_AFTER_CLOSE_PROPAL == 1)
1256
				{
1257
					$this->fetch($ret);
1258
					$this->valid($user);
1259
				}
1260
				return $ret;
1261
			}
1262
			else return -1;
1263
		}
1264
		else return -1;
1265
	}
1266
1267
1268
	/**
1269
	 *	Add an order line into database (linked to product/service or not)
1270
	 *
1271
	 *	@param      string			$desc            	Description of line
1272
	 *	@param      float			$pu_ht    	        Unit price (without tax)
1273
	 *	@param      float			$qty             	Quantite
1274
	 * 	@param    	float			$txtva           	Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
1275
	 * 	@param		float			$txlocaltax1		Local tax 1 rate (deprecated, use instead txtva with code inside)
1276
	 * 	@param		float			$txlocaltax2		Local tax 2 rate (deprecated, use instead txtva with code inside)
1277
	 *	@param      int				$fk_product      	Id of product
1278
	 *	@param      float			$remise_percent  	Pourcentage de remise de la ligne
1279
	 *	@param      int				$info_bits			Bits de type de lignes
1280
	 *	@param      int				$fk_remise_except	Id remise
1281
	 *	@param      string			$price_base_type	HT or TTC
1282
	 *	@param      float			$pu_ttc    		    Prix unitaire TTC
1283
	 *	@param      int				$date_start       	Start date of the line - Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1284
	 *	@param      int				$date_end         	End date of the line - Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1285
	 *	@param      int				$type				Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
1286
	 *	@param      int				$rang             	Position of line
1287
	 *	@param		int				$special_code		Special code (also used by externals modules!)
1288
	 *	@param		int				$fk_parent_line		Parent line
1289
	 *  @param		int				$fk_fournprice		Id supplier price
1290
	 *  @param		int				$pa_ht				Buying price (without tax)
1291
	 *  @param		string			$label				Label
1292
	 *  @param		array			$array_options		extrafields array. Example array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
1293
	 * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
1294
	 * 	@param		string		    $origin				'order', ...
1295
	 *  @param		int			    $origin_id			Id of origin object
1296
	 * 	@param		double			$pu_ht_devise		Unit price in currency
1297
	 *	@return     int             					>0 if OK, <0 if KO
1298
	 *
1299
	 *	@see        add_product
1300
	 *
1301
	 *	Les parametres sont deja cense etre juste et avec valeurs finales a l'appel
1302
	 *	de cette methode. Aussi, pour le taux tva, il doit deja avoir ete defini
1303
	 *	par l'appelant par la methode get_default_tva(societe_vendeuse,societe_acheteuse,produit)
1304
	 *	et le desc doit deja avoir la bonne valeur (a l'appelant de gerer le multilangue)
1305
	 */
1306
	function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $info_bits=0, $fk_remise_except=0, $price_base_type='HT', $pu_ttc=0, $date_start='', $date_end='', $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='',$array_options=0, $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise = 0)
1307
	{
1308
		global $mysoc, $conf, $langs, $user;
1309
1310
		$logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1311
		$logtext.= ", info_bits=$info_bits, fk_remise_except=$fk_remise_except, price_base_type=$price_base_type, pu_ttc=$pu_ttc, date_start=$date_start";
1312
		$logtext.= ", date_end=$date_end, type=$type special_code=$special_code, fk_unit=$fk_unit, origin=$origin, origin_id=$origin_id, pu_ht_devise=$pu_ht_devise";
1313
		dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1314
1315
		if ($this->statut == self::STATUS_DRAFT)
1316
		{
1317
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1318
1319
			// Clean parameters
1320
			if (empty($remise_percent)) $remise_percent=0;
1321
			if (empty($qty)) $qty=0;
1322
			if (empty($info_bits)) $info_bits=0;
1323
			if (empty($rang)) $rang=0;
1324
			if (empty($txtva)) $txtva=0;
1325
			if (empty($txlocaltax1)) $txlocaltax1=0;
1326
			if (empty($txlocaltax2)) $txlocaltax2=0;
1327
			if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
1328
			if (empty($this->fk_multicurrency)) $this->fk_multicurrency=0;
1329
1330
			$remise_percent=price2num($remise_percent);
1331
			$qty=price2num($qty);
1332
			$pu_ht=price2num($pu_ht);
1333
			$pu_ht_devise=price2num($pu_ht_devise);
1334
			$pu_ttc=price2num($pu_ttc);
1335
			$pa_ht=price2num($pa_ht);
1336
			if (!preg_match('/\((.*)\)/', $txtva)) {
1337
				$txtva = price2num($txtva);               // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1338
			}
1339
			$txlocaltax1 = price2num($txlocaltax1);
1340
			$txlocaltax2 = price2num($txlocaltax2);
1341
			if ($price_base_type=='HT')
1342
			{
1343
				$pu=$pu_ht;
1344
			}
1345
			else
1346
			{
1347
				$pu=$pu_ttc;
1348
			}
1349
			$label=trim($label);
1350
			$desc=trim($desc);
1351
1352
			// Check parameters
1353
			if ($type < 0) return -1;
1354
1355
            $this->db->begin();
1356
1357
			$product_type=$type;
1358
			if (!empty($fk_product))
1359
			{
1360
				$product=new Product($this->db);
1361
				$result=$product->fetch($fk_product);
1362
				$product_type=$product->type;
1363
1364
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
1365
				{
1366
					$langs->load("errors");
1367
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1368
					dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1369
					$this->db->rollback();
1370
					return self::STOCK_NOT_ENOUGH_FOR_ORDER;
1371
				}
1372
			}
1373
			// Calcul du total TTC et de la TVA pour la ligne a partir de
1374
			// qty, pu, remise_percent et txtva
1375
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1376
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1377
1378
			$localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
1379
1380
			// Clean vat code
1381
			$vat_src_code='';
1382
			if (preg_match('/\((.*)\)/', $txtva, $reg))
1383
			{
1384
				$vat_src_code = $reg[1];
1385
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
1386
			}
1387
1388
			$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1389
1390
			/*var_dump($txlocaltax1);
1391
			 var_dump($txlocaltax2);
1392
			 var_dump($localtaxes_type);
1393
			 var_dump($tabprice);
1394
			 var_dump($tabprice[9]);
1395
			 var_dump($tabprice[10]);
1396
			 exit;*/
1397
1398
			$total_ht  = $tabprice[0];
1399
			$total_tva = $tabprice[1];
1400
			$total_ttc = $tabprice[2];
1401
			$total_localtax1 = $tabprice[9];
1402
			$total_localtax2 = $tabprice[10];
1403
			$pu_ht = $tabprice[3];
1404
1405
			// MultiCurrency
1406
			$multicurrency_total_ht  = $tabprice[16];
1407
			$multicurrency_total_tva = $tabprice[17];
1408
			$multicurrency_total_ttc = $tabprice[18];
1409
			$pu_ht_devise = $tabprice[19];
1410
1411
			// Rang to use
1412
			$rangtouse = $rang;
1413
			if ($rangtouse == -1)
1414
			{
1415
				$rangmax = $this->line_max($fk_parent_line);
1416
				$rangtouse = $rangmax + 1;
1417
			}
1418
1419
			// TODO A virer
1420
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1421
			$price = $pu;
1422
			$remise = 0;
1423
			if ($remise_percent > 0)
1424
			{
1425
				$remise = round(($pu * $remise_percent / 100), 2);
1426
				$price = $pu - $remise;
1427
			}
1428
1429
			// Insert line
1430
			$this->line=new OrderLine($this->db);
1431
1432
			$this->line->context = $this->context;
1433
1434
			$this->line->fk_commande=$this->id;
1435
			$this->line->label=$label;
1436
			$this->line->desc=$desc;
1437
			$this->line->qty=$qty;
0 ignored issues
show
Documentation Bug introduced by
The property $qty was declared of type double, but $qty 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...
1438
1439
			$this->line->vat_src_code=$vat_src_code;
1440
			$this->line->tva_tx=$txtva;
0 ignored issues
show
Documentation Bug introduced by
It seems like $txtva can also be of type string. However, the property $tva_tx is declared as type double. 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...
1441
			$this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
1442
			$this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
1443
			$this->line->localtax1_type=$localtaxes_type[0];
1444
			$this->line->localtax2_type=$localtaxes_type[2];
1445
			$this->line->fk_product=$fk_product;
1446
			$this->line->product_type=$product_type;
1447
			$this->line->fk_remise_except=$fk_remise_except;
1448
			$this->line->remise_percent=$remise_percent;
0 ignored issues
show
Documentation Bug introduced by
The property $remise_percent was declared of type double, but $remise_percent 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...
1449
			$this->line->subprice=$pu_ht;
1450
			$this->line->rang=$rangtouse;
1451
			$this->line->info_bits=$info_bits;
1452
			$this->line->total_ht=$total_ht;
1453
			$this->line->total_tva=$total_tva;
1454
			$this->line->total_localtax1=$total_localtax1;
1455
			$this->line->total_localtax2=$total_localtax2;
1456
			$this->line->total_ttc=$total_ttc;
1457
			$this->line->special_code=$special_code;
1458
			$this->line->origin=$origin;
1459
			$this->line->origin_id=$origin_id;
1460
			$this->line->fk_parent_line=$fk_parent_line;
1461
			$this->line->fk_unit=$fk_unit;
1462
1463
			$this->line->date_start=$date_start;
1464
			$this->line->date_end=$date_end;
1465
1466
			$this->line->fk_fournprice = $fk_fournprice;
1467
			$this->line->pa_ht = $pa_ht;
0 ignored issues
show
Documentation Bug introduced by
The property $pa_ht was declared of type double, but $pa_ht 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...
1468
1469
			// Multicurrency
1470
			$this->line->fk_multicurrency			= $this->fk_multicurrency;
1471
			$this->line->multicurrency_code			= $this->multicurrency_code;
1472
			$this->line->multicurrency_subprice		= $pu_ht_devise;
1473
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
1474
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
1475
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
1476
1477
			// TODO Ne plus utiliser
1478
			$this->line->price=$price;
1479
			$this->line->remise=$remise;
1480
1481
			if (is_array($array_options) && count($array_options)>0) {
1482
				$this->line->array_options=$array_options;
1483
			}
1484
1485
			$result=$this->line->insert($user);
1486
			if ($result > 0)
1487
			{
1488
				// Reorder if child line
1489
				if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
1490
1491
				// Mise a jour informations denormalisees au niveau de la commande meme
1492
				$result=$this->update_price(1,'auto',0,$mysoc);	// This method is designed to add line from user input so total calculation must be done using 'auto' mode.
1493
				if ($result > 0)
1494
				{
1495
					$this->db->commit();
1496
					return $this->line->rowid;
1497
				}
1498
				else
1499
				{
1500
					$this->db->rollback();
1501
					return -1;
1502
				}
1503
			}
1504
			else
1505
			{
1506
				$this->error=$this->line->error;
1507
				dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1508
				$this->db->rollback();
1509
				return -2;
1510
			}
1511
		}
1512
		else
1513
		{
1514
			dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1515
			return -3;
1516
		}
1517
	}
1518
1519
1520
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1521
	/**
1522
	 *	Add line into array
1523
	 *	$this->client must be loaded
1524
	 *
1525
	 *	@param		int				$idproduct			Product Id
1526
	 *	@param		float			$qty				Quantity
1527
	 *	@param		float			$remise_percent		Product discount relative
1528
	 * 	@param    	int		$date_start         Start date of the line - Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1529
	 * 	@param    	int		$date_end           End date of the line - Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1530
	 * 	@return    	void
1531
	 *
1532
	 *	TODO	Remplacer les appels a cette fonction par generation objet Ligne
1533
	 *			insere dans tableau $this->products
1534
	 */
1535
	function add_product($idproduct, $qty, $remise_percent=0.0, $date_start='', $date_end='')
1536
	{
1537
        // phpcs:enable
1538
		global $conf, $mysoc;
1539
1540
		if (! $qty) $qty = 1;
1541
1542
		if ($idproduct > 0)
1543
		{
1544
			$prod=new Product($this->db);
1545
			$prod->fetch($idproduct);
1546
1547
			$tva_tx = get_default_tva($mysoc,$this->thirdparty,$prod->id);
1548
			$tva_npr = get_default_npr($mysoc,$this->thirdparty,$prod->id);
1549
			if (empty($tva_tx)) $tva_npr=0;
1550
			$vat_src_code = '';     // May be defined into tva_tx
1551
1552
			$localtax1_tx=get_localtax($tva_tx,1,$this->thirdparty,$mysoc,$tva_npr);
1553
			$localtax2_tx=get_localtax($tva_tx,2,$this->thirdparty,$mysoc,$tva_npr);
1554
1555
			// multiprix
1556
			if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1557
				$price = $prod->multiprices[$this->thirdparty->price_level];
1558
			} else {
1559
				$price = $prod->price;
1560
			}
1561
1562
			$line=new OrderLine($this->db);
1563
1564
			$line->context = $this->context;
1565
1566
			$line->fk_product=$idproduct;
1567
			$line->desc=$prod->description;
1568
			$line->qty=$qty;
1569
			$line->subprice=$price;
1570
			$line->remise_percent=$remise_percent;
1571
			$line->vat_src_code=$vat_src_code;
1572
			$line->tva_tx=$tva_tx;
0 ignored issues
show
Documentation Bug introduced by
It seems like $tva_tx can also be of type string. However, the property $tva_tx is declared as type double. 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...
1573
			$line->localtax1_tx=$localtax1_tx;
1574
			$line->localtax2_tx=$localtax2_tx;
1575
			$line->ref=$prod->ref;
0 ignored issues
show
Deprecated Code introduced by
The property CommonOrderLine::$ref has been deprecated: Use product_ref ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1575
			/** @scrutinizer ignore-deprecated */ $line->ref=$prod->ref;

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

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

Loading history...
1576
			$line->libelle=$prod->label;
0 ignored issues
show
Deprecated Code introduced by
The property CommonOrderLine::$libelle has been deprecated: Use product_label ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1576
			/** @scrutinizer ignore-deprecated */ $line->libelle=$prod->label;

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

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

Loading history...
1577
			$line->product_desc=$prod->description;
1578
			$line->fk_unit=$prod->fk_unit;
1579
1580
			// Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1581
			// Save the start and end date of the line in the object
1582
			if ($date_start) { $line->date_start = $date_start; }
1583
			if ($date_end)   { $line->date_end = $date_end; }
1584
1585
			$this->lines[] = $line;
1586
1587
			/** POUR AJOUTER AUTOMATIQUEMENT LES SOUSPRODUITS a LA COMMANDE
1588
			 if (! empty($conf->global->PRODUIT_SOUSPRODUITS))
1589
			 {
1590
			 $prod = new Product($this->db);
1591
			 $prod->fetch($idproduct);
1592
			 $prod -> get_sousproduits_arbo();
1593
			 $prods_arbo = $prod->get_arbo_each_prod();
1594
			 if(count($prods_arbo) > 0)
1595
			 {
1596
			 foreach($prods_arbo as $key => $value)
1597
			 {
1598
			 // print "id : ".$value[1].' :qty: '.$value[0].'<br>';
1599
			 if(! in_array($value[1],$this->products))
1600
			 $this->add_product($value[1], $value[0]);
1601
1602
			 }
1603
			 }
1604
1605
			 }
1606
			 **/
1607
		}
1608
	}
1609
1610
1611
	/**
1612
	 *	Get object and lines from database
1613
	 *
1614
	 *	@param      int			$id       		Id of object to load
1615
	 * 	@param		string		$ref			Ref of object
1616
	 * 	@param		string		$ref_ext		External reference of object
1617
	 * 	@param		string		$ref_int		Internal reference of other object
1618
	 *	@return     int         				>0 if OK, <0 if KO, 0 if not found
1619
	 */
1620
	function fetch($id, $ref='', $ref_ext='', $ref_int='')
1621
	{
1622
1623
		// Check parameters
1624
		if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
1625
1626
		$sql = 'SELECT c.rowid, c.entity, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_statut';
1627
		$sql.= ', c.amount_ht, c.total_ht, c.total_ttc, c.tva as total_tva, c.localtax1 as total_localtax1, c.localtax2 as total_localtax2, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_availability, c.fk_input_reason';
1628
		$sql.= ', c.fk_account';
1629
		$sql.= ', c.date_commande, c.date_valid, c.tms';
1630
		$sql.= ', c.date_livraison';
1631
		$sql.= ', c.fk_shipping_method';
1632
		$sql.= ', c.fk_warehouse';
1633
		$sql.= ', c.fk_projet, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1634
		$sql.= ', c.note_private, c.note_public, c.ref_client, c.ref_ext, c.ref_int, c.model_pdf, c.last_main_doc, c.fk_delivery_address, c.extraparams';
1635
		$sql.= ', c.fk_incoterms, c.location_incoterms';
1636
		$sql.= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1637
		$sql.= ", i.libelle as libelle_incoterms";
1638
		$sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1639
		$sql.= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1640
		$sql.= ', ca.code as availability_code, ca.label as availability_label';
1641
		$sql.= ', dr.code as demand_reason_code';
1642
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1643
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1644
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1645
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1646
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = ca.rowid';
1647
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1648
1649
		if ($id) $sql.= " WHERE c.rowid=".$id;
1650
		else $sql.= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1651
1652
		if ($ref)     $sql.= " AND c.ref='".$this->db->escape($ref)."'";
1653
		if ($ref_ext) $sql.= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1654
		if ($ref_int) $sql.= " AND c.ref_int='".$this->db->escape($ref_int)."'";
1655
1656
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1657
		$result = $this->db->query($sql);
1658
		if ($result)
1659
		{
1660
			$obj = $this->db->fetch_object($result);
1661
			if ($obj)
1662
			{
1663
				$this->id					= $obj->rowid;
1664
				$this->entity				= $obj->entity;
1665
1666
				$this->ref					= $obj->ref;
1667
				$this->ref_client			= $obj->ref_client;
1668
				$this->ref_customer			= $obj->ref_client;
1669
				$this->ref_ext				= $obj->ref_ext;
1670
				$this->ref_int				= $obj->ref_int;
1671
				$this->socid				= $obj->fk_soc;
1672
				$this->statut				= $obj->fk_statut;
1673
				$this->user_author_id		= $obj->fk_user_author;
1674
				$this->user_valid           = $obj->fk_user_valid;
1675
				$this->total_ht				= $obj->total_ht;
1676
				$this->total_tva			= $obj->total_tva;
1677
				$this->total_localtax1		= $obj->total_localtax1;
1678
				$this->total_localtax2		= $obj->total_localtax2;
1679
				$this->total_ttc			= $obj->total_ttc;
1680
				$this->date					= $this->db->jdate($obj->date_commande);
1681
				$this->date_commande		= $this->db->jdate($obj->date_commande);
1682
				$this->date_creation		= $this->db->jdate($obj->date_creation);
1683
				$this->date_validation		= $this->db->jdate($obj->date_valid);
1684
				$this->date_modification		= $this->db->jdate($obj->tms);
1685
				$this->remise				= $obj->remise;
1686
				$this->remise_percent		= $obj->remise_percent;
1687
				$this->remise_absolue		= $obj->remise_absolue;
1688
				$this->source				= $obj->source;
1689
				$this->billed				= $obj->billed;
1690
				$this->note					= $obj->note_private;	// deprecated
1691
				$this->note_private			= $obj->note_private;
1692
				$this->note_public			= $obj->note_public;
1693
				$this->fk_project			= $obj->fk_projet;
1694
				$this->modelpdf				= $obj->model_pdf;
1695
				$this->last_main_doc		= $obj->last_main_doc;
1696
				$this->mode_reglement_id	= $obj->fk_mode_reglement;
1697
				$this->mode_reglement_code	= $obj->mode_reglement_code;
1698
				$this->mode_reglement		= $obj->mode_reglement_libelle;
1699
				$this->cond_reglement_id	= $obj->fk_cond_reglement;
1700
				$this->cond_reglement_code	= $obj->cond_reglement_code;
1701
				$this->cond_reglement		= $obj->cond_reglement_libelle;
1702
				$this->cond_reglement_doc	= $obj->cond_reglement_libelle_doc;
1703
				$this->fk_account           = $obj->fk_account;
1704
				$this->availability_id		= $obj->fk_availability;
1705
				$this->availability_code	= $obj->availability_code;
1706
				$this->availability	    	= $obj->availability_label;
1707
				$this->demand_reason_id		= $obj->fk_input_reason;
1708
				$this->demand_reason_code	= $obj->demand_reason_code;
1709
				$this->date_livraison		= $this->db->jdate($obj->date_livraison);
1710
				$this->shipping_method_id   = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1711
				$this->warehouse_id           = ($obj->fk_warehouse>0)?$obj->fk_warehouse:null;
1712
				$this->fk_delivery_address	= $obj->fk_delivery_address;
1713
1714
				//Incoterms
1715
				$this->fk_incoterms = $obj->fk_incoterms;
1716
				$this->location_incoterms = $obj->location_incoterms;
1717
				$this->libelle_incoterms = $obj->libelle_incoterms;
1718
1719
				// Multicurrency
1720
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1721
				$this->multicurrency_code 		= $obj->multicurrency_code;
1722
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
1723
				$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
1724
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1725
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1726
1727
				$this->extraparams			= (array) json_decode($obj->extraparams, true);
1728
1729
				$this->lines				= array();
1730
1731
				if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
1732
1733
				// Retreive all extrafield
1734
				// fetch optionals attributes and labels
1735
				$this->fetch_optionals();
1736
1737
				$this->db->free($result);
1738
1739
				/*
1740
				 * Lines
1741
				 */
1742
				$result=$this->fetch_lines();
1743
				if ($result < 0)
1744
				{
1745
					return -3;
1746
				}
1747
				return 1;
1748
			}
1749
			else
1750
			{
1751
				$this->error='Order with id '.$id.' not found sql='.$sql;
1752
				return 0;
1753
			}
1754
		}
1755
		else
1756
		{
1757
			$this->error=$this->db->error();
1758
			return -1;
1759
		}
1760
	}
1761
1762
1763
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1764
	/**
1765
	 *	Adding line of fixed discount in the order in DB
1766
	 *
1767
	 *	@param     int	$idremise			Id de la remise fixe
1768
	 *	@return    int          			>0 si ok, <0 si ko
1769
	 */
1770
	function insert_discount($idremise)
1771
	{
1772
        // phpcs:enable
1773
		global $langs;
1774
1775
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1776
		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1777
1778
		$this->db->begin();
1779
1780
		$remise=new DiscountAbsolute($this->db);
1781
		$result=$remise->fetch($idremise);
1782
1783
		if ($result > 0)
1784
		{
1785
			if ($remise->fk_facture)	// Protection against multiple submission
1786
			{
1787
				$this->error=$langs->trans("ErrorDiscountAlreadyUsed");
1788
				$this->db->rollback();
1789
				return -5;
1790
			}
1791
1792
			$line = new OrderLine($this->db);
1793
1794
			$line->fk_commande=$this->id;
1795
			$line->fk_remise_except=$remise->id;
1796
			$line->desc=$remise->description;   	// Description ligne
1797
			$line->vat_src_code=$remise->vat_src_code;
1798
			$line->tva_tx=$remise->tva_tx;
1799
			$line->subprice=-$remise->amount_ht;
1800
			$line->price=-$remise->amount_ht;
1801
			$line->fk_product=0;					// Id produit predefini
1802
			$line->qty=1;
1803
			$line->remise=0;
1804
			$line->remise_percent=0;
1805
			$line->rang=-1;
1806
			$line->info_bits=2;
1807
1808
			$line->total_ht  = -$remise->amount_ht;
1809
			$line->total_tva = -$remise->amount_tva;
1810
			$line->total_ttc = -$remise->amount_ttc;
1811
1812
			$result=$line->insert();
1813
			if ($result > 0)
1814
			{
1815
				$result=$this->update_price(1);
1816
				if ($result > 0)
1817
				{
1818
					$this->db->commit();
1819
					return 1;
1820
				}
1821
				else
1822
				{
1823
					$this->db->rollback();
1824
					return -1;
1825
				}
1826
			}
1827
			else
1828
			{
1829
				$this->error=$line->error;
1830
				$this->db->rollback();
1831
				return -2;
1832
			}
1833
		}
1834
		else
1835
		{
1836
			$this->db->rollback();
1837
			return -2;
1838
		}
1839
	}
1840
1841
1842
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1843
	/**
1844
	 *	Load array lines
1845
	 *
1846
	 *	@param		int		$only_product	Return only physical products
1847
	 *	@return		int						<0 if KO, >0 if OK
1848
	 */
1849
	function fetch_lines($only_product=0)
1850
	{
1851
        // phpcs:enable
1852
		$this->lines=array();
1853
1854
		$sql = 'SELECT l.rowid, l.fk_product, l.fk_parent_line, l.product_type, l.fk_commande, l.label as custom_label, l.description, l.price, l.qty, l.vat_src_code, l.tva_tx,';
1855
		$sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.fk_remise_except, l.remise_percent, l.subprice, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht, l.rang, l.info_bits, l.special_code,';
1856
		$sql.= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
1857
		$sql.= ' l.fk_unit,';
1858
		$sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
1859
		$sql.= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch,';
1860
		$sql.= ' p.weight, p.weight_units, p.volume, p.volume_units';
1861
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
1862
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
1863
		$sql.= ' WHERE l.fk_commande = '.$this->id;
1864
		if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1865
		$sql .= ' ORDER BY l.rang, l.rowid';
1866
1867
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1868
		$result = $this->db->query($sql);
1869
		if ($result)
1870
		{
1871
			$num = $this->db->num_rows($result);
1872
1873
			$i = 0;
1874
			while ($i < $num)
1875
			{
1876
				$objp = $this->db->fetch_object($result);
1877
1878
				$line = new OrderLine($this->db);
1879
1880
				$line->rowid            = $objp->rowid;
1881
				$line->id               = $objp->rowid;
1882
				$line->fk_commande      = $objp->fk_commande;
1883
				$line->commande_id      = $objp->fk_commande;
1884
				$line->label            = $objp->custom_label;
1885
				$line->desc             = $objp->description;
1886
				$line->description      = $objp->description;		// Description line
1887
				$line->product_type     = $objp->product_type;
1888
				$line->qty              = $objp->qty;
1889
1890
				$line->vat_src_code     = $objp->vat_src_code;
1891
				$line->tva_tx           = $objp->tva_tx;
1892
				$line->localtax1_tx     = $objp->localtax1_tx;
1893
				$line->localtax2_tx     = $objp->localtax2_tx;
1894
				$line->localtax1_type	= $objp->localtax1_type;
1895
				$line->localtax2_type	= $objp->localtax2_type;
1896
				$line->total_ht         = $objp->total_ht;
1897
				$line->total_ttc        = $objp->total_ttc;
1898
				$line->total_tva        = $objp->total_tva;
1899
				$line->total_localtax1  = $objp->total_localtax1;
1900
				$line->total_localtax2  = $objp->total_localtax2;
1901
				$line->subprice         = $objp->subprice;
1902
				$line->fk_remise_except = $objp->fk_remise_except;
1903
				$line->remise_percent   = $objp->remise_percent;
1904
				$line->price            = $objp->price;
1905
				$line->fk_product       = $objp->fk_product;
1906
				$line->fk_fournprice 	= $objp->fk_fournprice;
1907
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1908
				$line->pa_ht 			= $marginInfos[0];
1909
				$line->marge_tx			= $marginInfos[1];
1910
				$line->marque_tx		= $marginInfos[2];
1911
				$line->rang             = $objp->rang;
1912
				$line->info_bits        = $objp->info_bits;
1913
				$line->special_code		= $objp->special_code;
1914
				$line->fk_parent_line	= $objp->fk_parent_line;
1915
1916
				$line->ref				= $objp->product_ref;
1917
				$line->product_ref		= $objp->product_ref;
1918
				$line->libelle			= $objp->product_label;
1919
				$line->product_label	= $objp->product_label;
1920
				$line->product_desc     = $objp->product_desc;
1921
				$line->product_tobatch  = $objp->product_tobatch;
1922
				$line->fk_product_type  = $objp->fk_product_type;	// Produit ou service
1923
				$line->fk_unit          = $objp->fk_unit;
1924
1925
				$line->weight           = $objp->weight;
1926
				$line->weight_units     = $objp->weight_units;
1927
				$line->volume           = $objp->volume;
1928
				$line->volume_units     = $objp->volume_units;
1929
1930
				$line->date_start       = $this->db->jdate($objp->date_start);
1931
				$line->date_end         = $this->db->jdate($objp->date_end);
1932
1933
				// Multicurrency
1934
				$line->fk_multicurrency 		= $objp->fk_multicurrency;
1935
				$line->multicurrency_code 		= $objp->multicurrency_code;
1936
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
1937
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
1938
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
1939
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
1940
1941
				$line->fetch_optionals();
1942
1943
                $this->lines[$i] = $line;
1944
1945
				$i++;
1946
			}
1947
1948
			$this->db->free($result);
1949
1950
			return 1;
1951
		}
1952
		else
1953
		{
1954
			$this->error=$this->db->error();
1955
			return -3;
1956
		}
1957
	}
1958
1959
1960
	/**
1961
	 *	Return number of line with type product.
1962
	 *
1963
	 *	@return		int		<0 if KO, Nbr of product lines if OK
1964
	 */
1965
	function getNbOfProductsLines()
1966
	{
1967
		$nb=0;
1968
		foreach($this->lines as $line)
1969
		{
1970
			if ($line->product_type == 0) $nb++;
1971
		}
1972
		return $nb;
1973
	}
1974
1975
	/**
1976
	 *	Return number of line with type service.
1977
	 *
1978
	 *	@return		int		<0 if KO, Nbr of service lines if OK
1979
	 */
1980
	function getNbOfServicesLines()
1981
	{
1982
		$nb=0;
1983
		foreach($this->lines as $line)
1984
		{
1985
			if ($line->product_type == 1) $nb++;
1986
		}
1987
		return $nb;
1988
	}
1989
1990
	/**
1991
	 *	Count numbe rof shipments for this order
1992
	 *
1993
	 * 	@return     int                			<0 if KO, Nb of shipment found if OK
1994
	 */
1995
	function getNbOfShipments()
1996
	{
1997
		$nb = 0;
1998
1999
		$sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2000
		$sql.= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2001
		$sql.= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2002
		$sql.= ' WHERE';
2003
		$sql.= ' ed.fk_origin_line = cd.rowid';
2004
		$sql.= ' AND cd.fk_commande =' .$this->id;
2005
		//print $sql;
2006
2007
		dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2008
		$resql = $this->db->query($sql);
2009
		if ($resql)
2010
		{
2011
			$obj = $this->db->fetch_object($resql);
2012
			if ($obj) $nb = $obj->nb;
2013
2014
			$this->db->free($resql);
2015
			return $nb;
2016
		}
2017
		else
2018
		{
2019
			$this->error=$this->db->lasterror();
2020
			return -1;
2021
		}
2022
	}
2023
2024
	/**
2025
	 *	Load array this->expeditions of lines of shipments with nb of products sent for each order line
2026
	 *  Note: For a dedicated shipment, the fetch_lines can be used to load the qty_asked and qty_shipped. This function is use to return qty_shipped cumulated for the order
2027
	 *
2028
	 *	@param      int		$filtre_statut      Filter on shipment status
2029
	 * 	@return     int                			<0 if KO, Nb of lines found if OK
2030
	 */
2031
	function loadExpeditions($filtre_statut=-1)
2032
	{
2033
		$this->expeditions = array();
2034
2035
		$sql = 'SELECT cd.rowid, cd.fk_product,';
2036
		$sql.= ' sum(ed.qty) as qty';
2037
		$sql.= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2038
		if ($filtre_statut >= 0) $sql.= ' '.MAIN_DB_PREFIX.'expedition as e,';
2039
		$sql.= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2040
		$sql.= ' WHERE';
2041
		if ($filtre_statut >= 0) $sql.= ' ed.fk_expedition = e.rowid AND';
2042
		$sql.= ' ed.fk_origin_line = cd.rowid';
2043
		$sql.= ' AND cd.fk_commande =' .$this->id;
2044
		if ($this->fk_product > 0) $sql.= ' AND cd.fk_product = '.$this->fk_product;
2045
		if ($filtre_statut >= 0) $sql.=' AND e.fk_statut >= '.$filtre_statut;
2046
		$sql.= ' GROUP BY cd.rowid, cd.fk_product';
2047
		//print $sql;
2048
2049
		dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2050
		$resql = $this->db->query($sql);
2051
		if ($resql)
2052
		{
2053
			$num = $this->db->num_rows($resql);
2054
			$i = 0;
2055
			while ($i < $num)
2056
			{
2057
				$obj = $this->db->fetch_object($resql);
2058
				$this->expeditions[$obj->rowid] = $obj->qty;
2059
				$i++;
2060
			}
2061
			$this->db->free($resql);
2062
			return $num;
2063
		}
2064
		else
2065
		{
2066
			$this->error=$this->db->lasterror();
2067
			return -1;
2068
		}
2069
	}
2070
2071
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2072
	/**
2073
	 * Returns a array with expeditions lines number
2074
	 *
2075
	 * @return	int		Nb of shipments
2076
	 *
2077
	 * TODO deprecate, move to Shipping class
2078
	 */
2079
	function nb_expedition()
2080
	{
2081
        // phpcs:enable
2082
		$sql = 'SELECT count(*)';
2083
		$sql.= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2084
		$sql.= ', '.MAIN_DB_PREFIX.'element_element as el';
2085
		$sql.= ' WHERE el.fk_source = '.$this->id;
2086
		$sql.= " AND el.fk_target = e.rowid";
2087
		$sql.= " AND el.targettype = 'shipping'";
2088
2089
		$resql = $this->db->query($sql);
2090
		if ($resql)
2091
		{
2092
			$row = $this->db->fetch_row($resql);
2093
			return $row[0];
2094
		}
2095
		else dol_print_error($this->db);
2096
	}
2097
2098
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2099
	/**
2100
	 *	Return a array with the pending stock by product
2101
	 *
2102
	 *	@param      int		$filtre_statut      Filtre sur statut
2103
	 *	@return     int                 		0 si OK, <0 si KO
2104
	 *
2105
	 *	TODO		FONCTION NON FINIE A FINIR
2106
	 */
2107
	function stock_array($filtre_statut=self::STATUS_CANCELED)
2108
	{
2109
        // phpcs:enable
2110
		$this->stocks = array();
2111
2112
		// Tableau des id de produit de la commande
2113
		$array_of_product=array();
2114
2115
		// Recherche total en stock pour chaque produit
2116
		// TODO $array_of_product est défini vide juste au dessus !!
2117
		if (count($array_of_product))
2118
		{
2119
			$sql = "SELECT fk_product, sum(ps.reel) as total";
2120
			$sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2121
			$sql.= " WHERE ps.fk_product IN (".join(',',$array_of_product).")";
2122
			$sql.= ' GROUP BY fk_product ';
2123
			$resql = $this->db->query($sql);
2124
			if ($resql)
2125
			{
2126
				$num = $this->db->num_rows($resql);
2127
				$i = 0;
2128
				while ($i < $num)
2129
				{
2130
					$obj = $this->db->fetch_object($resql);
2131
					$this->stocks[$obj->fk_product] = $obj->total;
2132
					$i++;
2133
				}
2134
				$this->db->free($resql);
2135
			}
2136
		}
2137
		return 0;
2138
	}
2139
2140
	/**
2141
	 *  Delete an order line
2142
	 *
2143
	 *	@param      User	$user		User object
2144
	 *  @param      int		$lineid		Id of line to delete
2145
	 *  @return     int        		 	>0 if OK, 0 if nothing to do, <0 if KO
2146
	 */
2147
	function deleteline($user=null, $lineid=0)
2148
	{
2149
		if ($this->statut == self::STATUS_DRAFT)
2150
		{
2151
			$this->db->begin();
2152
2153
			$sql = "SELECT fk_product, qty";
2154
			$sql.= " FROM ".MAIN_DB_PREFIX."commandedet";
2155
			$sql.= " WHERE rowid = ".$lineid;
2156
2157
			$result = $this->db->query($sql);
2158
			if ($result)
2159
			{
2160
				$obj = $this->db->fetch_object($result);
2161
2162
				if ($obj)
2163
				{
2164
					$product = new Product($this->db);
2165
					$product->id = $obj->fk_product;
2166
2167
					// Delete line
2168
					$line = new OrderLine($this->db);
2169
2170
					// For triggers
2171
					$line->fetch($lineid);
2172
2173
					if ($line->delete($user) > 0)
2174
					{
2175
						$result=$this->update_price(1);
2176
2177
						if ($result > 0)
2178
						{
2179
							$this->db->commit();
2180
							return 1;
2181
						}
2182
						else
2183
						{
2184
							$this->db->rollback();
2185
							$this->error=$this->db->lasterror();
2186
							return -1;
2187
						}
2188
					}
2189
					else
2190
					{
2191
						$this->db->rollback();
2192
						$this->error=$line->error;
2193
						return -1;
2194
					}
2195
				}
2196
				else
2197
				{
2198
					$this->db->rollback();
2199
					return 0;
2200
				}
2201
			}
2202
			else
2203
			{
2204
				$this->db->rollback();
2205
				$this->error=$this->db->lasterror();
2206
				return -1;
2207
			}
2208
		}
2209
		else
2210
		{
2211
			$this->error='ErrorDeleteLineNotAllowedByObjectStatus';
2212
			return -1;
2213
		}
2214
	}
2215
2216
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2217
	/**
2218
	 * 	Applique une remise relative
2219
	 *
2220
	 * 	@param     	User		$user		User qui positionne la remise
2221
	 * 	@param     	float		$remise		Discount (percent)
2222
	 * 	@param     	int			$notrigger	1=Does not execute triggers, 0= execute triggers
2223
	 *	@return		int 					<0 if KO, >0 if OK
2224
	 */
2225
	function set_remise($user, $remise, $notrigger=0)
2226
	{
2227
        // phpcs:enable
2228
		$remise=trim($remise)?trim($remise):0;
2229
2230
		if ($user->rights->commande->creer)
2231
		{
2232
			$error=0;
2233
2234
			$this->db->begin();
2235
2236
			$remise=price2num($remise);
2237
2238
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2239
			$sql.= ' SET remise_percent = '.$remise;
2240
			$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2241
2242
			dol_syslog(__METHOD__, LOG_DEBUG);
2243
			$resql=$this->db->query($sql);
2244
			if (!$resql)
2245
			{
2246
				$this->errors[]=$this->db->error();
2247
				$error++;
2248
			}
2249
2250
			if (! $error)
2251
			{
2252
				$this->oldcopy= clone $this;
2253
				$this->remise_percent = $remise;
2254
				$this->update_price(1);
2255
			}
2256
2257
			if (! $notrigger && empty($error))
2258
			{
2259
				// Call trigger
2260
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2261
				if ($result < 0) $error++;
2262
				// End call triggers
2263
			}
2264
2265
			if (! $error)
2266
			{
2267
				$this->db->commit();
2268
				return 1;
2269
			}
2270
			else
2271
			{
2272
				foreach($this->errors as $errmsg)
2273
				{
2274
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2275
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2276
				}
2277
				$this->db->rollback();
2278
				return -1*$error;
2279
			}
2280
		}
2281
	}
2282
2283
2284
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2285
	/**
2286
	 * 		Applique une remise absolue
2287
	 *
2288
	 * 		@param     	User		$user 		User qui positionne la remise
2289
	 * 		@param     	float		$remise		Discount
2290
	 * 		@param     	int			$notrigger	1=Does not execute triggers, 0= execute triggers
2291
	 *		@return		int 					<0 if KO, >0 if OK
2292
	 */
2293
	function set_remise_absolue($user, $remise, $notrigger=0)
2294
	{
2295
        // phpcs:enable
2296
		$remise=trim($remise)?trim($remise):0;
2297
2298
		if ($user->rights->commande->creer)
2299
		{
2300
			$error=0;
2301
2302
			$this->db->begin();
2303
2304
			$remise=price2num($remise);
2305
2306
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2307
			$sql.= ' SET remise_absolue = '.$remise;
2308
			$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2309
2310
			dol_syslog(__METHOD__, LOG_DEBUG);
2311
			$resql=$this->db->query($sql);
2312
			if (!$resql)
2313
			{
2314
				$this->errors[]=$this->db->error();
2315
				$error++;
2316
			}
2317
2318
			if (! $error)
2319
			{
2320
				$this->oldcopy= clone $this;
2321
				$this->remise_absolue = $remise;
2322
				$this->update_price(1);
2323
			}
2324
2325
			if (! $notrigger && empty($error))
2326
			{
2327
				// Call trigger
2328
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2329
				if ($result < 0) $error++;
2330
				// End call triggers
2331
			}
2332
2333
			if (! $error)
2334
			{
2335
				$this->db->commit();
2336
				return 1;
2337
			}
2338
			else
2339
			{
2340
				foreach($this->errors as $errmsg)
2341
				{
2342
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2343
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2344
				}
2345
				$this->db->rollback();
2346
				return -1*$error;
2347
			}
2348
		}
2349
	}
2350
2351
2352
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2353
	/**
2354
	 *	Set the order date
2355
	 *
2356
	 *	@param      User	$user       Object user making change
2357
	 *	@param      int		$date		Date
2358
	 * 	@param     	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2359
	 *	@return     int         		<0 if KO, >0 if OK
2360
	 */
2361
	function set_date($user, $date, $notrigger=0)
2362
	{
2363
        // phpcs:enable
2364
		if ($user->rights->commande->creer)
2365
		{
2366
			$error=0;
2367
2368
			$this->db->begin();
2369
2370
			$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2371
			$sql.= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2372
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2373
2374
			dol_syslog(__METHOD__, LOG_DEBUG);
2375
			$resql=$this->db->query($sql);
2376
			if (!$resql)
2377
			{
2378
				$this->errors[]=$this->db->error();
2379
				$error++;
2380
			}
2381
2382
			if (! $error)
2383
			{
2384
				$this->oldcopy= clone $this;
2385
				$this->date = $date;
2386
			}
2387
2388
			if (! $notrigger && empty($error))
2389
			{
2390
				// Call trigger
2391
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2392
				if ($result < 0) $error++;
2393
				// End call triggers
2394
			}
2395
2396
			if (! $error)
2397
			{
2398
				$this->db->commit();
2399
				return 1;
2400
			}
2401
			else
2402
			{
2403
				foreach($this->errors as $errmsg)
2404
				{
2405
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2406
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2407
				}
2408
				$this->db->rollback();
2409
				return -1*$error;
2410
			}
2411
		}
2412
		else
2413
		{
2414
			return -2;
2415
		}
2416
	}
2417
2418
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2419
	/**
2420
	 *	Set the planned delivery date
2421
	 *
2422
	 *	@param      User	$user        		Objet utilisateur qui modifie
2423
	 *	@param      int		$date_livraison     Date de livraison
2424
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2425
	 *	@return     int         				<0 si ko, >0 si ok
2426
	 */
2427
	function set_date_livraison($user, $date_livraison, $notrigger=0)
2428
	{
2429
        // phpcs:enable
2430
		if ($user->rights->commande->creer)
2431
		{
2432
			$error=0;
2433
2434
			$this->db->begin();
2435
2436
			$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2437
			$sql.= " SET date_livraison = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
2438
			$sql.= " WHERE rowid = ".$this->id;
2439
2440
			dol_syslog(__METHOD__, LOG_DEBUG);
2441
			$resql=$this->db->query($sql);
2442
			if (!$resql)
2443
			{
2444
				$this->errors[]=$this->db->error();
2445
				$error++;
2446
			}
2447
2448
			if (! $error)
2449
			{
2450
				$this->oldcopy= clone $this;
2451
				$this->date_livraison = $date_livraison;
2452
			}
2453
2454
			if (! $notrigger && empty($error))
2455
			{
2456
				// Call trigger
2457
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2458
				if ($result < 0) $error++;
2459
				// End call triggers
2460
			}
2461
2462
			if (! $error)
2463
			{
2464
				$this->db->commit();
2465
				return 1;
2466
			}
2467
			else
2468
			{
2469
				foreach($this->errors as $errmsg)
2470
				{
2471
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2472
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2473
				}
2474
				$this->db->rollback();
2475
				return -1*$error;
2476
			}
2477
		}
2478
		else
2479
		{
2480
			return -2;
2481
		}
2482
	}
2483
2484
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2485
	/**
2486
	 *  Return list of orders (eventuelly filtered on a user) into an array
2487
	 *
2488
	 *  @param		int		$shortlist		0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2489
	 *  @param      int		$draft      	0=not draft, 1=draft
2490
	 *  @param      User	$excluser      	Objet user to exclude
2491
	 *  @param    	int		$socid			Id third pary
2492
	 *  @param    	int		$limit			For pagination
2493
	 *  @param    	int		$offset			For pagination
2494
	 *  @param    	string	$sortfield		Sort criteria
2495
	 *  @param    	string	$sortorder		Sort order
2496
	 *  @return     int             		-1 if KO, array with result if OK
2497
	 */
2498
	function liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC')
2499
	{
2500
        // phpcs:enable
2501
		global $user;
2502
2503
		$ga = array();
2504
2505
		$sql = "SELECT s.rowid, s.nom as name, s.client,";
2506
		$sql.= " c.rowid as cid, c.ref";
2507
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2508
		$sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2509
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2510
		$sql.= " WHERE c.entity IN (".getEntity('commande').")";
2511
		$sql.= " AND c.fk_soc = s.rowid";
2512
		if (! $user->rights->societe->client->voir && ! $socid) //restriction
2513
		{
2514
			$sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2515
		}
2516
		if ($socid) $sql.= " AND s.rowid = ".$socid;
2517
		if ($draft) $sql.= " AND c.fk_statut = ".self::STATUS_DRAFT;
2518
		if (is_object($excluser)) $sql.= " AND c.fk_user_author <> ".$excluser->id;
2519
		$sql.= $this->db->order($sortfield,$sortorder);
2520
		$sql.= $this->db->plimit($limit,$offset);
2521
2522
		$result=$this->db->query($sql);
2523
		if ($result)
2524
		{
2525
			$numc = $this->db->num_rows($result);
2526
			if ($numc)
2527
			{
2528
				$i = 0;
2529
				while ($i < $numc)
2530
				{
2531
					$obj = $this->db->fetch_object($result);
2532
2533
					if ($shortlist == 1)
2534
					{
2535
						$ga[$obj->cid] = $obj->ref;
2536
					}
2537
					else if ($shortlist == 2)
2538
					{
2539
						$ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2540
					}
2541
					else
2542
					{
2543
						$ga[$i]['id']	= $obj->cid;
2544
						$ga[$i]['ref'] 	= $obj->ref;
2545
						$ga[$i]['name'] = $obj->name;
2546
					}
2547
					$i++;
2548
				}
2549
			}
2550
			return $ga;
2551
		}
2552
		else
2553
		{
2554
			dol_print_error($this->db);
2555
			return -1;
2556
		}
2557
	}
2558
2559
	/**
2560
	 *	Update delivery delay
2561
	 *
2562
	 *	@param      int		$availability_id	Id du nouveau mode
2563
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2564
	 *	@return     int         				>0 if OK, <0 if KO
2565
	 */
2566
	function availability($availability_id, $notrigger=0)
2567
	{
2568
		global $user;
2569
2570
		dol_syslog('Commande::availability('.$availability_id.')');
2571
		if ($this->statut >= self::STATUS_DRAFT)
2572
		{
2573
			$error=0;
2574
2575
			$this->db->begin();
2576
2577
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2578
			$sql .= ' SET fk_availability = '.$availability_id;
2579
			$sql .= ' WHERE rowid='.$this->id;
2580
2581
			dol_syslog(__METHOD__, LOG_DEBUG);
2582
			$resql=$this->db->query($sql);
2583
			if (!$resql)
2584
			{
2585
				$this->errors[]=$this->db->error();
2586
				$error++;
2587
			}
2588
2589
			if (! $error)
2590
			{
2591
				$this->oldcopy= clone $this;
2592
				$this->availability_id = $availability_id;
2593
			}
2594
2595
			if (! $notrigger && empty($error))
2596
			{
2597
				// Call trigger
2598
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2599
				if ($result < 0) $error++;
2600
				// End call triggers
2601
			}
2602
2603
			if (! $error)
2604
			{
2605
				$this->db->commit();
2606
				return 1;
2607
			}
2608
			else
2609
			{
2610
				foreach($this->errors as $errmsg)
2611
				{
2612
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2613
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2614
				}
2615
				$this->db->rollback();
2616
				return -1*$error;
2617
			}
2618
		}
2619
		else
2620
		{
2621
			$error_str='Command status do not meet requirement '.$this->statut;
2622
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2623
			$this->error=$error_str;
2624
			$this->errors[]= $this->error;
2625
			return -2;
2626
		}
2627
	}
2628
2629
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2630
	/**
2631
	 *	Update order demand_reason
2632
	 *
2633
	 *  @param      int		$demand_reason_id	Id of new demand
2634
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2635
	 *  @return     int        			 		>0 if ok, <0 if ko
2636
	 */
2637
	function demand_reason($demand_reason_id, $notrigger=0)
2638
	{
2639
        // phpcs:enable
2640
		global $user;
2641
2642
		dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2643
		if ($this->statut >= self::STATUS_DRAFT)
2644
		{
2645
			$error=0;
2646
2647
			$this->db->begin();
2648
2649
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2650
			$sql .= ' SET fk_input_reason = '.$demand_reason_id;
2651
			$sql .= ' WHERE rowid='.$this->id;
2652
2653
			dol_syslog(__METHOD__, LOG_DEBUG);
2654
			$resql=$this->db->query($sql);
2655
			if (!$resql)
2656
			{
2657
				$this->errors[]=$this->db->error();
2658
				$error++;
2659
			}
2660
2661
			if (! $error)
2662
			{
2663
				$this->oldcopy= clone $this;
2664
				$this->demand_reason_id = $demand_reason_id;
2665
			}
2666
2667
			if (! $notrigger && empty($error))
2668
			{
2669
				// Call trigger
2670
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2671
				if ($result < 0) $error++;
2672
				// End call triggers
2673
			}
2674
2675
			if (! $error)
2676
			{
2677
				$this->db->commit();
2678
				return 1;
2679
			}
2680
			else
2681
			{
2682
				foreach($this->errors as $errmsg)
2683
				{
2684
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2685
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2686
				}
2687
				$this->db->rollback();
2688
				return -1*$error;
2689
			}
2690
		}
2691
		else
2692
		{
2693
			$error_str='order status do not meet requirement '.$this->statut;
2694
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2695
			$this->error=$error_str;
2696
			$this->errors[]= $this->error;
2697
			return -2;
2698
		}
2699
	}
2700
2701
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2702
	/**
2703
	 *	Set customer ref
2704
	 *
2705
	 *	@param      User	$user           User that make change
2706
	 *	@param      string	$ref_client     Customer ref
2707
	 *  @param     	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2708
	 *	@return     int             		<0 if KO, >0 if OK
2709
	 */
2710
	function set_ref_client($user, $ref_client, $notrigger=0)
2711
	{
2712
        // phpcs:enable
2713
		if ($user->rights->commande->creer)
2714
		{
2715
			$error=0;
2716
2717
			$this->db->begin();
2718
2719
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2720
			$sql.= ' ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2721
			$sql.= ' WHERE rowid = '.$this->id;
2722
2723
			dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2724
			$resql=$this->db->query($sql);
2725
			if (!$resql)
2726
			{
2727
				$this->errors[]=$this->db->error();
2728
				$error++;
2729
			}
2730
2731
			if (! $error)
2732
			{
2733
				$this->oldcopy= clone $this;
2734
				$this->ref_client = $ref_client;
2735
			}
2736
2737
			if (! $notrigger && empty($error))
2738
			{
2739
				// Call trigger
2740
				$result=$this->call_trigger('ORDER_MODIFY',$user);
2741
				if ($result < 0) $error++;
2742
				// End call triggers
2743
			}
2744
			if (! $error)
2745
			{
2746
				$this->db->commit();
2747
				return 1;
2748
			}
2749
			else
2750
			{
2751
				foreach($this->errors as $errmsg)
2752
				{
2753
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2754
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2755
				}
2756
				$this->db->rollback();
2757
				return -1*$error;
2758
			}
2759
		}
2760
		else
2761
		{
2762
			return -1;
2763
		}
2764
	}
2765
2766
	/**
2767
	 * Classify the order as invoiced
2768
	 *
2769
	 * @param	User    $user       Object user making the change
2770
	 * @param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2771
	 * @return	int                 <0 if KO, >0 if OK
2772
	 */
2773
	function classifyBilled(User $user, $notrigger=0)
2774
	{
2775
		$error = 0;
2776
2777
		$this->db->begin();
2778
2779
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2780
		$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2781
2782
		dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
2783
		if ($this->db->query($sql))
2784
		{
2785
2786
			if (! $error)
2787
			{
2788
				$this->oldcopy= clone $this;
2789
				$this->billed=1;
2790
			}
2791
2792
			if (! $notrigger && empty($error))
2793
			{
2794
				// Call trigger
2795
				$result=$this->call_trigger('ORDER_CLASSIFY_BILLED',$user);
2796
				if ($result < 0) $error++;
2797
				// End call triggers
2798
			}
2799
2800
			if (! $error)
2801
			{
2802
				$this->db->commit();
2803
				return 1;
2804
			}
2805
			else
2806
			{
2807
				foreach($this->errors as $errmsg)
2808
				{
2809
					dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
2810
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2811
				}
2812
				$this->db->rollback();
2813
				return -1*$error;
2814
			}
2815
		}
2816
		else
2817
		{
2818
			$this->error=$this->db->error();
2819
			$this->db->rollback();
2820
			return -1;
2821
		}
2822
	}
2823
2824
	/**
2825
	 * Classify the order as not invoiced
2826
	 *
2827
	 * @return     int     <0 if ko, >0 if ok
2828
	 */
2829
	function classifyUnBilled()
2830
	{
2831
		global $conf, $user, $langs;
2832
		$error = 0;
2833
2834
		$this->db->begin();
2835
2836
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
2837
		$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2838
2839
		dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
2840
		if ($this->db->query($sql))
2841
		{
2842
			if (! $error)
2843
			{
2844
				$this->oldcopy= clone $this;
2845
				$this->billed=1;
2846
			}
2847
2848
			// Call trigger
2849
			$result=$this->call_trigger('ORDER_CLASSIFY_UNBILLED',$user);
2850
			if ($result < 0) $error++;
2851
			// End call triggers
2852
2853
			if (! $error)
2854
			{
2855
				$this->billed=0;
2856
2857
				$this->db->commit();
2858
				return 1;
2859
			}
2860
			else
2861
			{
2862
				foreach($this->errors as $errmsg)
2863
				{
2864
					dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
2865
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2866
				}
2867
				$this->db->rollback();
2868
				return -1*$error;
2869
			}
2870
		}
2871
		else
2872
		{
2873
			$this->error=$this->db->error();
2874
			$this->db->rollback();
2875
			return -1;
2876
		}
2877
	}
2878
2879
2880
	/**
2881
	 *  Update a line in database
2882
	 *
2883
	 *  @param    	int				$rowid            	Id of line to update
2884
	 *  @param    	string			$desc             	Description of line
2885
	 *  @param    	float			$pu               	Unit price
2886
	 *  @param    	float			$qty              	Quantity
2887
	 *  @param    	float			$remise_percent   	Percent of discount
2888
	 *  @param    	float			$txtva           	Taux TVA
2889
	 * 	@param		float			$txlocaltax1		Local tax 1 rate
2890
	 *  @param		float			$txlocaltax2		Local tax 2 rate
2891
	 *  @param    	string			$price_base_type	HT or TTC
2892
	 *  @param    	int				$info_bits        	Miscellaneous informations on line
2893
	 *  @param    	int				$date_start        	Start date of the line
2894
	 *  @param    	int				$date_end          	End date of the line
2895
	 * 	@param		int				$type				Type of line (0=product, 1=service)
2896
	 * 	@param		int				$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
2897
	 * 	@param		int				$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
2898
	 *  @param		int				$fk_fournprice		Id of origin supplier price
2899
	 *  @param		int				$pa_ht				Price (without tax) of product when it was bought
2900
	 *  @param		string			$label				Label
2901
	 *  @param		int				$special_code		Special code (also used by externals modules!)
2902
	 *  @param		array			$array_options		extrafields array
2903
	 * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
2904
	 *  @param		double			$pu_ht_devise		Amount in currency
2905
	 * 	@param		int				$notrigger			disable line update trigger
2906
	 *  @return   	int              					< 0 if KO, > 0 if OK
2907
	 */
2908
	function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0,$txlocaltax2=0.0, $price_base_type='HT', $info_bits=0, $date_start='', $date_end='', $type=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $fk_unit=null, $pu_ht_devise = 0, $notrigger=0)
2909
	{
2910
		global $conf, $mysoc, $langs, $user;
2911
2912
		dol_syslog(get_class($this)."::updateline id=$rowid, desc=$desc, pu=$pu, qty=$qty, remise_percent=$remise_percent, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, price_base_type=$price_base_type, info_bits=$info_bits, date_start=$date_start, date_end=$date_end, type=$type, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, special_code=$special_code");
2913
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2914
2915
		if (! empty($this->brouillon))
2916
		{
2917
			$this->db->begin();
2918
2919
			// Clean parameters
2920
			if (empty($qty)) $qty=0;
2921
			if (empty($info_bits)) $info_bits=0;
2922
			if (empty($txtva)) $txtva=0;
2923
			if (empty($txlocaltax1)) $txlocaltax1=0;
2924
			if (empty($txlocaltax2)) $txlocaltax2=0;
2925
			if (empty($remise_percent)) $remise_percent=0;
2926
			if (empty($special_code) || $special_code == 3) $special_code=0;
2927
2928
			$remise_percent=price2num($remise_percent);
2929
			$qty=price2num($qty);
2930
			$pu = price2num($pu);
2931
			$pa_ht=price2num($pa_ht);
2932
			$pu_ht_devise=price2num($pu_ht_devise);
2933
			$txtva=price2num($txtva);
2934
			$txlocaltax1=price2num($txlocaltax1);
2935
			$txlocaltax2=price2num($txlocaltax2);
2936
2937
			// Calcul du total TTC et de la TVA pour la ligne a partir de
2938
			// qty, pu, remise_percent et txtva
2939
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2940
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2941
2942
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
2943
2944
			// Clean vat code
2945
			$vat_src_code='';
2946
			if (preg_match('/\((.*)\)/', $txtva, $reg))
2947
			{
2948
				$vat_src_code = $reg[1];
2949
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
2950
			}
2951
2952
			$tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
2953
2954
			$total_ht  = $tabprice[0];
2955
			$total_tva = $tabprice[1];
2956
			$total_ttc = $tabprice[2];
2957
			$total_localtax1 = $tabprice[9];
2958
			$total_localtax2 = $tabprice[10];
2959
			$pu_ht  = $tabprice[3];
2960
			$pu_tva = $tabprice[4];
2961
			$pu_ttc = $tabprice[5];
2962
2963
			// MultiCurrency
2964
			$multicurrency_total_ht  = $tabprice[16];
2965
			$multicurrency_total_tva = $tabprice[17];
2966
			$multicurrency_total_ttc = $tabprice[18];
2967
			$pu_ht_devise = $tabprice[19];
2968
2969
			// Anciens indicateurs: $price, $subprice (a ne plus utiliser)
2970
			$price = $pu_ht;
2971
			if ($price_base_type == 'TTC')
2972
			{
2973
				$subprice = $pu_ttc;
2974
			}
2975
			else
2976
			{
2977
				$subprice = $pu_ht;
2978
			}
2979
			$remise = 0;
2980
			if ($remise_percent > 0)
2981
			{
2982
				$remise = round(($pu * $remise_percent / 100),2);
2983
				$price = ($pu - $remise);
2984
			}
2985
2986
			//Fetch current line from the database and then clone the object and set it in $oldline property
2987
			$line = new OrderLine($this->db);
2988
			$line->fetch($rowid);
2989
2990
			if (!empty($line->fk_product))
2991
			{
2992
				$product=new Product($this->db);
2993
				$result=$product->fetch($line->fk_product);
2994
				$product_type=$product->type;
2995
2996
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
2997
				{
2998
					$langs->load("errors");
2999
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3000
					dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3001
					$this->db->rollback();
3002
					return self::STOCK_NOT_ENOUGH_FOR_ORDER;
3003
				}
3004
			}
3005
3006
			$staticline = clone $line;
3007
3008
			$line->oldline = $staticline;
3009
			$this->line = $line;
3010
			$this->line->context = $this->context;
3011
3012
			// Reorder if fk_parent_line change
3013
			if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
3014
			{
3015
				$rangmax = $this->line_max($fk_parent_line);
3016
				$this->line->rang = $rangmax + 1;
3017
			}
3018
3019
			$this->line->rowid=$rowid;
0 ignored issues
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3019
			/** @scrutinizer ignore-deprecated */ $this->line->rowid=$rowid;

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

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

Loading history...
3020
			$this->line->label=$label;
3021
			$this->line->desc=$desc;
3022
			$this->line->qty=$qty;
0 ignored issues
show
Documentation Bug introduced by
The property $qty was declared of type double, but $qty 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...
3023
3024
			$this->line->vat_src_code	= $vat_src_code;
3025
			$this->line->tva_tx         = $txtva;
0 ignored issues
show
Documentation Bug introduced by
The property $tva_tx was declared of type double, but $txtva 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...
3026
			$this->line->localtax1_tx   = $txlocaltax1;
0 ignored issues
show
Documentation Bug introduced by
The property $localtax1_tx was declared of type double, but $txlocaltax1 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...
3027
			$this->line->localtax2_tx   = $txlocaltax2;
0 ignored issues
show
Documentation Bug introduced by
The property $localtax2_tx was declared of type double, but $txlocaltax2 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...
3028
			$this->line->localtax1_type = $localtaxes_type[0];
3029
			$this->line->localtax2_type = $localtaxes_type[2];
3030
			$this->line->remise_percent = $remise_percent;
0 ignored issues
show
Documentation Bug introduced by
The property $remise_percent was declared of type double, but $remise_percent 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...
3031
			$this->line->subprice       = $subprice;
3032
			$this->line->info_bits      = $info_bits;
3033
			$this->line->special_code   = $special_code;
3034
			$this->line->total_ht       = $total_ht;
3035
			$this->line->total_tva      = $total_tva;
3036
			$this->line->total_localtax1= $total_localtax1;
3037
			$this->line->total_localtax2= $total_localtax2;
3038
			$this->line->total_ttc      = $total_ttc;
3039
			$this->line->date_start     = $date_start;
3040
			$this->line->date_end       = $date_end;
3041
			$this->line->product_type   = $type;
3042
			$this->line->fk_parent_line = $fk_parent_line;
3043
			$this->line->skip_update_total=$skip_update_total;
3044
			$this->line->fk_unit        = $fk_unit;
3045
3046
			$this->line->fk_fournprice = $fk_fournprice;
3047
			$this->line->pa_ht = $pa_ht;
0 ignored issues
show
Documentation Bug introduced by
The property $pa_ht was declared of type double, but $pa_ht 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...
3048
3049
			// Multicurrency
3050
			$this->line->multicurrency_subprice		= $pu_ht_devise;
3051
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
3052
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
3053
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
3054
3055
			// TODO deprecated
3056
			$this->line->price=$price;
3057
			$this->line->remise=$remise;
3058
3059
			if (is_array($array_options) && count($array_options)>0) {
3060
				$this->line->array_options=$array_options;
3061
			}
3062
3063
			$result=$this->line->update($user, $notrigger);
3064
			if ($result > 0)
3065
			{
3066
				// Reorder if child line
3067
				if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
3068
3069
				// Mise a jour info denormalisees
3070
				$this->update_price(1);
3071
3072
				$this->db->commit();
3073
				return $result;
3074
			}
3075
			else
3076
			{
3077
				$this->error=$this->line->error;
3078
3079
				$this->db->rollback();
3080
				return -1;
3081
			}
3082
		}
3083
		else
3084
		{
3085
			$this->error=get_class($this)."::updateline Order status makes operation forbidden";
3086
			$this->errors=array('OrderStatusMakeOperationForbidden');
3087
			return -2;
3088
		}
3089
	}
3090
3091
	/**
3092
	 *      Update database
3093
	 *
3094
	 *      @param      User	$user        	User that modify
3095
	 *      @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
3096
	 *      @return     int      			   	<0 if KO, >0 if OK
3097
	 */
3098
	function update(User $user, $notrigger=0)
3099
	{
3100
		global $conf;
3101
3102
		$error=0;
3103
3104
		// Clean parameters
3105
		if (isset($this->ref)) $this->ref=trim($this->ref);
3106
		if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
3107
		if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3108
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
3109
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
3110
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
3111
3112
		// Check parameters
3113
		// Put here code to add control on parameters values
3114
3115
		// Update request
3116
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3117
3118
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
3119
		$sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
3120
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
3121
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
3122
		$sql.= " date_commande=".(strval($this->date_commande)!='' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3123
		$sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3124
		$sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
3125
		$sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
3126
		$sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
3127
		$sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
3128
		$sql.= " total_ttc=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
3129
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
3130
		$sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
3131
		$sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
3132
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
3133
		$sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
3134
		$sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
3135
		$sql.= " fk_account=".($this->fk_account>0?$this->fk_account:"null").",";
3136
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
3137
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
3138
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
3139
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
3140
3141
		$sql.= " WHERE rowid=".$this->id;
3142
3143
		$this->db->begin();
3144
3145
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
3146
		$resql = $this->db->query($sql);
3147
		if (! $resql) {
3148
			$error++; $this->errors[]="Error ".$this->db->lasterror();
3149
		}
3150
3151
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
3152
		{
3153
			$result=$this->insertExtraFields();
3154
			if ($result < 0)
3155
			{
3156
				$error++;
3157
			}
3158
		}
3159
3160
		if (! $error && ! $notrigger)
3161
		{
3162
			// Call trigger
3163
			$result=$this->call_trigger('ORDER_MODIFY', $user);
3164
			if ($result < 0) $error++;
3165
			// End call triggers
3166
		}
3167
3168
		// Commit or rollback
3169
		if ($error)
3170
		{
3171
			foreach($this->errors as $errmsg)
3172
			{
3173
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3174
				$this->error.=($this->error?', '.$errmsg:$errmsg);
3175
			}
3176
			$this->db->rollback();
3177
			return -1*$error;
3178
		}
3179
		else
3180
		{
3181
			$this->db->commit();
3182
			return 1;
3183
		}
3184
	}
3185
3186
	/**
3187
	 *	Delete the customer order
3188
	 *
3189
	 *	@param	User	$user		User object
3190
	 *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
3191
	 * 	@return	int					<=0 if KO, >0 if OK
3192
	 */
3193
	function delete($user, $notrigger=0)
3194
	{
3195
		global $conf, $langs;
3196
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3197
3198
		$error = 0;
3199
3200
		dol_syslog(get_class($this) . "::delete ".$this->id, LOG_DEBUG);
3201
3202
		$this->db->begin();
3203
3204
		if (! $error && ! $notrigger)
3205
		{
3206
			// Call trigger
3207
			$result=$this->call_trigger('ORDER_DELETE',$user);
3208
			if ($result < 0) $error++;
3209
			// End call triggers
3210
		}
3211
3212
		if ($this->nb_expedition() != 0)
3213
		{
3214
			$this->errors[] = $langs->trans('SomeShipmentExists');
3215
			$error++;
3216
		}
3217
3218
		if (! $error)
3219
		{
3220
			// Delete order details
3221
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE fk_commande = ".$this->id;
3222
			if (! $this->db->query($sql) )
3223
			{
3224
				$error++;
3225
				$this->errors[]=$this->db->lasterror();
3226
			}
3227
		}
3228
3229
		if (! $error)
3230
		{
3231
			// Delete linked object
3232
			$res = $this->deleteObjectLinked();
3233
			if ($res < 0) $error++;
3234
		}
3235
3236
		if (! $error)
3237
		{
3238
			// Delete linked contacts
3239
			$res = $this->delete_linked_contact();
3240
			if ($res < 0) $error++;
3241
		}
3242
3243
		if (! $error)
3244
		{
3245
			// Remove extrafields
3246
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
3247
			{
3248
				$result=$this->deleteExtraFields();
3249
				if ($result < 0)
3250
				{
3251
					$error++;
3252
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3253
				}
3254
			}
3255
		}
3256
3257
		if (! $error)
3258
		{
3259
			// Delete object
3260
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande WHERE rowid = ".$this->id;
3261
			if (! $this->db->query($sql) )
3262
			{
3263
				$error++;
3264
				$this->errors[]=$this->db->lasterror();
3265
			}
3266
		}
3267
3268
		if (! $error)
3269
		{
3270
			// Remove directory with files
3271
			$comref = dol_sanitizeFileName($this->ref);
3272
			if ($conf->commande->dir_output && !empty($this->ref))
3273
			{
3274
				$dir = $conf->commande->dir_output . "/" . $comref ;
3275
				$file = $conf->commande->dir_output . "/" . $comref . "/" . $comref . ".pdf";
3276
				if (file_exists($file))	// We must delete all files before deleting directory
3277
				{
3278
					dol_delete_preview($this);
3279
3280
					if (! dol_delete_file($file,0,0,0,$this)) // For triggers
3281
					{
3282
						$this->db->rollback();
3283
						return 0;
3284
					}
3285
				}
3286
				if (file_exists($dir))
3287
				{
3288
					if (! dol_delete_dir_recursive($dir))
3289
					{
3290
						$this->error=$langs->trans("ErrorCanNotDeleteDir",$dir);
3291
						$this->db->rollback();
3292
						return 0;
3293
					}
3294
				}
3295
			}
3296
		}
3297
3298
		if (! $error)
3299
		{
3300
			$this->db->commit();
3301
			return 1;
3302
		}
3303
		else
3304
		{
3305
			foreach($this->errors as $errmsg)
3306
			{
3307
				$this->error.=($this->error?', '.$errmsg:$errmsg);
3308
			}
3309
			$this->db->rollback();
3310
			return -1*$error;
3311
		}
3312
	}
3313
3314
3315
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3316
	/**
3317
	 *	Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3318
	 *
3319
	 *	@param		User	$user   Object user
3320
	 *	@return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
3321
	 */
3322
	function load_board($user)
3323
	{
3324
        // phpcs:enable
3325
		global $conf, $langs;
3326
3327
		$clause = " WHERE";
3328
3329
		$sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3330
		$sql.= " FROM ".MAIN_DB_PREFIX."commande as c";
3331
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3332
		{
3333
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3334
			$sql.= " WHERE sc.fk_user = " .$user->id;
3335
			$clause = " AND";
3336
		}
3337
		$sql.= $clause." c.entity IN (".getEntity('commande').")";
3338
		//$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3339
		$sql.= " AND ((c.fk_statut IN (".self::STATUS_VALIDATED.",".self::STATUS_SHIPMENTONPROCESS.")) OR (c.fk_statut = ".self::STATUS_CLOSED." AND c.facture = 0))";    // If status is 2 and facture=1, it must be selected
3340
		if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
3341
3342
		$resql=$this->db->query($sql);
3343
		if ($resql)
3344
		{
3345
			$response = new WorkboardResponse();
3346
			$response->warning_delay=$conf->commande->client->warning_delay/60/60/24;
3347
			$response->label=$langs->trans("OrdersToProcess");
3348
			$response->url=DOL_URL_ROOT.'/commande/list.php?viewstatut=-3&mainmenu=commercial&leftmenu=orders';
3349
			$response->img=img_object('',"order");
3350
3351
			$generic_commande = new Commande($this->db);
3352
3353
			while ($obj=$this->db->fetch_object($resql))
3354
			{
3355
				$response->nbtodo++;
3356
				$response->total+= $obj->total_ht;
3357
3358
				$generic_commande->statut = $obj->fk_statut;
3359
				$generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3360
				$generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3361
3362
				if ($generic_commande->hasDelay()) {
3363
					$response->nbtodolate++;
3364
				}
3365
			}
3366
3367
			return $response;
3368
		}
3369
		else
3370
		{
3371
			$this->error=$this->db->error();
3372
			return -1;
3373
		}
3374
	}
3375
3376
	/**
3377
	 *	Return source label of order
3378
	 *
3379
	 *	@return     string      Label
3380
	 */
3381
	function getLabelSource()
3382
	{
3383
		global $langs;
3384
3385
		$label=$langs->trans('OrderSource'.$this->source);
3386
3387
		if ($label == 'OrderSource') return '';
3388
		return $label;
3389
	}
3390
3391
	/**
3392
	 *	Return status label of Order
3393
	 *
3394
	 *	@param      int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
3395
	 *	@return     string      		Label of status
3396
	 */
3397
	function getLibStatut($mode)
3398
	{
3399
		return $this->LibStatut($this->statut, $this->billed, $mode);
3400
	}
3401
3402
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3403
	/**
3404
	 *	Return label of status
3405
	 *
3406
	 *	@param		int		$statut      	  Id statut
3407
	 *  @param      int		$billed    		  If invoiced
3408
	 *	@param      int		$mode        	  0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
3409
	 *  @param      int     $donotshowbilled  Do not show billed status after order status
3410
	 *  @return     string					  Label of status
3411
	 */
3412
	function LibStatut($statut,$billed,$mode,$donotshowbilled=0)
3413
	{
3414
        // phpcs:enable
3415
		global $langs, $conf;
3416
3417
		$billedtext = '';
3418
		if (empty($donotshowbilled)) $billedtext .= ($billed?' - '.$langs->trans("Billed"):'');
3419
3420
		//print 'x'.$statut.'-'.$billed;
3421
		if ($mode == 0)
3422
		{
3423
			if ($statut==self::STATUS_CANCELED) return $langs->trans('StatusOrderCanceled');
3424
			if ($statut==self::STATUS_DRAFT) return $langs->trans('StatusOrderDraft');
3425
			if ($statut==self::STATUS_VALIDATED) return $langs->trans('StatusOrderValidated').$billedtext;
3426
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return $langs->trans('StatusOrderSentShort').$billedtext;
3427
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderToBill');
3428
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderProcessed').$billedtext;
3429
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderDelivered');
3430
		}
3431
		elseif ($mode == 1)
3432
		{
3433
			if ($statut==self::STATUS_CANCELED) return $langs->trans('StatusOrderCanceledShort');
3434
			if ($statut==self::STATUS_DRAFT) return $langs->trans('StatusOrderDraftShort');
3435
			if ($statut==self::STATUS_VALIDATED) return $langs->trans('StatusOrderValidatedShort').$billedtext;
3436
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return $langs->trans('StatusOrderSentShort').$billedtext;
3437
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderToBillShort');
3438
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderProcessed').$billedtext;
3439
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return $langs->trans('StatusOrderDelivered');
3440
		}
3441
		elseif ($mode == 2)
3442
		{
3443
			if ($statut==self::STATUS_CANCELED) return img_picto($langs->trans('StatusOrderCanceled'),'statut5').' '.$langs->trans('StatusOrderCanceledShort');
3444
			if ($statut==self::STATUS_DRAFT) return img_picto($langs->trans('StatusOrderDraft'),'statut0').' '.$langs->trans('StatusOrderDraftShort');
3445
			if ($statut==self::STATUS_VALIDATED) return img_picto($langs->trans('StatusOrderValidated'),'statut1').' '.$langs->trans('StatusOrderValidatedShort').$billedtext;
3446
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return img_picto($langs->trans('StatusOrderSent'),'statut3').' '.$langs->trans('StatusOrderSentShort').$billedtext;
3447
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderToBill'),'statut4').' '.$langs->trans('StatusOrderToBillShort');
3448
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6').' '.$langs->trans('StatusOrderProcessed').$billedtext;
3449
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderDelivered'),'statut6').' '.$langs->trans('StatusOrderDeliveredShort');
3450
		}
3451
		elseif ($mode == 3)
3452
		{
3453
			if ($statut==self::STATUS_CANCELED) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
3454
			if ($statut==self::STATUS_DRAFT) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
3455
			if ($statut==self::STATUS_VALIDATED) return img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1');
3456
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return img_picto($langs->trans('StatusOrderSentShort').$billedtext,'statut3');
3457
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
3458
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6');
3459
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderDelivered'),'statut6');
3460
		}
3461
		elseif ($mode == 4)
3462
		{
3463
			if ($statut==self::STATUS_CANCELED) return img_picto($langs->trans('StatusOrderCanceled'),'statut5').' '.$langs->trans('StatusOrderCanceled');
3464
			if ($statut==self::STATUS_DRAFT) return img_picto($langs->trans('StatusOrderDraft'),'statut0').' '.$langs->trans('StatusOrderDraft');
3465
			if ($statut==self::STATUS_VALIDATED) return img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1').' '.$langs->trans('StatusOrderValidated').$billedtext;
3466
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return img_picto($langs->trans('StatusOrderSentShort').$billedtext,'statut3').' '.$langs->trans('StatusOrderSent').$billedtext;
3467
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderToBill'),'statut4').' '.$langs->trans('StatusOrderToBill');
3468
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderProcessedShort').$billedtext,'statut6').' '.$langs->trans('StatusOrderProcessed').$billedtext;
3469
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return img_picto($langs->trans('StatusOrderDelivered'),'statut6').' '.$langs->trans('StatusOrderDelivered');
3470
		}
3471
		elseif ($mode == 5)
3472
		{
3473
			if ($statut==self::STATUS_CANCELED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderCanceledShort').' </span>'.img_picto($langs->trans('StatusOrderCanceled'),'statut5');
3474
			if ($statut==self::STATUS_DRAFT) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDraftShort').' </span>'.img_picto($langs->trans('StatusOrderDraft'),'statut0');
3475
			if ($statut==self::STATUS_VALIDATED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderValidatedShort').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1');
3476
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderSentShort').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderSent').$billedtext,'statut3');
3477
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderToBillShort').' </span>'.img_picto($langs->trans('StatusOrderToBill'),'statut4');
3478
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderProcessedShort').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6');
3479
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDeliveredShort').' </span>'.img_picto($langs->trans('StatusOrderDelivered'),'statut6');
3480
		}
3481
		elseif ($mode == 6)
3482
		{
3483
			if ($statut==self::STATUS_CANCELED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderCanceled').' </span>'.img_picto($langs->trans('StatusOrderCanceled'),'statut5');
3484
			if ($statut==self::STATUS_DRAFT) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDraft').' </span>'.img_picto($langs->trans('StatusOrderDraft'),'statut0');
3485
			if ($statut==self::STATUS_VALIDATED) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderValidated').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderValidated').$billedtext,'statut1');
3486
			if ($statut==self::STATUS_SHIPMENTONPROCESS) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderSent').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderSent').$billedtext,'statut3');
3487
			if ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderToBill').' </span>'.img_picto($langs->trans('StatusOrderToBill'),'statut4');
3488
			if ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderProcessed').$billedtext.' </span>'.img_picto($langs->trans('StatusOrderProcessed').$billedtext,'statut6');
3489
			if ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) return '<span class="hideonsmartphone">'.$langs->trans('StatusOrderDelivered').' </span>'.img_picto($langs->trans('StatusOrderDelivered'),'statut6');
3490
		}
3491
	}
3492
3493
3494
	/**
3495
	 *	Return clicable link of object (with eventually picto)
3496
	 *
3497
	 *	@param      int			$withpicto                Add picto into link
3498
	 *	@param      string	    $option                   Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3499
	 *	@param      int			$max          	          Max length to show
3500
	 *	@param      int			$short			          ???
3501
	 *  @param	    int   	    $notooltip		          1=Disable tooltip
3502
	 *  @param      int         $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3503
	 *	@return     string          			          String with URL
3504
	 */
3505
	function getNomUrl($withpicto=0, $option='', $max=0, $short=0, $notooltip=0, $save_lastsearch_value=-1)
3506
	{
3507
		global $conf, $langs, $user;
3508
3509
		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
3510
3511
		$result='';
3512
3513
		if (! empty($conf->expedition->enabled) && ($option == '1' || $option == '2')) $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3514
		else $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3515
3516
		if (!$user->rights->commande->lire)
3517
			$option = 'nolink';
3518
3519
		if ($option !== 'nolink')
3520
		{
3521
			// Add param to save lastsearch_values or not
3522
			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3523
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3524
			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3525
		}
3526
3527
		if ($short) return $url;
3528
3529
		$label = '';
3530
3531
		if ($user->rights->commande->lire) {
3532
			$label = '<u>'.$langs->trans("ShowOrder").'</u>';
3533
			$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3534
			$label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
3535
			if (!empty($this->total_ht)) {
3536
				$label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3537
			}
3538
			if (!empty($this->total_tva)) {
3539
				$label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1,	$conf->currency);
3540
			}
3541
			if (!empty($this->total_ttc)) {
3542
				$label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3543
			}
3544
		}
3545
3546
		$linkclose='';
3547
		if (empty($notooltip) && $user->rights->commande->lire)
3548
		{
3549
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3550
			{
3551
				$label=$langs->trans("ShowOrder");
3552
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3553
			}
3554
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3555
			$linkclose.=' class="classfortooltip"';
3556
		}
3557
3558
		$linkstart = '<a href="'.$url.'"';
3559
		$linkstart.=$linkclose.'>';
3560
		$linkend='</a>';
3561
3562
		if ($option === 'nolink') {
3563
			$linkstart = '';
3564
			$linkend = '';
3565
		}
3566
3567
		$result .= $linkstart;
3568
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3569
		if ($withpicto != 2) $result.= $this->ref;
3570
		$result .= $linkend;
3571
3572
		return $result;
3573
	}
3574
3575
3576
	/**
3577
	 *	Charge les informations d'ordre info dans l'objet commande
3578
	 *
3579
	 *	@param  int		$id       Id of order
3580
	 *	@return	void
3581
	 */
3582
	function info($id)
3583
	{
3584
		$sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3585
		$sql.= ' date_valid as datev,';
3586
		$sql.= ' date_cloture as datecloture,';
3587
		$sql.= ' fk_user_author, fk_user_valid, fk_user_cloture';
3588
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3589
		$sql.= ' WHERE c.rowid = '.$id;
3590
		$result=$this->db->query($sql);
3591
		if ($result)
3592
		{
3593
			if ($this->db->num_rows($result))
3594
			{
3595
				$obj = $this->db->fetch_object($result);
3596
				$this->id = $obj->rowid;
3597
				if ($obj->fk_user_author)
3598
				{
3599
					$cuser = new User($this->db);
3600
					$cuser->fetch($obj->fk_user_author);
3601
					$this->user_creation   = $cuser;
3602
				}
3603
3604
				if ($obj->fk_user_valid)
3605
				{
3606
					$vuser = new User($this->db);
3607
					$vuser->fetch($obj->fk_user_valid);
3608
					$this->user_validation = $vuser;
3609
				}
3610
3611
				if ($obj->fk_user_cloture)
3612
				{
3613
					$cluser = new User($this->db);
3614
					$cluser->fetch($obj->fk_user_cloture);
3615
					$this->user_cloture   = $cluser;
3616
				}
3617
3618
				$this->date_creation     = $this->db->jdate($obj->datec);
3619
				$this->date_modification = $this->db->jdate($obj->datem);
3620
				$this->date_validation   = $this->db->jdate($obj->datev);
3621
				$this->date_cloture      = $this->db->jdate($obj->datecloture);
3622
			}
3623
3624
			$this->db->free($result);
3625
		}
3626
		else
3627
		{
3628
			dol_print_error($this->db);
3629
		}
3630
	}
3631
3632
3633
	/**
3634
	 *  Initialise an instance with random values.
3635
	 *  Used to build previews or test instances.
3636
	 *	id must be 0 if object instance is a specimen.
3637
	 *
3638
	 *  @return	void
3639
	 */
3640
	function initAsSpecimen()
3641
	{
3642
		global $langs;
3643
3644
		dol_syslog(get_class($this)."::initAsSpecimen");
3645
3646
		// Load array of products prodids
3647
		$num_prods = 0;
3648
		$prodids = array();
3649
		$sql = "SELECT rowid";
3650
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
3651
		$sql.= " WHERE entity IN (".getEntity('product').")";
3652
		$resql = $this->db->query($sql);
3653
		if ($resql)
3654
		{
3655
			$num_prods = $this->db->num_rows($resql);
3656
			$i = 0;
3657
			while ($i < $num_prods)
3658
			{
3659
				$i++;
3660
				$row = $this->db->fetch_row($resql);
3661
				$prodids[$i] = $row[0];
3662
			}
3663
		}
3664
3665
		// Initialise parametres
3666
		$this->id=0;
3667
		$this->ref = 'SPECIMEN';
3668
		$this->specimen=1;
3669
		$this->socid = 1;
3670
		$this->date = time();
3671
		$this->date_lim_reglement=$this->date+3600*24*30;
3672
		$this->cond_reglement_code = 'RECEP';
3673
		$this->mode_reglement_code = 'CHQ';
3674
		$this->availability_code   = 'DSP';
3675
		$this->demand_reason_code  = 'SRC_00';
3676
		$this->note_public='This is a comment (public)';
3677
		$this->note_private='This is a comment (private)';
3678
		// Lines
3679
		$nbp = 5;
3680
		$xnbp = 0;
3681
		while ($xnbp < $nbp)
3682
		{
3683
			$line=new OrderLine($this->db);
3684
3685
			$line->desc=$langs->trans("Description")." ".$xnbp;
3686
			$line->qty=1;
3687
			$line->subprice=100;
3688
			$line->price=100;
3689
			$line->tva_tx=20;
3690
			if ($xnbp == 2)
3691
			{
3692
				$line->total_ht=50;
3693
				$line->total_ttc=60;
3694
				$line->total_tva=10;
3695
				$line->remise_percent=50;
3696
			}
3697
			else
3698
			{
3699
				$line->total_ht=100;
3700
				$line->total_ttc=120;
3701
				$line->total_tva=20;
3702
				$line->remise_percent=0;
3703
			}
3704
			if ($num_prods > 0)
3705
			{
3706
				$prodid = mt_rand(1, $num_prods);
3707
				$line->fk_product=$prodids[$prodid];
3708
				$line->product_ref='SPECIMEN';
3709
			}
3710
3711
			$this->lines[$xnbp]=$line;
3712
3713
			$this->total_ht       += $line->total_ht;
3714
			$this->total_tva      += $line->total_tva;
3715
			$this->total_ttc      += $line->total_ttc;
3716
3717
			$xnbp++;
3718
		}
3719
	}
3720
3721
3722
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3723
	/**
3724
	 *	Charge indicateurs this->nb de tableau de bord
3725
	 *
3726
	 *	@return     int         <0 si ko, >0 si ok
3727
	 */
3728
	function load_state_board()
3729
	{
3730
        // phpcs:enable
3731
		global $user;
3732
3733
		$this->nb=array();
3734
		$clause = "WHERE";
3735
3736
		$sql = "SELECT count(co.rowid) as nb";
3737
		$sql.= " FROM ".MAIN_DB_PREFIX."commande as co";
3738
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3739
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3740
		{
3741
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3742
			$sql.= " WHERE sc.fk_user = " .$user->id;
3743
			$clause = "AND";
3744
		}
3745
		$sql.= " ".$clause." co.entity IN (".getEntity('commande').")";
3746
3747
		$resql=$this->db->query($sql);
3748
		if ($resql)
3749
		{
3750
			while ($obj=$this->db->fetch_object($resql))
3751
			{
3752
				$this->nb["orders"]=$obj->nb;
3753
			}
3754
			$this->db->free($resql);
3755
			return 1;
3756
		}
3757
		else
3758
		{
3759
			dol_print_error($this->db);
3760
			$this->error=$this->db->error();
3761
			return -1;
3762
		}
3763
	}
3764
3765
	/**
3766
	 * 	Create an array of order lines
3767
	 *
3768
	 * 	@return int		>0 if OK, <0 if KO
3769
	 */
3770
	function getLinesArray()
3771
	{
3772
		return $this->fetch_lines();
3773
	}
3774
3775
	/**
3776
	 *  Create a document onto disk according to template module.
3777
	 *
3778
	 *  @param	    string		$modele			Force template to use ('' to not force)
3779
	 *  @param		Translate	$outputlangs	objet lang a utiliser pour traduction
3780
	 *  @param      int			$hidedetails    Hide details of lines
3781
	 *  @param      int			$hidedesc       Hide description
3782
	 *  @param      int			$hideref        Hide ref
3783
	 *  @param      null|array  $moreparams     Array to provide more information
3784
	 *  @return     int         				0 if KO, 1 if OK
3785
	 */
3786
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
3787
	{
3788
		global $conf,$langs;
3789
3790
		$langs->load("orders");
3791
3792
		if (! dol_strlen($modele)) {
3793
3794
			$modele = 'einstein';
3795
3796
			if ($this->modelpdf) {
3797
				$modele = $this->modelpdf;
3798
			} elseif (! empty($conf->global->COMMANDE_ADDON_PDF)) {
3799
				$modele = $conf->global->COMMANDE_ADDON_PDF;
3800
			}
3801
		}
3802
3803
		$modelpath = "core/modules/commande/doc/";
3804
3805
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3806
	}
3807
3808
3809
	/**
3810
	 * Function used to replace a thirdparty id with another one.
3811
	 *
3812
	 * @param DoliDB $db Database handler
3813
	 * @param int $origin_id Old thirdparty id
3814
	 * @param int $dest_id New thirdparty id
3815
	 * @return bool
3816
	 */
3817
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3818
	{
3819
		$tables = array(
3820
		'commande'
3821
		);
3822
3823
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3824
	}
3825
3826
	/**
3827
	 * Is the customer order delayed?
3828
	 *
3829
	 * @return bool     true if late, false if not
3830
	 */
3831
	public function hasDelay()
3832
	{
3833
		global $conf;
3834
3835
		if (! ($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
3836
			return false;   // Never late if not inside this status range
3837
		}
3838
3839
		$now = dol_now();
3840
3841
		return max($this->date_commande, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
3842
	}
3843
3844
	/**
3845
	 * Show the customer delayed info
3846
	 *
3847
	 * @return string       Show delayed information
3848
	 */
3849
	public function showDelay()
3850
	{
3851
		global $conf, $langs;
3852
3853
		if (empty($this->date_livraison)) $text=$langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3854
		else $text=$text=$langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
3855
		$text.=' '.($conf->commande->client->warning_delay>0?'+':'-').' '.round(abs($conf->commande->client->warning_delay)/3600/24,1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3856
3857
		return $text;
3858
	}
3859
}
3860
3861
3862
/**
3863
 *  Class to manage order lines
3864
 */
3865
class OrderLine extends CommonOrderLine
3866
{
3867
	/**
3868
	 * @var string ID to identify managed object
3869
	 */
3870
	public $element='commandedet';
3871
3872
	public $table_element='commandedet';
3873
3874
	var $oldline;
3875
3876
	/**
3877
	 * Id of parent order
3878
	 * @var int
3879
	 */
3880
	public $fk_commande;
3881
3882
	/**
3883
	 * Id of parent order
3884
	 * @var int
3885
	 * @deprecated Use fk_commande
3886
	 * @see fk_commande
3887
	 */
3888
	public $commande_id;
3889
3890
	// From llx_commandedet
3891
	var $fk_parent_line;
3892
	var $fk_facture;
3893
3894
	/**
3895
	 * @var string Order lines label
3896
	 */
3897
	public $label;
3898
3899
	var $fk_remise_except;
3900
	var $rang = 0;
3901
	var $fk_fournprice;
3902
3903
	/**
3904
	 * Buy price without taxes
3905
	 * @var float
3906
	 */
3907
	var $pa_ht;
3908
	var $marge_tx;
3909
	var $marque_tx;
3910
3911
	/**
3912
	 * @deprecated
3913
	 * @see remise_percent, fk_remise_except
3914
	 */
3915
	var $remise;
3916
3917
	// Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
3918
	// Start and end date of the line
3919
	var $date_start;
3920
	var $date_end;
3921
3922
	var $skip_update_total; // Skip update price total for special lines
3923
3924
3925
	/**
3926
	 *      Constructor
3927
	 *
3928
	 *      @param     DoliDB	$db      handler d'acces base de donnee
3929
	 */
3930
	function __construct($db)
3931
	{
3932
		$this->db= $db;
3933
	}
3934
3935
	/**
3936
	 *  Load line order
3937
	 *
3938
	 *  @param  int		$rowid          Id line order
3939
	 *  @return	int						<0 if KO, >0 if OK
3940
	 */
3941
	function fetch($rowid)
3942
	{
3943
		$sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_parent_line, cd.fk_product, cd.product_type, cd.label as custom_label, cd.description, cd.price, cd.qty, cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx,';
3944
		$sql.= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice,';
3945
		$sql.= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.fk_product_fournisseur_price as fk_fournprice, cd.buy_price_ht as pa_ht, cd.rang, cd.special_code,';
3946
		$sql.= ' cd.fk_unit,';
3947
		$sql.= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3948
		$sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc, p.tobatch as product_tobatch,';
3949
		$sql.= ' cd.date_start, cd.date_end';
3950
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
3951
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3952
		$sql.= ' WHERE cd.rowid = '.$rowid;
3953
		$result = $this->db->query($sql);
3954
		if ($result)
3955
		{
3956
			$objp = $this->db->fetch_object($result);
3957
			$this->rowid            = $objp->rowid;
3958
			$this->id				= $objp->rowid;
3959
			$this->fk_commande      = $objp->fk_commande;
3960
			$this->fk_parent_line   = $objp->fk_parent_line;
3961
			$this->label            = $objp->custom_label;
3962
			$this->desc             = $objp->description;
3963
			$this->qty              = $objp->qty;
3964
			$this->price            = $objp->price;
3965
			$this->subprice         = $objp->subprice;
3966
			$this->vat_src_code     = $objp->vat_src_code;
3967
			$this->tva_tx           = $objp->tva_tx;
3968
			$this->localtax1_tx		= $objp->localtax1_tx;
3969
			$this->localtax2_tx		= $objp->localtax2_tx;
3970
			$this->remise           = $objp->remise;
3971
			$this->remise_percent   = $objp->remise_percent;
3972
			$this->fk_remise_except = $objp->fk_remise_except;
3973
			$this->fk_product       = $objp->fk_product;
3974
			$this->product_type     = $objp->product_type;
3975
			$this->info_bits        = $objp->info_bits;
3976
			$this->special_code		= $objp->special_code;
3977
			$this->total_ht         = $objp->total_ht;
3978
			$this->total_tva        = $objp->total_tva;
3979
			$this->total_localtax1  = $objp->total_localtax1;
3980
			$this->total_localtax2  = $objp->total_localtax2;
3981
			$this->total_ttc        = $objp->total_ttc;
3982
			$this->fk_fournprice	= $objp->fk_fournprice;
3983
			$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3984
			$this->pa_ht			= $marginInfos[0];
3985
			$this->marge_tx			= $marginInfos[1];
3986
			$this->marque_tx		= $marginInfos[2];
3987
			$this->special_code		= $objp->special_code;
3988
			$this->rang             = $objp->rang;
3989
3990
			$this->ref				= $objp->product_ref;      // deprecated
3991
			$this->product_ref		= $objp->product_ref;
3992
			$this->libelle			= $objp->product_libelle;  // deprecated
3993
			$this->product_label	= $objp->product_libelle;
3994
			$this->product_desc     = $objp->product_desc;
3995
			$this->product_tobatch  = $objp->product_tobatch;
3996
			$this->fk_unit          = $objp->fk_unit;
3997
3998
			$this->date_start       = $this->db->jdate($objp->date_start);
3999
			$this->date_end         = $this->db->jdate($objp->date_end);
4000
4001
			$this->fk_multicurrency			= $objp->fk_multicurrency;
4002
			$this->multicurrency_code		= $objp->multicurrency_code;
4003
			$this->multicurrency_subprice	= $objp->multicurrency_subprice;
4004
			$this->multicurrency_total_ht	= $objp->multicurrency_total_ht;
4005
			$this->multicurrency_total_tva	= $objp->multicurrency_total_tva;
4006
			$this->multicurrency_total_ttc	= $objp->multicurrency_total_ttc;
4007
4008
			$this->db->free($result);
4009
4010
			return 1;
4011
		}
4012
		else
4013
		{
4014
			$this->error = $this->db->lasterror();
4015
			return -1;
4016
		}
4017
	}
4018
4019
	/**
4020
	 * 	Delete line in database
4021
	 *
4022
	 *	@param      User	$user        	User that modify
4023
	 *  @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
4024
	 *	@return	 int  <0 si ko, >0 si ok
4025
	 */
4026
	function delete(User $user, $notrigger=0)
4027
	{
4028
		global $conf, $langs;
4029
4030
		$error=0;
4031
4032
		$this->db->begin();
4033
4034
		$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid=".$this->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

4034
		$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid="./** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4035
4036
		dol_syslog("OrderLine::delete", LOG_DEBUG);
4037
		$resql=$this->db->query($sql);
4038
		if ($resql)
4039
		{
4040
			// Remove extrafields
4041
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
4042
			{
4043
				$this->id=$this->rowid;
4044
				$result=$this->deleteExtraFields();
4045
				if ($result < 0)
4046
				{
4047
					$error++;
4048
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4049
				}
4050
			}
4051
4052
			if (! $error && ! $notrigger)
4053
			{
4054
				// Call trigger
4055
				$result=$this->call_trigger('LINEORDER_DELETE',$user);
4056
				if ($result < 0) $error++;
4057
				// End call triggers
4058
			}
4059
4060
			if (!$error) {
4061
				$this->db->commit();
4062
				return 1;
4063
			}
4064
4065
			foreach($this->errors as $errmsg)
4066
			{
4067
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4068
				$this->error.=($this->error?', '.$errmsg:$errmsg);
4069
			}
4070
			$this->db->rollback();
4071
			return -1*$error;
4072
		}
4073
		else
4074
		{
4075
			$this->error=$this->db->lasterror();
4076
			return -1;
4077
		}
4078
	}
4079
4080
	/**
4081
	 *	Insert line into database
4082
	 *
4083
	 *	@param      User	$user        	User that modify
4084
	 *	@param      int		$notrigger		1 = disable triggers
4085
	 *	@return		int						<0 if KO, >0 if OK
4086
	 */
4087
	function insert($user=null, $notrigger=0)
4088
	{
4089
		global $langs, $conf;
4090
4091
		$error=0;
4092
4093
		$pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4094
4095
		dol_syslog(get_class($this)."::insert rang=".$this->rang);
4096
4097
		// Clean parameters
4098
		if (empty($this->tva_tx)) $this->tva_tx=0;
4099
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4100
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4101
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4102
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4103
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4104
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4105
		if (empty($this->rang)) $this->rang=0;
4106
		if (empty($this->remise)) $this->remise=0;
4107
		if (empty($this->remise_percent)) $this->remise_percent=0;
4108
		if (empty($this->info_bits)) $this->info_bits=0;
4109
		if (empty($this->special_code)) $this->special_code=0;
4110
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4111
		if (empty($this->pa_ht)) $this->pa_ht=0;
4112
4113
		// if buy price not defined, define buyprice as configured in margin admin
4114
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4115
		{
4116
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4117
			{
4118
				return $result;
4119
			}
4120
			else
4121
			{
4122
				$this->pa_ht = $result;
4123
			}
4124
		}
4125
4126
		// Check parameters
4127
		if ($this->product_type < 0) return -1;
4128
4129
		$this->db->begin();
4130
4131
		// Insertion dans base de la ligne
4132
		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4133
		$sql.= ' (fk_commande, fk_parent_line, label, description, qty, ';
4134
		$sql.= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4135
		$sql.= ' fk_product, product_type, remise_percent, subprice, price, remise, fk_remise_except,';
4136
		$sql.= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4137
		$sql.= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4138
		$sql.= ' fk_unit';
4139
		$sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4140
		$sql.= ')';
4141
		$sql.= " VALUES (".$this->fk_commande.",";
4142
		$sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
4143
		$sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
4144
		$sql.= " '".$this->db->escape($this->desc)."',";
4145
		$sql.= " '".price2num($this->qty)."',";
4146
		$sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
4147
		$sql.= " '".price2num($this->tva_tx)."',";
4148
		$sql.= " '".price2num($this->localtax1_tx)."',";
4149
		$sql.= " '".price2num($this->localtax2_tx)."',";
4150
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
4151
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
4152
		$sql.= ' '.(! empty($this->fk_product)?$this->fk_product:"null").',';
4153
		$sql.= " '".$this->db->escape($this->product_type)."',";
4154
		$sql.= " '".price2num($this->remise_percent)."',";
4155
		$sql.= " ".(price2num($this->subprice)!==''?price2num($this->subprice):"null").",";
4156
		$sql.= " ".($this->price!=''?"'".price2num($this->price)."'":"null").",";
4157
		$sql.= " '".price2num($this->remise)."',";
4158
		$sql.= ' '.(! empty($this->fk_remise_except)?$this->fk_remise_except:"null").',';
4159
		$sql.= ' '.$this->special_code.',';
4160
		$sql.= ' '.$this->rang.',';
4161
		$sql.= ' '.(! empty($this->fk_fournprice)?$this->fk_fournprice:"null").',';
4162
		$sql.= ' '.price2num($this->pa_ht).',';
4163
		$sql.= " '".$this->db->escape($this->info_bits)."',";
4164
		$sql.= " ".price2num($this->total_ht).",";
4165
		$sql.= " ".price2num($this->total_tva).",";
4166
		$sql.= " ".price2num($this->total_localtax1).",";
4167
		$sql.= " ".price2num($this->total_localtax2).",";
4168
		$sql.= " ".price2num($this->total_ttc).",";
4169
		$sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
4170
		$sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null").',';
4171
		$sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4172
		$sql.= ", ".(! empty($this->fk_multicurrency) ? $this->fk_multicurrency : 'NULL');
4173
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
4174
		$sql.= ", ".$this->multicurrency_subprice;
4175
		$sql.= ", ".$this->multicurrency_total_ht;
4176
		$sql.= ", ".$this->multicurrency_total_tva;
4177
		$sql.= ", ".$this->multicurrency_total_ttc;
4178
		$sql.= ')';
4179
4180
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4181
		$resql=$this->db->query($sql);
4182
		if ($resql)
4183
		{
4184
			$this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4185
4186
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4187
			{
4188
				$this->id=$this->rowid;
4189
				$result=$this->insertExtraFields();
4190
				if ($result < 0)
4191
				{
4192
					$error++;
4193
				}
4194
			}
4195
4196
			if (! $error && ! $notrigger)
4197
			{
4198
				// Call trigger
4199
				$result=$this->call_trigger('LINEORDER_INSERT',$user);
4200
				if ($result < 0) $error++;
4201
				// End call triggers
4202
			}
4203
4204
			if (!$error) {
4205
				$this->db->commit();
4206
				return 1;
4207
			}
4208
4209
			foreach($this->errors as $errmsg)
4210
			{
4211
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4212
				$this->error.=($this->error?', '.$errmsg:$errmsg);
4213
			}
4214
			$this->db->rollback();
4215
			return -1*$error;
4216
		}
4217
		else
4218
		{
4219
			$this->error=$this->db->error();
4220
			$this->db->rollback();
4221
			return -2;
4222
		}
4223
	}
4224
4225
	/**
4226
	 *	Update the line object into db
4227
	 *
4228
	 *	@param      User	$user        	User that modify
4229
	 *	@param      int		$notrigger		1 = disable triggers
4230
	 *	@return		int		<0 si ko, >0 si ok
4231
	 */
4232
	function update(User $user, $notrigger=0)
4233
	{
4234
		global $conf,$langs;
4235
4236
		$error=0;
4237
4238
		$pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4239
4240
		// Clean parameters
4241
		if (empty($this->tva_tx)) $this->tva_tx=0;
4242
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4243
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4244
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4245
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4246
		if (empty($this->qty)) $this->qty=0;
4247
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4248
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4249
		if (empty($this->marque_tx)) $this->marque_tx=0;
4250
		if (empty($this->marge_tx)) $this->marge_tx=0;
4251
		if (empty($this->remise)) $this->remise=0;
4252
		if (empty($this->remise_percent)) $this->remise_percent=0;
4253
		if (empty($this->info_bits)) $this->info_bits=0;
4254
		if (empty($this->special_code)) $this->special_code=0;
4255
		if (empty($this->product_type)) $this->product_type=0;
4256
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4257
		if (empty($this->pa_ht)) $this->pa_ht=0;
4258
4259
		// if buy price not defined, define buyprice as configured in margin admin
4260
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4261
		{
4262
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4263
			{
4264
				return $result;
4265
			}
4266
			else
4267
			{
4268
				$this->pa_ht = $result;
4269
			}
4270
		}
4271
4272
		$this->db->begin();
4273
4274
		// Mise a jour ligne en base
4275
		$sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4276
		$sql.= " description='".$this->db->escape($this->desc)."'";
4277
		$sql.= " , label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4278
		$sql.= " , vat_src_code=".(! empty($this->vat_src_code)?"'".$this->db->escape($this->vat_src_code)."'":"''");
4279
		$sql.= " , tva_tx=".price2num($this->tva_tx);
4280
		$sql.= " , localtax1_tx=".price2num($this->localtax1_tx);
4281
		$sql.= " , localtax2_tx=".price2num($this->localtax2_tx);
4282
		$sql.= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4283
		$sql.= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4284
		$sql.= " , qty=".price2num($this->qty);
4285
		$sql.= " , subprice=".price2num($this->subprice)."";
4286
		$sql.= " , remise_percent=".price2num($this->remise_percent)."";
4287
		$sql.= " , price=".price2num($this->price)."";					// TODO A virer
4288
		$sql.= " , remise=".price2num($this->remise)."";				// TODO A virer
4289
		if (empty($this->skip_update_total))
4290
		{
4291
			$sql.= " , total_ht=".price2num($this->total_ht)."";
4292
			$sql.= " , total_tva=".price2num($this->total_tva)."";
4293
			$sql.= " , total_ttc=".price2num($this->total_ttc)."";
4294
			$sql.= " , total_localtax1=".price2num($this->total_localtax1);
4295
			$sql.= " , total_localtax2=".price2num($this->total_localtax2);
4296
		}
4297
		$sql.= " , fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?$this->fk_fournprice:"null");
4298
		$sql.= " , buy_price_ht='".price2num($this->pa_ht)."'";
4299
		$sql.= " , info_bits=".$this->info_bits;
4300
		$sql.= " , special_code=".$this->special_code;
4301
		$sql.= " , date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4302
		$sql.= " , date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4303
		$sql.= " , product_type=".$this->product_type;
4304
		$sql.= " , fk_parent_line=".(! empty($this->fk_parent_line)?$this->fk_parent_line:"null");
4305
		if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4306
		$sql.= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4307
4308
		// Multicurrency
4309
		$sql.= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4310
		$sql.= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4311
		$sql.= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4312
		$sql.= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4313
4314
		$sql.= " WHERE rowid = ".$this->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

4314
		$sql.= " WHERE rowid = "./** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4315
4316
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
4317
		$resql=$this->db->query($sql);
4318
		if ($resql)
4319
		{
4320
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4321
			{
4322
				$this->id=$this->rowid;
4323
				$result=$this->insertExtraFields();
4324
				if ($result < 0)
4325
				{
4326
					$error++;
4327
				}
4328
			}
4329
4330
			if (! $error && ! $notrigger)
4331
			{
4332
				// Call trigger
4333
				$result=$this->call_trigger('LINEORDER_UPDATE',$user);
4334
				if ($result < 0) $error++;
4335
				// End call triggers
4336
			}
4337
4338
			if (!$error) {
4339
				$this->db->commit();
4340
				return 1;
4341
			}
4342
4343
			foreach($this->errors as $errmsg)
4344
			{
4345
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4346
				$this->error.=($this->error?', '.$errmsg:$errmsg);
4347
			}
4348
			$this->db->rollback();
4349
			return -1*$error;
4350
		}
4351
		else
4352
		{
4353
			$this->error=$this->db->error();
4354
			$this->db->rollback();
4355
			return -2;
4356
		}
4357
	}
4358
4359
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4360
	/**
4361
	 *	Update DB line fields total_xxx
4362
	 *	Used by migration
4363
	 *
4364
	 *	@return		int		<0 if KO, >0 if OK
4365
	 */
4366
	function update_total()
4367
	{
4368
        // phpcs:enable
4369
		$this->db->begin();
4370
4371
		// Clean parameters
4372
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4373
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4374
4375
		// Mise a jour ligne en base
4376
		$sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4377
		$sql.= " total_ht='".price2num($this->total_ht)."'";
4378
		$sql.= ",total_tva='".price2num($this->total_tva)."'";
4379
		$sql.= ",total_localtax1='".price2num($this->total_localtax1)."'";
4380
		$sql.= ",total_localtax2='".price2num($this->total_localtax2)."'";
4381
		$sql.= ",total_ttc='".price2num($this->total_ttc)."'";
4382
		$sql.= " WHERE rowid = ".$this->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

4382
		$sql.= " WHERE rowid = "./** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4383
4384
		dol_syslog("OrderLine::update_total", LOG_DEBUG);
4385
4386
		$resql=$this->db->query($sql);
4387
		if ($resql)
4388
		{
4389
			$this->db->commit();
4390
			return 1;
4391
		}
4392
		else
4393
		{
4394
			$this->error=$this->db->error();
4395
			$this->db->rollback();
4396
			return -2;
4397
		}
4398
	}
4399
}
4400