Passed
Branch develop (5cbde9)
by
unknown
26:38
created

Commande::showDelay()   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nop 0
dl 0
loc 9
rs 10
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
	//! key of module source when order generated from a dedicated module ('cashdesk', 'takepos', ...)
208
	public $module_source;
209
	//! key of pos source ('0', '1', ...)
210
	public $pos_source;
211
212
	/**
213
	 * ERR Not enough stock
214
	 */
215
	const STOCK_NOT_ENOUGH_FOR_ORDER = -3;
216
217
	/**
218
	 * Canceled status
219
	 */
220
	const STATUS_CANCELED = -1;
221
	/**
222
	 * Draft status
223
	 */
224
	const STATUS_DRAFT = 0;
225
	/**
226
	 * Validated status
227
	 */
228
	const STATUS_VALIDATED = 1;
229
	/**
230
	 * Shipment on process
231
	 */
232
	const STATUS_SHIPMENTONPROCESS = 2;
233
	const STATUS_ACCEPTED = 2;				// For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
234
235
	/**
236
	 * Closed (Sent, billed or not)
237
	 */
238
	const STATUS_CLOSED = 3;
239
240
241
	/**
242
	 *	Constructor
243
	 *
244
	 *  @param		DoliDB		$db      Database handler
245
	 */
246
	public function __construct($db)
247
	{
248
		$this->db = $db;
249
250
		$this->remise = 0;
251
		$this->remise_percent = 0;
252
253
		$this->products = array();
254
	}
255
256
	/**
257
	 *  Returns the reference to the following non used Order depending on the active numbering module
258
	 *  defined into COMMANDE_ADDON
259
	 *
260
	 *  @param	Societe		$soc  	Object thirdparty
261
	 *  @return string      		Order free reference
262
	 */
263
	public function getNextNumRef($soc)
264
	{
265
		global $langs, $conf;
266
		$langs->load("order");
267
268
		if (! empty($conf->global->COMMANDE_ADDON))
269
		{
270
			$mybool=false;
271
272
			$file = $conf->global->COMMANDE_ADDON.".php";
273
			$classname = $conf->global->COMMANDE_ADDON;
274
275
			// Include file with class
276
			$dirmodels=array_merge(array('/'), (array) $conf->modules_parts['models']);
277
			foreach ($dirmodels as $reldir)
278
			{
279
				$dir = dol_buildpath($reldir."core/modules/commande/");
280
281
				// Load file with numbering class (if found)
282
				$mybool|=@include_once $dir.$file;
283
			}
284
285
            if ($mybool === false)
286
            {
287
                dol_print_error('', "Failed to include file ".$file);
288
                return '';
289
            }
290
291
			$obj = new $classname();
292
			$numref = $obj->getNextValue($soc, $this);
293
294
			if ($numref != "")
295
			{
296
				return $numref;
297
			}
298
			else
299
			{
300
				$this->error=$obj->error;
301
				//dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
302
				return "";
303
			}
304
		}
305
		else
306
		{
307
			print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
308
			return "";
309
		}
310
	}
311
312
313
	/**
314
	 *	Validate order
315
	 *
316
	 *	@param		User	$user     		User making status change
317
	 *	@param		int		$idwarehouse	Id of warehouse to use for stock decrease
318
	 *  @param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
319
	 *	@return  	int						<=0 if OK, 0=Nothing done, >0 if KO
320
	 */
321
	public function valid($user, $idwarehouse = 0, $notrigger = 0)
322
	{
323
		global $conf,$langs;
324
325
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
326
327
		$error=0;
328
329
		// Protection
330
		if ($this->statut == self::STATUS_VALIDATED)
331
		{
332
			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
333
			return 0;
334
		}
335
336
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
337
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate))))
338
		{
339
			$this->error='NotEnoughPermissions';
340
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
341
			return -1;
342
		}
343
344
		$now=dol_now();
345
346
		$this->db->begin();
347
348
		// Definition du nom de module de numerotation de commande
349
		$soc = new Societe($this->db);
350
		$soc->fetch($this->socid);
351
352
		// Class of company linked to order
353
		$result=$soc->set_as_client();
354
355
		// Define new ref
356
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
357
		{
358
			$num = $this->getNextNumRef($soc);
359
		}
360
		else
361
		{
362
			$num = $this->ref;
363
		}
364
		$this->newref = $num;
365
366
		// Validate
367
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
368
		$sql.= " SET ref = '".$num."',";
369
		$sql.= " fk_statut = ".self::STATUS_VALIDATED.",";
370
		$sql.= " date_valid='".$this->db->idate($now)."',";
371
		$sql.= " fk_user_valid = ".$user->id;
372
		$sql.= " WHERE rowid = ".$this->id;
373
374
		dol_syslog(get_class($this)."::valid()", LOG_DEBUG);
375
		$resql=$this->db->query($sql);
376
		if (! $resql)
377
		{
378
			dol_print_error($this->db);
379
			$this->error=$this->db->lasterror();
380
			$error++;
381
		}
382
383
		if (! $error)
384
		{
385
			// If stock is incremented on validate order, we must increment it
386
			if ($result >= 0 && ! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
387
			{
388
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
389
				$langs->load("agenda");
390
391
				// Loop on each line
392
				$cpt=count($this->lines);
393
				for ($i = 0; $i < $cpt; $i++)
394
				{
395
					if ($this->lines[$i]->fk_product > 0)
396
					{
397
						$mouvP = new MouvementStock($this->db);
398
						$mouvP->origin = &$this;
399
						// We decrement stock of product (and sub-products)
400
						$result=$mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
401
						if ($result < 0)
402
						{
403
							$error++;
404
							$this->error=$mouvP->error;
405
						}
406
					}
407
					if ($error) break;
408
				}
409
			}
410
		}
411
412
		if (! $error && ! $notrigger)
413
		{
414
			// Call trigger
415
			$result=$this->call_trigger('ORDER_VALIDATE', $user);
416
			if ($result < 0) $error++;
417
			// End call triggers
418
		}
419
420
		if (! $error)
421
		{
422
			$this->oldref = $this->ref;
423
424
			// Rename directory if dir was a temporary ref
425
			if (preg_match('/^[\(]?PROV/i', $this->ref))
426
			{
427
				// Now we rename also files into index
428
				$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref)+1).")), filepath = 'commande/".$this->db->escape($this->newref)."'";
429
				$sql.= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
430
				$resql = $this->db->query($sql);
431
				if (! $resql) { $error++; $this->error = $this->db->lasterror(); }
432
433
				// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
434
				$oldref = dol_sanitizeFileName($this->ref);
435
				$newref = dol_sanitizeFileName($num);
436
				$dirsource = $conf->commande->dir_output.'/'.$oldref;
437
				$dirdest = $conf->commande->dir_output.'/'.$newref;
438
				if (! $error && file_exists($dirsource))
439
				{
440
					dol_syslog(get_class($this)."::valid() rename dir ".$dirsource." into ".$dirdest);
441
442
					if (@rename($dirsource, $dirdest))
443
					{
444
						dol_syslog("Rename ok");
445
						// Rename docs starting with $oldref with $newref
446
						$listoffiles=dol_dir_list($conf->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
447
						foreach($listoffiles as $fileentry)
448
						{
449
							$dirsource=$fileentry['name'];
450
							$dirdest=preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
451
							$dirsource=$fileentry['path'].'/'.$dirsource;
452
							$dirdest=$fileentry['path'].'/'.$dirdest;
453
							@rename($dirsource, $dirdest);
1 ignored issue
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

453
							/** @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...
454
						}
455
					}
456
				}
457
			}
458
		}
459
460
		// Set new ref and current status
461
		if (! $error)
462
		{
463
			$this->ref = $num;
464
			$this->statut = self::STATUS_VALIDATED;
465
            $this->brouillon = 0;
466
		}
467
468
		if (! $error)
469
		{
470
			$this->db->commit();
471
			return 1;
472
		}
473
		else
474
		{
475
			$this->db->rollback();
476
			return -1;
477
		}
478
	}
479
480
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
481
	/**
482
	 *	Set draft status
483
	 *
484
	 *	@param	User	$user			Object user that modify
485
	 *	@param	int		$idwarehouse	Warehouse ID to use for stock change (Used only if option STOCK_CALCULATE_ON_VALIDATE_ORDER is on)
486
	 *	@return	int						<0 if KO, >0 if OK
487
	 */
488
    public function setDraft($user, $idwarehouse = -1)
489
    {
490
        //phpcs:enable
491
		global $conf,$langs;
492
493
		$error=0;
494
495
		// Protection
496
		if ($this->statut <= self::STATUS_DRAFT)
497
		{
498
			return 0;
499
		}
500
501
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
502
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate))))
503
		{
504
			$this->error='Permission denied';
505
			return -1;
506
		}
507
508
		dol_syslog(__METHOD__, LOG_DEBUG);
509
510
		$this->db->begin();
511
512
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
513
		$sql.= " SET fk_statut = ".self::STATUS_DRAFT;
514
		$sql.= " WHERE rowid = ".$this->id;
515
516
		if ($this->db->query($sql))
517
		{
518
		    if (! $error)
519
		    {
520
		        $this->oldcopy= clone $this;
521
		    }
522
523
		    // If stock is decremented on validate order, we must reincrement it
524
			if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
525
			{
526
				$result = 0;
527
528
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
529
				$langs->load("agenda");
530
531
				$num=count($this->lines);
532
				for ($i = 0; $i < $num; $i++)
533
				{
534
					if ($this->lines[$i]->fk_product > 0)
535
					{
536
						$mouvP = new MouvementStock($this->db);
537
						$mouvP->origin = &$this;
538
						// We increment stock of product (and sub-products)
539
						$result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
540
						if ($result < 0) { $error++; $this->error=$mouvP->error; break; }
541
					}
542
				}
543
			}
544
545
			if (!$error) {
546
				// Call trigger
547
				$result=$this->call_trigger('ORDER_UNVALIDATE', $user);
548
				if ($result < 0) $error++;
549
			}
550
551
			if (!$error) {
552
				$this->statut=self::STATUS_DRAFT;
553
				$this->db->commit();
554
				return 1;
555
			}else {
556
				$this->db->rollback();
557
				return -1;
558
			}
559
		}
560
		else
561
		{
562
			$this->error=$this->db->error();
563
			$this->db->rollback();
564
			return -1;
565
		}
566
    }
567
568
569
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
570
	/**
571
	 *	Tag the order as validated (opened)
572
	 *	Function used when order is reopend after being closed.
573
	 *
574
	 *	@param      User	$user       Object user that change status
575
	 *	@return     int         		<0 if KO, 0 if nothing is done, >0 if OK
576
	 */
577
	public function set_reopen($user)
578
	{
579
        // phpcs:enable
580
		$error=0;
581
582
		if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED)
583
		{
584
			dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
585
			return 0;
586
		}
587
588
		$this->db->begin();
589
590
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
591
		$sql.= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0';
592
		$sql.= ' WHERE rowid = '.$this->id;
593
594
		dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
595
		$resql = $this->db->query($sql);
596
		if ($resql)
597
		{
598
			// Call trigger
599
			$result=$this->call_trigger('ORDER_REOPEN', $user);
600
			if ($result < 0) $error++;
601
			// End call triggers
602
		}
603
		else
604
		{
605
			$error++;
606
			$this->error=$this->db->lasterror();
607
			dol_print_error($this->db);
608
		}
609
610
		if (! $error)
611
		{
612
			$this->statut = self::STATUS_VALIDATED;
613
			$this->billed = 0;
614
615
			$this->db->commit();
616
			return 1;
617
		}
618
		else
619
		{
620
			foreach($this->errors as $errmsg)
621
			{
622
				dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
623
				$this->error.=($this->error?', '.$errmsg:$errmsg);
624
			}
625
			$this->db->rollback();
626
			return -1*$error;
627
		}
628
	}
629
630
	/**
631
	 *  Close order
632
	 *
633
	 * 	@param      User	$user       Objet user that close
634
	 *  @param		int		$notrigger	1=Does not execute triggers, 0=Execute triggers
635
	 *	@return		int					<0 if KO, >0 if OK
636
	 */
637
	public function cloture($user, $notrigger = 0)
638
	{
639
		global $conf;
640
641
		$error=0;
642
643
		if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->creer))
644
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->commande->order_advance->validate)))
645
		{
646
			$this->db->begin();
647
648
			$now=dol_now();
649
650
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
651
			$sql.= ' SET fk_statut = '.self::STATUS_CLOSED.',';
652
			$sql.= ' fk_user_cloture = '.$user->id.',';
653
			$sql.= " date_cloture = '".$this->db->idate($now)."'";
654
			$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
655
656
			if ($this->db->query($sql))
657
			{
658
				if (! $notrigger)
659
				{
660
					// Call trigger
661
					$result=$this->call_trigger('ORDER_CLOSE', $user);
662
					if ($result < 0) $error++;
663
					// End call triggers
664
				}
665
666
				if (! $error)
667
				{
668
					$this->statut=self::STATUS_CLOSED;
669
670
					$this->db->commit();
671
					return 1;
672
				}
673
				else
674
				{
675
					$this->db->rollback();
676
					return -1;
677
				}
678
			}
679
			else
680
			{
681
				$this->error=$this->db->lasterror();
682
683
				$this->db->rollback();
684
				return -1;
685
			}
686
		}
687
		return 0;
688
	}
689
690
	/**
691
	 * 	Cancel an order
692
	 * 	If stock is decremented on order validation, we must reincrement it
693
	 *
694
	 *	@param	int		$idwarehouse	Id warehouse to use for stock change.
695
	 *	@return	int						<0 if KO, >0 if OK
696
	 */
697
	public function cancel($idwarehouse = -1)
698
	{
699
		global $conf,$user,$langs;
700
701
		$error=0;
702
703
		$this->db->begin();
704
705
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
706
		$sql.= " SET fk_statut = ".self::STATUS_CANCELED;
707
		$sql.= " WHERE rowid = ".$this->id;
708
		$sql.= " AND fk_statut = ".self::STATUS_VALIDATED;
709
710
		dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
711
		if ($this->db->query($sql))
712
		{
713
			// If stock is decremented on validate order, we must reincrement it
714
			if (! empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
715
			{
716
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
717
				$langs->load("agenda");
718
719
				$num=count($this->lines);
720
				for ($i = 0; $i < $num; $i++)
721
				{
722
					if ($this->lines[$i]->fk_product > 0)
723
					{
724
						$mouvP = new MouvementStock($this->db);
725
						// We increment stock of product (and sub-products)
726
						$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
727
						if ($result < 0)
728
						{
729
							$error++;
730
							$this->error=$mouvP->error;
731
							break;
732
						}
733
					}
734
				}
735
			}
736
737
			if (! $error)
738
			{
739
				// Call trigger
740
				$result=$this->call_trigger('ORDER_CANCEL', $user);
741
				if ($result < 0) $error++;
742
				// End call triggers
743
			}
744
745
			if (! $error)
746
			{
747
				$this->statut=self::STATUS_CANCELED;
748
				$this->db->commit();
749
				return 1;
750
			}
751
			else
752
			{
753
				foreach($this->errors as $errmsg)
754
				{
755
					dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
756
					$this->error.=($this->error?', '.$errmsg:$errmsg);
757
				}
758
				$this->db->rollback();
759
				return -1*$error;
760
			}
761
		}
762
		else
763
		{
764
			$this->error=$this->db->error();
765
			$this->db->rollback();
766
			return -1;
767
		}
768
	}
769
770
	/**
771
	 *	Create order
772
	 *	Note that this->ref can be set or empty. If empty, we will use "(PROV)"
773
	 *
774
	 *	@param		User	$user 		Objet user that make creation
775
	 *	@param		int	    $notrigger	Disable all triggers
776
	 *	@return 	int			        <0 if KO, >0 if OK
777
	 */
778
	public function create($user, $notrigger = 0)
779
	{
780
		global $conf,$langs;
781
		$error=0;
782
783
		// Clean parameters
784
		$this->brouillon = 1;		// set command as draft
785
786
		// $date_commande is deprecated
787
		$date = ($this->date_commande ? $this->date_commande : $this->date);
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande has been deprecated. ( Ignorable by Annotation )

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

787
		$date = ($this->date_commande ? /** @scrutinizer ignore-deprecated */ $this->date_commande : $this->date);
Loading history...
788
789
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
790
		if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
791
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
792
		if (empty($this->fk_multicurrency))
793
		{
794
			$this->multicurrency_code = $conf->currency;
795
			$this->fk_multicurrency = 0;
796
			$this->multicurrency_tx = 1;
797
		}
798
799
		dol_syslog(get_class($this)."::create user=".$user->id);
800
801
		// Check parameters
802
		if (! empty($this->ref))	// We check that ref is not already used
803
		{
804
			$result=self::isExistingObject($this->element, 0, $this->ref);	// Check ref is not yet used
805
			if ($result > 0)
806
			{
807
				$this->error='ErrorRefAlreadyExists';
808
				dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
809
				$this->db->rollback();
810
				return -1;
811
			}
812
		}
813
814
		$soc = new Societe($this->db);
815
		$result=$soc->fetch($this->socid);
816
		if ($result < 0)
817
		{
818
			$this->error="Failed to fetch company";
819
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
820
			return -2;
821
		}
822
		if (! empty($conf->global->COMMANDE_REQUIRE_SOURCE) && $this->source < 0)
823
		{
824
			$this->error=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
825
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
826
			return -1;
827
		}
828
829
		$now=dol_now();
830
831
		$this->db->begin();
832
833
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
834
		$sql.= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client, ref_int";
835
		$sql.= ", model_pdf, fk_cond_reglement, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
836
		$sql.= ", fk_shipping_method";
837
		$sql.= ", fk_warehouse";
838
		$sql.= ", remise_absolue, remise_percent";
839
		$sql.= ", fk_incoterms, location_incoterms";
840
		$sql.= ", entity, module_source, pos_source";
841
		$sql.= ", fk_multicurrency";
842
		$sql.= ", multicurrency_code";
843
		$sql.= ", multicurrency_tx";
844
		$sql.= ")";
845
		$sql.= " VALUES ('(PROV)', ".$this->socid.", '".$this->db->idate($now)."', ".$user->id;
846
		$sql.= ", ".($this->fk_project>0?$this->fk_project:"null");
847
		$sql.= ", '".$this->db->idate($date)."'";
848
		$sql.= ", ".($this->source>=0 && $this->source != '' ?$this->db->escape($this->source):'null');
849
		$sql.= ", '".$this->db->escape($this->note_private)."'";
850
		$sql.= ", '".$this->db->escape($this->note_public)."'";
851
		$sql.= ", ".($this->ref_ext?"'".$this->db->escape($this->ref_ext)."'":"null");
852
		$sql.= ", ".($this->ref_client?"'".$this->db->escape($this->ref_client)."'":"null");
853
		$sql.= ", ".($this->ref_int?"'".$this->db->escape($this->ref_int)."'":"null");
854
		$sql.= ", '".$this->db->escape($this->modelpdf)."'";
855
		$sql.= ", ".($this->cond_reglement_id>0?$this->cond_reglement_id:"null");
856
		$sql.= ", ".($this->mode_reglement_id>0?$this->mode_reglement_id:"null");
857
		$sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
858
		$sql.= ", ".($this->availability_id>0?$this->availability_id:"null");
859
		$sql.= ", ".($this->demand_reason_id>0?$this->demand_reason_id:"null");
860
		$sql.= ", ".($this->date_livraison?"'".$this->db->idate($this->date_livraison)."'":"null");
861
		$sql.= ", ".($this->fk_delivery_address>0?$this->fk_delivery_address:'NULL');
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

861
		$sql.= ", ".(/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address>0?$this->fk_delivery_address:'NULL');
Loading history...
862
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
863
		$sql.= ", ".($this->warehouse_id>0?$this->warehouse_id:'NULL');
864
		$sql.= ", ".($this->remise_absolue>0?$this->db->escape($this->remise_absolue):'NULL');
865
		$sql.= ", ".($this->remise_percent>0?$this->db->escape($this->remise_percent):0);
866
		$sql.= ", ".(int) $this->fk_incoterms;
867
		$sql.= ", '".$this->db->escape($this->location_incoterms)."'";
868
		$sql.= ", ".$conf->entity;
869
        $sql.= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
870
		$sql.= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
871
		$sql.= ", ".(int) $this->fk_multicurrency;
872
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
873
		$sql.= ", ".(double) $this->multicurrency_tx;
874
		$sql.= ")";
875
876
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
877
		$resql=$this->db->query($sql);
878
		if ($resql)
879
		{
880
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
881
882
			if ($this->id)
883
			{
884
				$fk_parent_line=0;
885
				$num=count($this->lines);
886
887
				/*
888
				 *  Insert products details into db
889
				 */
890
				for ($i=0;$i<$num;$i++)
891
				{
892
					$line = $this->lines[$i];
893
894
					// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
895
					//if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
896
					if (! is_object($line)) $line = (object) $line;
897
898
					// Reset fk_parent_line for no child products and special product
899
					if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
900
						$fk_parent_line = 0;
901
					}
902
903
					// Complete vat rate with code
904
					$vatrate = $line->tva_tx;
905
					if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
906
907
                    $result = $this->addline(
908
						$line->desc,
909
						$line->subprice,
910
						$line->qty,
911
						$vatrate,
912
						$line->localtax1_tx,
913
						$line->localtax2_tx,
914
						$line->fk_product,
915
						$line->remise_percent,
916
						$line->info_bits,
917
						$line->fk_remise_except,
918
						'HT',
919
						0,
920
						$line->date_start,
921
						$line->date_end,
922
						$line->product_type,
923
						$line->rang,
924
						$line->special_code,
925
						$fk_parent_line,
926
						$line->fk_fournprice,
927
						$line->pa_ht,
928
						$line->label,
929
						$line->array_options,
930
						$line->fk_unit,
931
						$this->element,
932
						$line->id
933
					);
934
					if ($result < 0)
935
					{
936
						if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER)
937
						{
938
							$this->error=$this->db->lasterror();
939
							dol_print_error($this->db);
940
						}
941
						$this->db->rollback();
942
						return -1;
943
					}
944
					// Defined the new fk_parent_line
945
					if ($result > 0 && $line->product_type == 9) {
946
						$fk_parent_line = $result;
947
					}
948
				}
949
950
				// update ref
951
				$initialref='(PROV'.$this->id.')';
952
				if (! empty($this->ref)) $initialref=$this->ref;
953
954
				$sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".$this->id;
955
				if ($this->db->query($sql))
956
				{
957
					if ($this->id)
958
					{
959
						$this->ref = $initialref;
960
961
						if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
962
						{
963
							$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
964
						}
965
966
						// Add object linked
967
						if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
968
						{
969
							foreach($this->linked_objects as $origin => $tmp_origin_id)
970
							{
971
								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, ...))
972
								{
973
									foreach($tmp_origin_id as $origin_id)
974
									{
975
										$ret = $this->add_object_linked($origin, $origin_id);
976
										if (! $ret)
977
										{
978
											$this->error=$this->db->lasterror();
979
											$error++;
980
										}
981
									}
982
								}
983
								else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
984
								{
985
									$origin_id = $tmp_origin_id;
986
									$ret = $this->add_object_linked($origin, $origin_id);
987
									if (! $ret)
988
									{
989
										$this->error=$this->db->lasterror();
990
										$error++;
991
									}
992
								}
993
							}
994
						}
995
996
						if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id))   // Get contact from origin object
997
						{
998
							$originforcontact = $this->origin;
999
							$originidforcontact = $this->origin_id;
1000
							if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
1001
							{
1002
								require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
1003
								$exp = new Expedition($this->db);
1004
								$exp->fetch($this->origin_id);
1005
								$exp->fetchObjectLinked();
1006
								if (count($exp->linkedObjectsIds['commande']) > 0)
1007
								{
1008
									foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
1009
									{
1010
										$originforcontact = 'commande';
1011
										if (is_object($value)) $originidforcontact = $value->id;
1012
										else $originidforcontact = $value;
1013
										break; // We take first one
1014
									}
1015
								}
1016
							}
1017
1018
							$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";
1019
							$sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
1020
1021
							$resqlcontact = $this->db->query($sqlcontact);
1022
							if ($resqlcontact)
1023
							{
1024
								while($objcontact = $this->db->fetch_object($resqlcontact))
1025
								{
1026
									//print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1027
									$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
1028
								}
1029
							}
1030
							else dol_print_error($resqlcontact);
1031
						}
1032
					}
1033
1034
					if (! $error)
1035
					{
1036
						$result=$this->insertExtraFields();
1037
						if ($result < 0) $error++;
1038
					}
1039
1040
					if (! $error && ! $notrigger)
1041
					{
1042
						// Call trigger
1043
						$result=$this->call_trigger('ORDER_CREATE', $user);
1044
						if ($result < 0) $error++;
1045
						// End call triggers
1046
					}
1047
1048
					if (! $error)
1049
					{
1050
						$this->db->commit();
1051
						return $this->id;
1052
					}
1053
					else
1054
					{
1055
						$this->db->rollback();
1056
						return -1*$error;
1057
					}
1058
				}
1059
				else
1060
				{
1061
					$this->error=$this->db->lasterror();
1062
					$this->db->rollback();
1063
					return -1;
1064
				}
1065
			}
1066
		}
1067
		else
1068
		{
1069
			dol_print_error($this->db);
1070
			$this->db->rollback();
1071
			return -1;
1072
		}
1073
	}
1074
1075
1076
	/**
1077
	 *	Load an object from its id and create a new one in database
1078
	 *
1079
	 *  @param	    User	$user		User making the clone
1080
	 *	@param		int		$socid		Id of thirdparty
1081
	 *	@return		int					New id of clone
1082
	 */
1083
	public function createFromClone(User $user, $socid = 0)
1084
	{
1085
		global $conf, $user,$hookmanager;
1086
1087
		$error=0;
1088
1089
		$this->db->begin();
1090
1091
		// get lines so they will be clone
1092
		foreach($this->lines as $line)
1093
			$line->fetch_optionals();
1094
1095
		// Load source object
1096
		$objFrom = clone $this;
1097
1098
		// Change socid if needed
1099
		if (! empty($socid) && $socid != $this->socid)
1100
		{
1101
			$objsoc = new Societe($this->db);
1102
1103
			if ($objsoc->fetch($socid)>0)
1104
			{
1105
				$this->socid 				= $objsoc->id;
1106
				$this->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1107
				$this->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1108
				$this->fk_project			= 0;
1109
				$this->fk_delivery_address	= 0;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

1109
				/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address	= 0;
Loading history...
1110
			}
1111
1112
			// TODO Change product price if multi-prices
1113
		}
1114
1115
		$this->id=0;
1116
		$this->ref = '';
1117
		$this->statut=self::STATUS_DRAFT;
1118
1119
		// Clear fields
1120
		$this->user_author_id     = $user->id;
1121
		$this->user_valid         = '';
1122
		$this->date				  = dol_now();
1123
		$this->date_commande	  = dol_now();
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande has been deprecated. ( Ignorable by Annotation )

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

1123
		/** @scrutinizer ignore-deprecated */ $this->date_commande	  = dol_now();
Loading history...
1124
		$this->date_creation      = '';
1125
		$this->date_validation    = '';
1126
		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $this->ref_client = '';
1127
1128
		// Create clone
1129
		$this->context['createfromclone'] = 'createfromclone';
1130
		$result=$this->create($user);
1131
		if ($result < 0) $error++;
1132
1133
		if (! $error)
1134
		{
1135
			// copy internal contacts
1136
			if ($this->copy_linked_contact($objFrom, 'internal') < 0)
1137
			{
1138
				$error++;
1139
			}
1140
		}
1141
1142
		if (! $error)
1143
		{
1144
			// copy external contacts if same company
1145
			if ($this->socid == $objFrom->socid)
1146
			{
1147
				if ($this->copy_linked_contact($objFrom, 'external') < 0)
1148
					$error++;
1149
			}
1150
		}
1151
1152
		if (! $error)
1153
		{
1154
			// Hook of thirdparty module
1155
			if (is_object($hookmanager))
1156
			{
1157
				$parameters=array('objFrom'=>$objFrom);
1158
				$action='';
1159
				$reshook=$hookmanager->executeHooks('createFrom', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
1160
				if ($reshook < 0) $error++;
1161
			}
1162
		}
1163
1164
		unset($this->context['createfromclone']);
1165
1166
		// End
1167
		if (! $error)
1168
		{
1169
			$this->db->commit();
1170
			return $this->id;
1171
		}
1172
		else
1173
		{
1174
			$this->db->rollback();
1175
			return -1;
1176
		}
1177
	}
1178
1179
1180
	/**
1181
	 *  Load an object from a proposal and create a new order into database
1182
	 *
1183
	 *  @param      Object			$object 	        Object source
1184
	 *  @param		User			$user				User making creation
1185
	 *  @return     int             					<0 if KO, 0 if nothing done, 1 if OK
1186
	 */
1187
	public function createFromProposal($object, User $user)
1188
	{
1189
		global $conf, $hookmanager;
1190
1191
		dol_include_once('/core/class/extrafields.class.php');
1192
1193
		$error=0;
1194
1195
1196
		$this->date_commande = dol_now();
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande has been deprecated. ( Ignorable by Annotation )

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

1196
		/** @scrutinizer ignore-deprecated */ $this->date_commande = dol_now();
Loading history...
1197
		$this->source = 0;
1198
1199
		$num=count($object->lines);
1200
		for ($i = 0; $i < $num; $i++)
1201
		{
1202
			$line = new OrderLine($this->db);
1203
1204
			$line->libelle           = $object->lines[$i]->libelle;
1 ignored issue
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

1204
			/** @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...
1205
			$line->label             = $object->lines[$i]->label;
1206
			$line->desc              = $object->lines[$i]->desc;
1207
			$line->price             = $object->lines[$i]->price;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

1207
			/** @scrutinizer ignore-deprecated */ $line->price             = $object->lines[$i]->price;
Loading history...
1208
			$line->subprice          = $object->lines[$i]->subprice;
1209
			$line->vat_src_code      = $object->lines[$i]->vat_src_code;
1210
			$line->tva_tx            = $object->lines[$i]->tva_tx;
1211
			$line->localtax1_tx      = $object->lines[$i]->localtax1_tx;
1212
			$line->localtax2_tx      = $object->lines[$i]->localtax2_tx;
1213
			$line->qty               = $object->lines[$i]->qty;
1214
			$line->fk_remise_except  = $object->lines[$i]->fk_remise_except;
1215
			$line->remise_percent    = $object->lines[$i]->remise_percent;
1216
			$line->fk_product        = $object->lines[$i]->fk_product;
1217
			$line->info_bits         = $object->lines[$i]->info_bits;
1218
			$line->product_type      = $object->lines[$i]->product_type;
1219
			$line->rang              = $object->lines[$i]->rang;
1220
			$line->special_code      = $object->lines[$i]->special_code;
1221
			$line->fk_parent_line    = $object->lines[$i]->fk_parent_line;
1222
			$line->fk_unit			 = $object->lines[$i]->fk_unit;
1223
1224
			$line->date_start      	= $object->lines[$i]->date_start;
1225
			$line->date_end    		= $object->lines[$i]->date_end;
1226
1227
			$line->fk_fournprice	= $object->lines[$i]->fk_fournprice;
1228
			$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);
1229
			$line->pa_ht			= $marginInfos[0];
1230
			$line->marge_tx			= $marginInfos[1];
1231
			$line->marque_tx		= $marginInfos[2];
1232
1233
			// get extrafields from original line
1234
			$object->lines[$i]->fetch_optionals();
1235
			foreach($object->lines[$i]->array_options as $options_key => $value)
1236
				$line->array_options[$options_key] = $value;
1237
1238
				$this->lines[$i] = $line;
1239
		}
1240
1241
		$this->socid                = $object->socid;
1242
		$this->fk_project           = $object->fk_project;
1243
		$this->cond_reglement_id    = $object->cond_reglement_id;
1244
		$this->mode_reglement_id    = $object->mode_reglement_id;
1245
		$this->fk_account           = $object->fk_account;
1246
		$this->availability_id      = $object->availability_id;
1247
		$this->demand_reason_id     = $object->demand_reason_id;
1248
		$this->date_livraison       = $object->date_livraison;
1249
		$this->shipping_method_id   = $object->shipping_method_id;
1250
		$this->warehouse_id         = $object->warehouse_id;
1251
		$this->fk_delivery_address  = $object->fk_delivery_address;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

1251
		/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address  = $object->fk_delivery_address;
Loading history...
1252
		$this->contact_id           = $object->contactid;
1253
		$this->ref_client           = $object->ref_client;
1254
1255
		if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN))
1256
		{
1257
            $this->note_private         = $object->note_private;
1258
            $this->note_public          = $object->note_public;
1259
		}
1260
1261
		$this->origin				= $object->element;
1262
		$this->origin_id			= $object->id;
1263
1264
		// get extrafields from original line
1265
		$object->fetch_optionals($object->id);
1266
1267
		$e = new ExtraFields($this->db);
1268
		$element_extrafields = $e->fetch_name_optionals_label($this->element);
1269
1270
		foreach($object->array_options as $options_key => $value) {
1271
			if(array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)){
1272
				$this->array_options[$options_key] = $value;
1273
			}
1274
		}
1275
		// Possibility to add external linked objects with hooks
1276
		$this->linked_objects[$this->origin] = $this->origin_id;
1277
		if (is_array($object->other_linked_objects) && ! empty($object->other_linked_objects))
1278
		{
1279
			$this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1280
		}
1281
1282
		$ret = $this->create($user);
1283
1284
		if ($ret > 0)
1285
		{
1286
			// Actions hooked (by external module)
1287
			$hookmanager->initHooks(array('orderdao'));
1288
1289
			$parameters=array('objFrom'=>$object);
1290
			$action='';
1291
			$reshook=$hookmanager->executeHooks('createFrom', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
1292
			if ($reshook < 0) $error++;
1293
1294
			if (! $error)
1295
			{
1296
				// Ne pas passer par la commande provisoire
1297
				if ($conf->global->COMMANDE_VALID_AFTER_CLOSE_PROPAL == 1)
1298
				{
1299
					$this->fetch($ret);
1300
					$this->valid($user);
1301
				}
1302
				return $ret;
1303
			}
1304
			else return -1;
1305
		}
1306
		else return -1;
1307
	}
1308
1309
1310
	/**
1311
	 *	Add an order line into database (linked to product/service or not)
1312
	 *
1313
	 *	@param      string			$desc            	Description of line
1314
	 *	@param      float			$pu_ht    	        Unit price (without tax)
1315
	 *	@param      float			$qty             	Quantite
1316
	 * 	@param    	float			$txtva           	Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
1317
	 * 	@param		float			$txlocaltax1		Local tax 1 rate (deprecated, use instead txtva with code inside)
1318
	 * 	@param		float			$txlocaltax2		Local tax 2 rate (deprecated, use instead txtva with code inside)
1319
	 *	@param      int				$fk_product      	Id of product
1320
	 *	@param      float			$remise_percent  	Percentage discount of the line
1321
	 *	@param      int				$info_bits			Bits of type of lines
1322
	 *	@param      int				$fk_remise_except	Id remise
1323
	 *	@param      string			$price_base_type	HT or TTC
1324
	 *	@param      float			$pu_ttc    		    Prix unitaire TTC
1325
	 *	@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)
1326
	 *	@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)
1327
	 *	@param      int				$type				Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
1328
	 *	@param      int				$rang             	Position of line
1329
	 *	@param		int				$special_code		Special code (also used by externals modules!)
1330
	 *	@param		int				$fk_parent_line		Parent line
1331
	 *  @param		int				$fk_fournprice		Id supplier price
1332
	 *  @param		int				$pa_ht				Buying price (without tax)
1333
	 *  @param		string			$label				Label
1334
	 *  @param		array			$array_options		extrafields array. Example array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
1335
	 * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
1336
	 * 	@param		string		    $origin				'order', ...
1337
	 *  @param		int			    $origin_id			Id of origin object
1338
	 * 	@param		double			$pu_ht_devise		Unit price in currency
1339
	 *	@return     int             					>0 if OK, <0 if KO
1340
	 *
1341
	 *	@see        add_product()
1342
	 *
1343
	 *	Les parametres sont deja cense etre juste et avec valeurs finales a l'appel
1344
	 *	de cette methode. Aussi, pour le taux tva, il doit deja avoir ete defini
1345
	 *	par l'appelant par la methode get_default_tva(societe_vendeuse,societe_acheteuse,produit)
1346
	 *	et le desc doit deja avoir la bonne valeur (a l'appelant de gerer le multilangue)
1347
	 */
1348
	public 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)
1349
	{
1350
		global $mysoc, $conf, $langs, $user;
1351
1352
		$logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1353
		$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";
1354
		$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";
1355
		dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1356
1357
		if ($this->statut == self::STATUS_DRAFT)
1358
		{
1359
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1360
1361
			// Clean parameters
1362
			if (empty($remise_percent)) $remise_percent=0;
1363
			if (empty($qty)) $qty=0;
1364
			if (empty($info_bits)) $info_bits=0;
1365
			if (empty($rang)) $rang=0;
1366
			if (empty($txtva)) $txtva=0;
1367
			if (empty($txlocaltax1)) $txlocaltax1=0;
1368
			if (empty($txlocaltax2)) $txlocaltax2=0;
1369
			if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
1370
			if (empty($this->fk_multicurrency)) $this->fk_multicurrency=0;
1371
1372
			$remise_percent=price2num($remise_percent);
1373
			$qty=price2num($qty);
1374
			$pu_ht=price2num($pu_ht);
1375
			$pu_ht_devise=price2num($pu_ht_devise);
1376
			$pu_ttc=price2num($pu_ttc);
1377
			$pa_ht=price2num($pa_ht);
1378
			if (!preg_match('/\((.*)\)/', $txtva)) {
1379
				$txtva = price2num($txtva);               // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1380
			}
1381
			$txlocaltax1 = price2num($txlocaltax1);
1382
			$txlocaltax2 = price2num($txlocaltax2);
1383
			if ($price_base_type=='HT')
1384
			{
1385
				$pu=$pu_ht;
1386
			}
1387
			else
1388
			{
1389
				$pu=$pu_ttc;
1390
			}
1391
			$label=trim($label);
1392
			$desc=trim($desc);
1393
1394
			// Check parameters
1395
			if ($type < 0) return -1;
1396
1397
			if ($date_start && $date_end && $date_start > $date_end) {
1398
				$langs->load("errors");
1399
				$this->error=$langs->trans('ErrorStartDateGreaterEnd');
1400
				return -1;
1401
			}
1402
1403
            $this->db->begin();
1404
1405
			$product_type=$type;
1406
			if (!empty($fk_product))
1407
			{
1408
				$product=new Product($this->db);
1409
				$result=$product->fetch($fk_product);
1410
				$product_type=$product->type;
1411
1412
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
1413
				{
1414
					$langs->load("errors");
1415
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1416
					dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1417
					$this->db->rollback();
1418
					return self::STOCK_NOT_ENOUGH_FOR_ORDER;
1419
				}
1420
			}
1421
			// Calcul du total TTC et de la TVA pour la ligne a partir de
1422
			// qty, pu, remise_percent et txtva
1423
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1424
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1425
1426
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1427
1428
			// Clean vat code
1429
			$vat_src_code='';
1430
			if (preg_match('/\((.*)\)/', $txtva, $reg))
1431
			{
1432
				$vat_src_code = $reg[1];
1433
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
1434
			}
1435
1436
			$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);
1437
1438
			/*var_dump($txlocaltax1);
1439
			 var_dump($txlocaltax2);
1440
			 var_dump($localtaxes_type);
1441
			 var_dump($tabprice);
1442
			 var_dump($tabprice[9]);
1443
			 var_dump($tabprice[10]);
1444
			 exit;*/
1445
1446
			$total_ht  = $tabprice[0];
1447
			$total_tva = $tabprice[1];
1448
			$total_ttc = $tabprice[2];
1449
			$total_localtax1 = $tabprice[9];
1450
			$total_localtax2 = $tabprice[10];
1451
			$pu_ht = $tabprice[3];
1452
1453
			// MultiCurrency
1454
			$multicurrency_total_ht  = $tabprice[16];
1455
			$multicurrency_total_tva = $tabprice[17];
1456
			$multicurrency_total_ttc = $tabprice[18];
1457
			$pu_ht_devise = $tabprice[19];
1458
1459
			// Rang to use
1460
			$rangtouse = $rang;
1461
			if ($rangtouse == -1)
1462
			{
1463
				$rangmax = $this->line_max($fk_parent_line);
1464
				$rangtouse = $rangmax + 1;
1465
			}
1466
1467
			// TODO A virer
1468
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1469
			$price = $pu;
1470
			$remise = 0;
1471
			if ($remise_percent > 0)
1472
			{
1473
				$remise = round(($pu * $remise_percent / 100), 2);
1474
				$price = $pu - $remise;
1475
			}
1476
1477
			// Insert line
1478
			$this->line=new OrderLine($this->db);
1479
1480
			$this->line->context = $this->context;
1481
1482
			$this->line->fk_commande=$this->id;
1483
			$this->line->label=$label;
1484
			$this->line->desc=$desc;
1485
			$this->line->qty=$qty;
1 ignored issue
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...
1486
1487
			$this->line->vat_src_code=$vat_src_code;
1488
			$this->line->tva_tx=$txtva;
1 ignored issue
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...
1489
			$this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
1490
			$this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
1491
			$this->line->localtax1_type=$localtaxes_type[0];
1492
			$this->line->localtax2_type=$localtaxes_type[2];
1493
			$this->line->fk_product=$fk_product;
1494
			$this->line->product_type=$product_type;
1495
			$this->line->fk_remise_except=$fk_remise_except;
1496
			$this->line->remise_percent=$remise_percent;
1 ignored issue
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...
1497
			$this->line->subprice=$pu_ht;
1498
			$this->line->rang=$rangtouse;
1499
			$this->line->info_bits=$info_bits;
1500
			$this->line->total_ht=$total_ht;
1501
			$this->line->total_tva=$total_tva;
1502
			$this->line->total_localtax1=$total_localtax1;
1503
			$this->line->total_localtax2=$total_localtax2;
1504
			$this->line->total_ttc=$total_ttc;
1505
			$this->line->special_code=$special_code;
1506
			$this->line->origin=$origin;
1507
			$this->line->origin_id=$origin_id;
1508
			$this->line->fk_parent_line=$fk_parent_line;
1509
			$this->line->fk_unit=$fk_unit;
1510
1511
			$this->line->date_start=$date_start;
1512
			$this->line->date_end=$date_end;
1513
1514
			$this->line->fk_fournprice = $fk_fournprice;
1515
			$this->line->pa_ht = $pa_ht;
1 ignored issue
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...
1516
1517
			// Multicurrency
1518
			$this->line->fk_multicurrency			= $this->fk_multicurrency;
1519
			$this->line->multicurrency_code			= $this->multicurrency_code;
1520
			$this->line->multicurrency_subprice		= $pu_ht_devise;
1521
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
1522
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
1523
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
1524
1525
			// TODO Ne plus utiliser
1526
			$this->line->price=$price;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

1526
			/** @scrutinizer ignore-deprecated */ $this->line->price=$price;
Loading history...
1527
			$this->line->remise=$remise;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

1527
			/** @scrutinizer ignore-deprecated */ $this->line->remise=$remise;
Loading history...
1528
1529
			if (is_array($array_options) && count($array_options)>0) {
1530
				$this->line->array_options=$array_options;
1531
			}
1532
1533
			$result=$this->line->insert($user);
1534
			if ($result > 0)
1535
			{
1536
				// Reorder if child line
1537
				if (! empty($fk_parent_line)) $this->line_order(true, 'DESC');
1538
1539
				// Mise a jour informations denormalisees au niveau de la commande meme
1540
				$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.
1541
				if ($result > 0)
1542
				{
1543
					$this->db->commit();
1544
					return $this->line->rowid;
1545
				}
1546
				else
1547
				{
1548
					$this->db->rollback();
1549
					return -1;
1550
				}
1551
			}
1552
			else
1553
			{
1554
				$this->error=$this->line->error;
1555
				dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1556
				$this->db->rollback();
1557
				return -2;
1558
			}
1559
		}
1560
		else
1561
		{
1562
			dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1563
			return -3;
1564
		}
1565
	}
1566
1567
1568
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1569
	/**
1570
	 *	Add line into array
1571
	 *	$this->client must be loaded
1572
	 *
1573
	 *	@param  int     $idproduct          Product Id
1574
	 *	@param  float   $qty                Quantity
1575
	 *	@param  float   $remise_percent     Product discount relative
1576
	 * 	@param  int     $date_start         Start date of the line
1577
	 * 	@param  int     $date_end           End date of the line
1578
	 * 	@return void
1579
	 *
1580
	 *	TODO	Remplacer les appels a cette fonction par generation objet Ligne
1581
	 *			insere dans tableau $this->products
1582
	 */
1583
	public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1584
	{
1585
        // phpcs:enable
1586
		global $conf, $mysoc;
1587
1588
		if (! $qty) $qty = 1;
1589
1590
		if ($idproduct > 0)
1591
		{
1592
			$prod=new Product($this->db);
1593
			$prod->fetch($idproduct);
1594
1595
			$tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1596
			$tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1597
			if (empty($tva_tx)) $tva_npr=0;
1598
			$vat_src_code = '';     // May be defined into tva_tx
1599
1600
			$localtax1_tx=get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1601
			$localtax2_tx=get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1602
1603
			// multiprix
1604
			if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1605
				$price = $prod->multiprices[$this->thirdparty->price_level];
1606
			} else {
1607
				$price = $prod->price;
1608
			}
1609
1610
			$line=new OrderLine($this->db);
1611
1612
			$line->context = $this->context;
1613
1614
			$line->fk_product=$idproduct;
1615
			$line->desc=$prod->description;
1616
			$line->qty=$qty;
1617
			$line->subprice=$price;
1618
			$line->remise_percent=$remise_percent;
1619
			$line->vat_src_code=$vat_src_code;
1620
			$line->tva_tx=$tva_tx;
1 ignored issue
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...
1621
			$line->localtax1_tx=$localtax1_tx;
1622
			$line->localtax2_tx=$localtax2_tx;
1623
			$line->ref=$prod->ref;
1 ignored issue
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

1623
			/** @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...
1624
			$line->libelle=$prod->label;
1 ignored issue
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

1624
			/** @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...
1625
			$line->product_desc=$prod->description;
1626
			$line->fk_unit=$prod->fk_unit;
1627
1628
			// Save the start and end date of the line in the object
1629
			if ($date_start) { $line->date_start = $date_start; }
1630
			if ($date_end)   { $line->date_end = $date_end; }
1631
1632
			$this->lines[] = $line;
1633
1634
			/** POUR AJOUTER AUTOMATIQUEMENT LES SOUSPRODUITS a LA COMMANDE
1635
			 if (! empty($conf->global->PRODUIT_SOUSPRODUITS))
1636
			 {
1637
			 $prod = new Product($this->db);
1638
			 $prod->fetch($idproduct);
1639
			 $prod -> get_sousproduits_arbo();
1640
			 $prods_arbo = $prod->get_arbo_each_prod();
1641
			 if(count($prods_arbo) > 0)
1642
			 {
1643
			 foreach($prods_arbo as $key => $value)
1644
			 {
1645
			 // print "id : ".$value[1].' :qty: '.$value[0].'<br>';
1646
			 if(! in_array($value[1],$this->products))
1647
			 $this->add_product($value[1], $value[0]);
1648
1649
			 }
1650
			 }
1651
1652
			 }
1653
			 **/
1654
		}
1655
	}
1656
1657
1658
	/**
1659
	 *	Get object and lines from database
1660
	 *
1661
	 *	@param      int			$id       		Id of object to load
1662
	 * 	@param		string		$ref			Ref of object
1663
	 * 	@param		string		$ref_ext		External reference of object
1664
	 * 	@param		string		$ref_int		Internal reference of other object
1665
	 *	@return     int         				>0 if OK, <0 if KO, 0 if not found
1666
	 */
1667
	public function fetch($id, $ref = '', $ref_ext = '', $ref_int = '')
1668
	{
1669
1670
		// Check parameters
1671
		if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1;
1672
1673
		$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';
1674
		$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';
1675
		$sql.= ', c.fk_account';
1676
		$sql.= ', c.date_commande, c.date_valid, c.tms';
1677
		$sql.= ', c.date_livraison';
1678
		$sql.= ', c.fk_shipping_method';
1679
		$sql.= ', c.fk_warehouse';
1680
		$sql.= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1681
		$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';
1682
		$sql.= ', c.fk_incoterms, c.location_incoterms';
1683
		$sql.= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1684
		$sql.= ", c.module_source, c.pos_source";
1685
        $sql.= ", i.libelle as label_incoterms";
1686
		$sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1687
		$sql.= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1688
		$sql.= ', ca.code as availability_code, ca.label as availability_label';
1689
		$sql.= ', dr.code as demand_reason_code';
1690
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1691
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1692
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1693
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1694
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1695
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1696
1697
		if ($id) $sql.= " WHERE c.rowid=".$id;
1698
		else $sql.= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1699
1700
		if ($ref)     $sql.= " AND c.ref='".$this->db->escape($ref)."'";
1701
		if ($ref_ext) $sql.= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1702
		if ($ref_int) $sql.= " AND c.ref_int='".$this->db->escape($ref_int)."'";
1703
1704
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1705
		$result = $this->db->query($sql);
1706
		if ($result)
1707
		{
1708
			$obj = $this->db->fetch_object($result);
1709
			if ($obj)
1710
			{
1711
				$this->id					= $obj->rowid;
1712
				$this->entity				= $obj->entity;
1713
1714
				$this->ref					= $obj->ref;
1715
				$this->ref_client			= $obj->ref_client;
1716
				$this->ref_customer			= $obj->ref_client;
1717
				$this->ref_ext				= $obj->ref_ext;
1718
				$this->ref_int				= $obj->ref_int;
1719
				$this->socid				= $obj->fk_soc;
1720
				$this->statut				= $obj->fk_statut;
1721
				$this->user_author_id		= $obj->fk_user_author;
1722
				$this->user_valid           = $obj->fk_user_valid;
1723
				$this->total_ht				= $obj->total_ht;
1724
				$this->total_tva			= $obj->total_tva;
1725
				$this->total_localtax1		= $obj->total_localtax1;
1726
				$this->total_localtax2		= $obj->total_localtax2;
1727
				$this->total_ttc			= $obj->total_ttc;
1728
				$this->date					= $this->db->jdate($obj->date_commande);
1729
				$this->date_commande		= $this->db->jdate($obj->date_commande);
1730
				$this->date_creation		= $this->db->jdate($obj->date_creation);
1731
				$this->date_validation		= $this->db->jdate($obj->date_valid);
1732
				$this->date_modification	= $this->db->jdate($obj->tms);
1733
				$this->remise				= $obj->remise;
1734
				$this->remise_percent		= $obj->remise_percent;
1735
				$this->remise_absolue		= $obj->remise_absolue;
1736
				$this->source				= $obj->source;
1737
				$this->billed				= $obj->billed;
1738
				$this->note					= $obj->note_private;	// deprecated
1739
				$this->note_private			= $obj->note_private;
1740
				$this->note_public			= $obj->note_public;
1741
				$this->fk_project			= $obj->fk_project;
1742
				$this->modelpdf				= $obj->model_pdf;
1743
				$this->last_main_doc		= $obj->last_main_doc;
1744
				$this->mode_reglement_id	= $obj->fk_mode_reglement;
1745
				$this->mode_reglement_code	= $obj->mode_reglement_code;
1746
				$this->mode_reglement		= $obj->mode_reglement_libelle;
1747
				$this->cond_reglement_id	= $obj->fk_cond_reglement;
1748
				$this->cond_reglement_code	= $obj->cond_reglement_code;
1749
				$this->cond_reglement		= $obj->cond_reglement_libelle;
1750
				$this->cond_reglement_doc	= $obj->cond_reglement_libelle_doc;
1751
				$this->fk_account           = $obj->fk_account;
1752
				$this->availability_id		= $obj->fk_availability;
1753
				$this->availability_code	= $obj->availability_code;
1754
				$this->availability	    	= $obj->availability_label;
1755
				$this->demand_reason_id		= $obj->fk_input_reason;
1756
				$this->demand_reason_code	= $obj->demand_reason_code;
1757
				$this->date_livraison		= $this->db->jdate($obj->date_livraison);
1758
				$this->shipping_method_id   = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1759
				$this->warehouse_id         = ($obj->fk_warehouse>0)?$obj->fk_warehouse:null;
1760
				$this->fk_delivery_address	= $obj->fk_delivery_address;
1761
				$this->module_source        = $obj->module_source;
1762
				$this->pos_source           = $obj->pos_source;
1763
1764
				//Incoterms
1765
				$this->fk_incoterms         = $obj->fk_incoterms;
1766
				$this->location_incoterms   = $obj->location_incoterms;
1767
				$this->label_incoterms    = $obj->label_incoterms;
1768
1769
				// Multicurrency
1770
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1771
				$this->multicurrency_code 		= $obj->multicurrency_code;
1772
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
1773
				$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
1774
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1775
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1776
1777
				$this->extraparams			= (array) json_decode($obj->extraparams, true);
1778
1779
				$this->lines				= array();
1780
1781
				if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
1782
1783
				// Retreive all extrafield
1784
				// fetch optionals attributes and labels
1785
				$this->fetch_optionals();
1786
1787
				$this->db->free($result);
1788
1789
				/*
1790
				 * Lines
1791
				 */
1792
				$result=$this->fetch_lines();
1793
				if ($result < 0)
1794
				{
1795
					return -3;
1796
				}
1797
				return 1;
1798
			}
1799
			else
1800
			{
1801
				$this->error='Order with id '.$id.' not found sql='.$sql;
1802
				return 0;
1803
			}
1804
		}
1805
		else
1806
		{
1807
			$this->error=$this->db->error();
1808
			return -1;
1809
		}
1810
	}
1811
1812
1813
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1814
	/**
1815
	 *	Adding line of fixed discount in the order in DB
1816
	 *
1817
	 *	@param     int	$idremise			Id de la remise fixe
1818
	 *	@return    int          			>0 si ok, <0 si ko
1819
	 */
1820
	public function insert_discount($idremise)
1821
	{
1822
        // phpcs:enable
1823
		global $langs;
1824
1825
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1826
		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1827
1828
		$this->db->begin();
1829
1830
		$remise=new DiscountAbsolute($this->db);
1831
		$result=$remise->fetch($idremise);
1832
1833
		if ($result > 0)
1834
		{
1835
			if ($remise->fk_facture)	// Protection against multiple submission
1836
			{
1837
				$this->error=$langs->trans("ErrorDiscountAlreadyUsed");
1838
				$this->db->rollback();
1839
				return -5;
1840
			}
1841
1842
			$line = new OrderLine($this->db);
1843
1844
			$line->fk_commande=$this->id;
1845
			$line->fk_remise_except=$remise->id;
1846
			$line->desc=$remise->description;   	// Description ligne
1847
			$line->vat_src_code=$remise->vat_src_code;
0 ignored issues
show
Bug introduced by
The property vat_src_code does not seem to exist on DiscountAbsolute.
Loading history...
1848
			$line->tva_tx=$remise->tva_tx;
1849
			$line->subprice=-$remise->amount_ht;
1850
			$line->price=-$remise->amount_ht;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

1850
			/** @scrutinizer ignore-deprecated */ $line->price=-$remise->amount_ht;
Loading history...
1851
			$line->fk_product=0;					// Id produit predefini
1852
			$line->qty=1;
1853
			$line->remise=0;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

1853
			/** @scrutinizer ignore-deprecated */ $line->remise=0;
Loading history...
1854
			$line->remise_percent=0;
1855
			$line->rang=-1;
1856
			$line->info_bits=2;
1857
1858
			$line->total_ht  = -$remise->amount_ht;
1859
			$line->total_tva = -$remise->amount_tva;
1860
			$line->total_ttc = -$remise->amount_ttc;
1861
1862
			$result=$line->insert();
1863
			if ($result > 0)
1864
			{
1865
				$result=$this->update_price(1);
1866
				if ($result > 0)
1867
				{
1868
					$this->db->commit();
1869
					return 1;
1870
				}
1871
				else
1872
				{
1873
					$this->db->rollback();
1874
					return -1;
1875
				}
1876
			}
1877
			else
1878
			{
1879
				$this->error=$line->error;
1880
				$this->db->rollback();
1881
				return -2;
1882
			}
1883
		}
1884
		else
1885
		{
1886
			$this->db->rollback();
1887
			return -2;
1888
		}
1889
	}
1890
1891
1892
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1893
	/**
1894
	 *	Load array lines
1895
	 *
1896
	 *	@param		int		$only_product	Return only physical products
1897
	 *	@param		int		$loadalsotranslation	Return translation for products
1898
	 *	@return		int						<0 if KO, >0 if OK
1899
	 */
1900
	public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
1901
	{
1902
		global $langs, $conf;
1903
        // phpcs:enable
1904
		$this->lines=array();
1905
1906
		$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,';
1907
		$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,';
1908
		$sql.= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
1909
		$sql.= ' l.fk_unit,';
1910
		$sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
1911
		$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,';
1912
		$sql.= ' p.weight, p.weight_units, p.volume, p.volume_units';
1913
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
1914
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
1915
		$sql.= ' WHERE l.fk_commande = '.$this->id;
1916
		if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1917
		$sql .= ' ORDER BY l.rang, l.rowid';
1918
1919
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1920
		$result = $this->db->query($sql);
1921
		if ($result)
1922
		{
1923
			$num = $this->db->num_rows($result);
1924
1925
			$i = 0;
1926
			while ($i < $num)
1927
			{
1928
				$objp = $this->db->fetch_object($result);
1929
1930
				$line = new OrderLine($this->db);
1931
1932
				$line->rowid            = $objp->rowid;
1933
				$line->id               = $objp->rowid;
1934
				$line->fk_commande      = $objp->fk_commande;
1935
				$line->commande_id      = $objp->fk_commande;
1936
				$line->label            = $objp->custom_label;
1937
				$line->desc             = $objp->description;
1938
				$line->description      = $objp->description;		// Description line
1939
				$line->product_type     = $objp->product_type;
1940
				$line->qty              = $objp->qty;
1941
1942
				$line->vat_src_code     = $objp->vat_src_code;
1943
				$line->tva_tx           = $objp->tva_tx;
1944
				$line->localtax1_tx     = $objp->localtax1_tx;
1945
				$line->localtax2_tx     = $objp->localtax2_tx;
1946
				$line->localtax1_type	= $objp->localtax1_type;
1947
				$line->localtax2_type	= $objp->localtax2_type;
1948
				$line->total_ht         = $objp->total_ht;
1949
				$line->total_ttc        = $objp->total_ttc;
1950
				$line->total_tva        = $objp->total_tva;
1951
				$line->total_localtax1  = $objp->total_localtax1;
1952
				$line->total_localtax2  = $objp->total_localtax2;
1953
				$line->subprice         = $objp->subprice;
1954
				$line->fk_remise_except = $objp->fk_remise_except;
1955
				$line->remise_percent   = $objp->remise_percent;
1956
				$line->price            = $objp->price;
1957
				$line->fk_product       = $objp->fk_product;
1958
				$line->fk_fournprice 	= $objp->fk_fournprice;
1959
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1960
				$line->pa_ht 			= $marginInfos[0];
1961
				$line->marge_tx			= $marginInfos[1];
1962
				$line->marque_tx		= $marginInfos[2];
1963
				$line->rang             = $objp->rang;
1964
				$line->info_bits        = $objp->info_bits;
1965
				$line->special_code		= $objp->special_code;
1966
				$line->fk_parent_line	= $objp->fk_parent_line;
1967
1968
				$line->ref				= $objp->product_ref;
1969
				$line->product_ref		= $objp->product_ref;
1970
				$line->libelle			= $objp->product_label;
1971
				$line->product_label	= $objp->product_label;
1972
				$line->product_desc     = $objp->product_desc;
1973
				$line->product_tobatch  = $objp->product_tobatch;
1974
				$line->fk_product_type  = $objp->fk_product_type;	// Produit ou service
1975
				$line->fk_unit          = $objp->fk_unit;
1976
1977
				$line->weight           = $objp->weight;
1978
				$line->weight_units     = $objp->weight_units;
1979
				$line->volume           = $objp->volume;
1980
				$line->volume_units     = $objp->volume_units;
1981
1982
				$line->date_start       = $this->db->jdate($objp->date_start);
1983
				$line->date_end         = $this->db->jdate($objp->date_end);
1984
1985
				// Multicurrency
1986
				$line->fk_multicurrency 		= $objp->fk_multicurrency;
1987
				$line->multicurrency_code 		= $objp->multicurrency_code;
1988
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
1989
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
1990
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
1991
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
1992
1993
				$line->fetch_optionals();
1994
1995
				// multilangs
1996
        		if (! empty($conf->global->MAIN_MULTILANGS) && ! empty($objp->fk_product) && ! empty($loadalsotranslation)) {
1997
        		$line = new Product($this->db);
1998
        		$line->fetch($objp->fk_product);
1999
        		$line->getMultiLangs();
2000
        		}
2001
2002
                $this->lines[$i] = $line;
2003
2004
				$i++;
2005
			}
2006
2007
			$this->db->free($result);
2008
2009
			return 1;
2010
		}
2011
		else
2012
		{
2013
			$this->error=$this->db->error();
2014
			return -3;
2015
		}
2016
	}
2017
2018
2019
	/**
2020
	 *	Return number of line with type product.
2021
	 *
2022
	 *	@return		int		<0 if KO, Nbr of product lines if OK
2023
	 */
2024
	public function getNbOfProductsLines()
2025
	{
2026
		$nb=0;
2027
		foreach($this->lines as $line)
2028
		{
2029
			if ($line->product_type == 0) $nb++;
2030
		}
2031
		return $nb;
2032
	}
2033
2034
	/**
2035
	 *	Return number of line with type service.
2036
	 *
2037
	 *	@return		int		<0 if KO, Nbr of service lines if OK
2038
	 */
2039
	public function getNbOfServicesLines()
2040
	{
2041
		$nb=0;
2042
		foreach($this->lines as $line)
2043
		{
2044
			if ($line->product_type == 1) $nb++;
2045
		}
2046
		return $nb;
2047
	}
2048
2049
	/**
2050
	 *	Count number of shipments for this order
2051
	 *
2052
	 * 	@return     int                			<0 if KO, Nb of shipment found if OK
2053
	 */
2054
	public function getNbOfShipments()
2055
	{
2056
		$nb = 0;
2057
2058
		$sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2059
		$sql.= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2060
		$sql.= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2061
		$sql.= ' WHERE';
2062
		$sql.= ' ed.fk_origin_line = cd.rowid';
2063
		$sql.= ' AND cd.fk_commande =' .$this->id;
2064
		//print $sql;
2065
2066
		dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2067
		$resql = $this->db->query($sql);
2068
		if ($resql)
2069
		{
2070
			$obj = $this->db->fetch_object($resql);
2071
			if ($obj) $nb = $obj->nb;
2072
2073
			$this->db->free($resql);
2074
			return $nb;
2075
		}
2076
		else
2077
		{
2078
			$this->error=$this->db->lasterror();
2079
			return -1;
2080
		}
2081
	}
2082
2083
	/**
2084
	 *	Load array this->expeditions of lines of shipments with nb of products sent for each order line
2085
	 *  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
2086
	 *
2087
	 *	@param      int		$filtre_statut      Filter on shipment status
2088
	 * 	@return     int                			<0 if KO, Nb of lines found if OK
2089
	 */
2090
	public function loadExpeditions($filtre_statut = -1)
2091
	{
2092
		$this->expeditions = array();
2093
2094
		$sql = 'SELECT cd.rowid, cd.fk_product,';
2095
		$sql.= ' sum(ed.qty) as qty';
2096
		$sql.= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2097
		if ($filtre_statut >= 0) $sql.= ' '.MAIN_DB_PREFIX.'expedition as e,';
2098
		$sql.= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2099
		$sql.= ' WHERE';
2100
		if ($filtre_statut >= 0) $sql.= ' ed.fk_expedition = e.rowid AND';
2101
		$sql.= ' ed.fk_origin_line = cd.rowid';
2102
		$sql.= ' AND cd.fk_commande =' .$this->id;
2103
		if ($this->fk_product > 0) $sql.= ' AND cd.fk_product = '.$this->fk_product;
2104
		if ($filtre_statut >= 0) $sql.=' AND e.fk_statut >= '.$filtre_statut;
2105
		$sql.= ' GROUP BY cd.rowid, cd.fk_product';
2106
		//print $sql;
2107
2108
		dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2109
		$resql = $this->db->query($sql);
2110
		if ($resql)
2111
		{
2112
			$num = $this->db->num_rows($resql);
2113
			$i = 0;
2114
			while ($i < $num)
2115
			{
2116
				$obj = $this->db->fetch_object($resql);
2117
				$this->expeditions[$obj->rowid] = $obj->qty;
2118
				$i++;
2119
			}
2120
			$this->db->free($resql);
2121
			return $num;
2122
		}
2123
		else
2124
		{
2125
			$this->error=$this->db->lasterror();
2126
			return -1;
2127
		}
2128
	}
2129
2130
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2131
	/**
2132
	 * Returns a array with expeditions lines number
2133
	 *
2134
	 * @return	int		Nb of shipments
2135
	 *
2136
	 * TODO deprecate, move to Shipping class
2137
	 */
2138
	public function nb_expedition()
2139
	{
2140
        // phpcs:enable
2141
		$sql = 'SELECT count(*)';
2142
		$sql.= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2143
		$sql.= ', '.MAIN_DB_PREFIX.'element_element as el';
2144
		$sql.= ' WHERE el.fk_source = '.$this->id;
2145
		$sql.= " AND el.fk_target = e.rowid";
2146
		$sql.= " AND el.targettype = 'shipping'";
2147
2148
		$resql = $this->db->query($sql);
2149
		if ($resql)
2150
		{
2151
			$row = $this->db->fetch_row($resql);
2152
			return $row[0];
2153
		}
2154
		else dol_print_error($this->db);
2155
	}
2156
2157
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2158
	/**
2159
	 *	Return a array with the pending stock by product
2160
	 *
2161
	 *	@param      int		$filtre_statut      Filtre sur statut
2162
	 *	@return     int                 		0 si OK, <0 si KO
2163
	 *
2164
	 *	TODO		FONCTION NON FINIE A FINIR
2165
	 */
2166
	public function stock_array($filtre_statut = self::STATUS_CANCELED)
2167
	{
2168
        // phpcs:enable
2169
		$this->stocks = array();
2170
2171
		// Tableau des id de produit de la commande
2172
		$array_of_product=array();
2173
2174
		// Recherche total en stock pour chaque produit
2175
		// TODO $array_of_product est défini vide juste au dessus !!
2176
		if (count($array_of_product))
2177
		{
2178
			$sql = "SELECT fk_product, sum(ps.reel) as total";
2179
			$sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2180
			$sql.= " WHERE ps.fk_product IN (".join(',', $array_of_product).")";
2181
			$sql.= ' GROUP BY fk_product ';
2182
			$resql = $this->db->query($sql);
2183
			if ($resql)
2184
			{
2185
				$num = $this->db->num_rows($resql);
2186
				$i = 0;
2187
				while ($i < $num)
2188
				{
2189
					$obj = $this->db->fetch_object($resql);
2190
					$this->stocks[$obj->fk_product] = $obj->total;
2191
					$i++;
2192
				}
2193
				$this->db->free($resql);
2194
			}
2195
		}
2196
		return 0;
2197
	}
2198
2199
	/**
2200
	 *  Delete an order line
2201
	 *
2202
	 *	@param      User	$user		User object
2203
	 *  @param      int		$lineid		Id of line to delete
2204
	 *  @return     int        		 	>0 if OK, 0 if nothing to do, <0 if KO
2205
	 */
2206
	public function deleteline($user = null, $lineid = 0)
2207
	{
2208
		if ($this->statut == self::STATUS_DRAFT)
2209
		{
2210
			$this->db->begin();
2211
2212
			$sql = "SELECT fk_product, qty";
2213
			$sql.= " FROM ".MAIN_DB_PREFIX."commandedet";
2214
			$sql.= " WHERE rowid = ".$lineid;
2215
2216
			$result = $this->db->query($sql);
2217
			if ($result)
2218
			{
2219
				$obj = $this->db->fetch_object($result);
2220
2221
				if ($obj)
2222
				{
2223
					$product = new Product($this->db);
2224
					$product->id = $obj->fk_product;
2225
2226
					// Delete line
2227
					$line = new OrderLine($this->db);
2228
2229
					// For triggers
2230
					$line->fetch($lineid);
2231
2232
					if ($line->delete($user) > 0)
2233
					{
2234
						$result=$this->update_price(1);
2235
2236
						if ($result > 0)
2237
						{
2238
							$this->db->commit();
2239
							return 1;
2240
						}
2241
						else
2242
						{
2243
							$this->db->rollback();
2244
							$this->error=$this->db->lasterror();
2245
							return -1;
2246
						}
2247
					}
2248
					else
2249
					{
2250
						$this->db->rollback();
2251
						$this->error=$line->error;
2252
						return -1;
2253
					}
2254
				}
2255
				else
2256
				{
2257
					$this->db->rollback();
2258
					return 0;
2259
				}
2260
			}
2261
			else
2262
			{
2263
				$this->db->rollback();
2264
				$this->error=$this->db->lasterror();
2265
				return -1;
2266
			}
2267
		}
2268
		else
2269
		{
2270
			$this->error='ErrorDeleteLineNotAllowedByObjectStatus';
2271
			return -1;
2272
		}
2273
	}
2274
2275
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2276
	/**
2277
	 * 	Applique une remise relative
2278
	 *
2279
	 * 	@param     	User		$user		User qui positionne la remise
2280
	 * 	@param     	float		$remise		Discount (percent)
2281
	 * 	@param     	int			$notrigger	1=Does not execute triggers, 0= execute triggers
2282
	 *	@return		int 					<0 if KO, >0 if OK
2283
	 */
2284
	public function set_remise($user, $remise, $notrigger = 0)
2285
	{
2286
        // phpcs:enable
2287
		$remise=trim($remise)?trim($remise):0;
2288
2289
		if ($user->rights->commande->creer)
2290
		{
2291
			$error=0;
2292
2293
			$this->db->begin();
2294
2295
			$remise=price2num($remise);
2296
2297
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2298
			$sql.= ' SET remise_percent = '.$remise;
2299
			$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2300
2301
			dol_syslog(__METHOD__, LOG_DEBUG);
2302
			$resql=$this->db->query($sql);
2303
			if (!$resql)
2304
			{
2305
				$this->errors[]=$this->db->error();
2306
				$error++;
2307
			}
2308
2309
			if (! $error)
2310
			{
2311
				$this->oldcopy= clone $this;
2312
				$this->remise_percent = $remise;
2313
				$this->update_price(1);
2314
			}
2315
2316
			if (! $notrigger && empty($error))
2317
			{
2318
				// Call trigger
2319
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2320
				if ($result < 0) $error++;
2321
				// End call triggers
2322
			}
2323
2324
			if (! $error)
2325
			{
2326
				$this->db->commit();
2327
				return 1;
2328
			}
2329
			else
2330
			{
2331
				foreach($this->errors as $errmsg)
2332
				{
2333
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2334
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2335
				}
2336
				$this->db->rollback();
2337
				return -1*$error;
2338
			}
2339
		}
2340
	}
2341
2342
2343
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2344
	/**
2345
	 * 		Applique une remise absolue
2346
	 *
2347
	 * 		@param     	User		$user 		User qui positionne la remise
2348
	 * 		@param     	float		$remise		Discount
2349
	 * 		@param     	int			$notrigger	1=Does not execute triggers, 0= execute triggers
2350
	 *		@return		int 					<0 if KO, >0 if OK
2351
	 */
2352
	public function set_remise_absolue($user, $remise, $notrigger = 0)
2353
	{
2354
        // phpcs:enable
2355
		$remise=trim($remise)?trim($remise):0;
2356
2357
		if ($user->rights->commande->creer)
2358
		{
2359
			$error=0;
2360
2361
			$this->db->begin();
2362
2363
			$remise=price2num($remise);
2364
2365
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2366
			$sql.= ' SET remise_absolue = '.$remise;
2367
			$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2368
2369
			dol_syslog(__METHOD__, LOG_DEBUG);
2370
			$resql=$this->db->query($sql);
2371
			if (!$resql)
2372
			{
2373
				$this->errors[]=$this->db->error();
2374
				$error++;
2375
			}
2376
2377
			if (! $error)
2378
			{
2379
				$this->oldcopy= clone $this;
2380
				$this->remise_absolue = $remise;
2381
				$this->update_price(1);
2382
			}
2383
2384
			if (! $notrigger && empty($error))
2385
			{
2386
				// Call trigger
2387
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2388
				if ($result < 0) $error++;
2389
				// End call triggers
2390
			}
2391
2392
			if (! $error)
2393
			{
2394
				$this->db->commit();
2395
				return 1;
2396
			}
2397
			else
2398
			{
2399
				foreach($this->errors as $errmsg)
2400
				{
2401
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2402
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2403
				}
2404
				$this->db->rollback();
2405
				return -1*$error;
2406
			}
2407
		}
2408
	}
2409
2410
2411
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2412
	/**
2413
	 *	Set the order date
2414
	 *
2415
	 *	@param      User	$user       Object user making change
2416
	 *	@param      int		$date		Date
2417
	 * 	@param     	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2418
	 *	@return     int         		<0 if KO, >0 if OK
2419
	 */
2420
	public function set_date($user, $date, $notrigger = 0)
2421
	{
2422
        // phpcs:enable
2423
		if ($user->rights->commande->creer)
2424
		{
2425
			$error=0;
2426
2427
			$this->db->begin();
2428
2429
			$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2430
			$sql.= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2431
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2432
2433
			dol_syslog(__METHOD__, LOG_DEBUG);
2434
			$resql=$this->db->query($sql);
2435
			if (!$resql)
2436
			{
2437
				$this->errors[]=$this->db->error();
2438
				$error++;
2439
			}
2440
2441
			if (! $error)
2442
			{
2443
				$this->oldcopy= clone $this;
2444
				$this->date = $date;
2445
			}
2446
2447
			if (! $notrigger && empty($error))
2448
			{
2449
				// Call trigger
2450
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2451
				if ($result < 0) $error++;
2452
				// End call triggers
2453
			}
2454
2455
			if (! $error)
2456
			{
2457
				$this->db->commit();
2458
				return 1;
2459
			}
2460
			else
2461
			{
2462
				foreach($this->errors as $errmsg)
2463
				{
2464
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2465
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2466
				}
2467
				$this->db->rollback();
2468
				return -1*$error;
2469
			}
2470
		}
2471
		else
2472
		{
2473
			return -2;
2474
		}
2475
	}
2476
2477
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2478
	/**
2479
	 *	Set the planned delivery date
2480
	 *
2481
	 *	@param      User	$user        		Objet utilisateur qui modifie
2482
	 *	@param      int		$date_livraison     Date de livraison
2483
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2484
	 *	@return     int         				<0 si ko, >0 si ok
2485
	 */
2486
	public function set_date_livraison($user, $date_livraison, $notrigger = 0)
2487
	{
2488
        // phpcs:enable
2489
		if ($user->rights->commande->creer)
2490
		{
2491
			$error=0;
2492
2493
			$this->db->begin();
2494
2495
			$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2496
			$sql.= " SET date_livraison = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
2497
			$sql.= " WHERE rowid = ".$this->id;
2498
2499
			dol_syslog(__METHOD__, LOG_DEBUG);
2500
			$resql=$this->db->query($sql);
2501
			if (!$resql)
2502
			{
2503
				$this->errors[]=$this->db->error();
2504
				$error++;
2505
			}
2506
2507
			if (! $error)
2508
			{
2509
				$this->oldcopy= clone $this;
2510
				$this->date_livraison = $date_livraison;
2511
			}
2512
2513
			if (! $notrigger && empty($error))
2514
			{
2515
				// Call trigger
2516
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2517
				if ($result < 0) $error++;
2518
				// End call triggers
2519
			}
2520
2521
			if (! $error)
2522
			{
2523
				$this->db->commit();
2524
				return 1;
2525
			}
2526
			else
2527
			{
2528
				foreach($this->errors as $errmsg)
2529
				{
2530
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2531
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2532
				}
2533
				$this->db->rollback();
2534
				return -1*$error;
2535
			}
2536
		}
2537
		else
2538
		{
2539
			return -2;
2540
		}
2541
	}
2542
2543
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2544
	/**
2545
	 *  Return list of orders (eventuelly filtered on a user) into an array
2546
	 *
2547
	 *  @param		int		$shortlist		0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2548
	 *  @param      int		$draft      	0=not draft, 1=draft
2549
	 *  @param      User	$excluser      	Objet user to exclude
2550
	 *  @param    	int		$socid			Id third pary
2551
	 *  @param    	int		$limit			For pagination
2552
	 *  @param    	int		$offset			For pagination
2553
	 *  @param    	string	$sortfield		Sort criteria
2554
	 *  @param    	string	$sortorder		Sort order
2555
	 *  @return     int             		-1 if KO, array with result if OK
2556
	 */
2557
	public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2558
	{
2559
        // phpcs:enable
2560
		global $user;
2561
2562
		$ga = array();
2563
2564
		$sql = "SELECT s.rowid, s.nom as name, s.client,";
2565
		$sql.= " c.rowid as cid, c.ref";
2566
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2567
		$sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2568
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2569
		$sql.= " WHERE c.entity IN (".getEntity('commande').")";
2570
		$sql.= " AND c.fk_soc = s.rowid";
2571
		if (! $user->rights->societe->client->voir && ! $socid) //restriction
2572
		{
2573
			$sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2574
		}
2575
		if ($socid) $sql.= " AND s.rowid = ".$socid;
2576
		if ($draft) $sql.= " AND c.fk_statut = ".self::STATUS_DRAFT;
2577
		if (is_object($excluser)) $sql.= " AND c.fk_user_author <> ".$excluser->id;
2578
		$sql.= $this->db->order($sortfield, $sortorder);
2579
		$sql.= $this->db->plimit($limit, $offset);
2580
2581
		$result=$this->db->query($sql);
2582
		if ($result)
2583
		{
2584
			$numc = $this->db->num_rows($result);
2585
			if ($numc)
2586
			{
2587
				$i = 0;
2588
				while ($i < $numc)
2589
				{
2590
					$obj = $this->db->fetch_object($result);
2591
2592
					if ($shortlist == 1)
2593
					{
2594
						$ga[$obj->cid] = $obj->ref;
2595
					}
2596
					elseif ($shortlist == 2)
2597
					{
2598
						$ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2599
					}
2600
					else
2601
					{
2602
						$ga[$i]['id']	= $obj->cid;
2603
						$ga[$i]['ref'] 	= $obj->ref;
2604
						$ga[$i]['name'] = $obj->name;
2605
					}
2606
					$i++;
2607
				}
2608
			}
2609
			return $ga;
2610
		}
2611
		else
2612
		{
2613
			dol_print_error($this->db);
2614
			return -1;
2615
		}
2616
	}
2617
2618
	/**
2619
	 *	Update delivery delay
2620
	 *
2621
	 *	@param      int		$availability_id	Id du nouveau mode
2622
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2623
	 *	@return     int         				>0 if OK, <0 if KO
2624
	 */
2625
	public function availability($availability_id, $notrigger = 0)
2626
	{
2627
		global $user;
2628
2629
		dol_syslog('Commande::availability('.$availability_id.')');
2630
		if ($this->statut >= self::STATUS_DRAFT)
2631
		{
2632
			$error=0;
2633
2634
			$this->db->begin();
2635
2636
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2637
			$sql .= ' SET fk_availability = '.$availability_id;
2638
			$sql .= ' WHERE rowid='.$this->id;
2639
2640
			dol_syslog(__METHOD__, LOG_DEBUG);
2641
			$resql=$this->db->query($sql);
2642
			if (!$resql)
2643
			{
2644
				$this->errors[]=$this->db->error();
2645
				$error++;
2646
			}
2647
2648
			if (! $error)
2649
			{
2650
				$this->oldcopy= clone $this;
2651
				$this->availability_id = $availability_id;
2652
			}
2653
2654
			if (! $notrigger && empty($error))
2655
			{
2656
				// Call trigger
2657
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2658
				if ($result < 0) $error++;
2659
				// End call triggers
2660
			}
2661
2662
			if (! $error)
2663
			{
2664
				$this->db->commit();
2665
				return 1;
2666
			}
2667
			else
2668
			{
2669
				foreach($this->errors as $errmsg)
2670
				{
2671
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2672
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2673
				}
2674
				$this->db->rollback();
2675
				return -1*$error;
2676
			}
2677
		}
2678
		else
2679
		{
2680
			$error_str='Command status do not meet requirement '.$this->statut;
2681
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2682
			$this->error=$error_str;
2683
			$this->errors[]= $this->error;
2684
			return -2;
2685
		}
2686
	}
2687
2688
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2689
	/**
2690
	 *	Update order demand_reason
2691
	 *
2692
	 *  @param      int		$demand_reason_id	Id of new demand
2693
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2694
	 *  @return     int        			 		>0 if ok, <0 if ko
2695
	 */
2696
	public function demand_reason($demand_reason_id, $notrigger = 0)
2697
	{
2698
        // phpcs:enable
2699
		global $user;
2700
2701
		dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2702
		if ($this->statut >= self::STATUS_DRAFT)
2703
		{
2704
			$error=0;
2705
2706
			$this->db->begin();
2707
2708
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2709
			$sql .= ' SET fk_input_reason = '.$demand_reason_id;
2710
			$sql .= ' WHERE rowid='.$this->id;
2711
2712
			dol_syslog(__METHOD__, LOG_DEBUG);
2713
			$resql=$this->db->query($sql);
2714
			if (!$resql)
2715
			{
2716
				$this->errors[]=$this->db->error();
2717
				$error++;
2718
			}
2719
2720
			if (! $error)
2721
			{
2722
				$this->oldcopy= clone $this;
2723
				$this->demand_reason_id = $demand_reason_id;
2724
			}
2725
2726
			if (! $notrigger && empty($error))
2727
			{
2728
				// Call trigger
2729
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2730
				if ($result < 0) $error++;
2731
				// End call triggers
2732
			}
2733
2734
			if (! $error)
2735
			{
2736
				$this->db->commit();
2737
				return 1;
2738
			}
2739
			else
2740
			{
2741
				foreach($this->errors as $errmsg)
2742
				{
2743
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2744
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2745
				}
2746
				$this->db->rollback();
2747
				return -1*$error;
2748
			}
2749
		}
2750
		else
2751
		{
2752
			$error_str='order status do not meet requirement '.$this->statut;
2753
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2754
			$this->error=$error_str;
2755
			$this->errors[]= $this->error;
2756
			return -2;
2757
		}
2758
	}
2759
2760
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2761
	/**
2762
	 *	Set customer ref
2763
	 *
2764
	 *	@param      User	$user           User that make change
2765
	 *	@param      string	$ref_client     Customer ref
2766
	 *  @param     	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2767
	 *	@return     int             		<0 if KO, >0 if OK
2768
	 */
2769
	public function set_ref_client($user, $ref_client, $notrigger = 0)
2770
	{
2771
        // phpcs:enable
2772
		if ($user->rights->commande->creer)
2773
		{
2774
			$error=0;
2775
2776
			$this->db->begin();
2777
2778
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2779
			$sql.= ' ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2780
			$sql.= ' WHERE rowid = '.$this->id;
2781
2782
			dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2783
			$resql=$this->db->query($sql);
2784
			if (!$resql)
2785
			{
2786
				$this->errors[]=$this->db->error();
2787
				$error++;
2788
			}
2789
2790
			if (! $error)
2791
			{
2792
				$this->oldcopy= clone $this;
2793
				$this->ref_client = $ref_client;
2794
			}
2795
2796
			if (! $notrigger && empty($error))
2797
			{
2798
				// Call trigger
2799
				$result=$this->call_trigger('ORDER_MODIFY', $user);
2800
				if ($result < 0) $error++;
2801
				// End call triggers
2802
			}
2803
			if (! $error)
2804
			{
2805
				$this->db->commit();
2806
				return 1;
2807
			}
2808
			else
2809
			{
2810
				foreach($this->errors as $errmsg)
2811
				{
2812
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2813
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2814
				}
2815
				$this->db->rollback();
2816
				return -1*$error;
2817
			}
2818
		}
2819
		else
2820
		{
2821
			return -1;
2822
		}
2823
	}
2824
2825
	/**
2826
	 * Classify the order as invoiced
2827
	 *
2828
	 * @param	User    $user       Object user making the change
2829
	 * @param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2830
	 * @return	int                 <0 if KO, >0 if OK
2831
	 */
2832
	public function classifyBilled(User $user, $notrigger = 0)
2833
	{
2834
		$error = 0;
2835
2836
		$this->db->begin();
2837
2838
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2839
		$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2840
2841
		dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
2842
		if ($this->db->query($sql))
2843
		{
2844
2845
			if (! $error)
2846
			{
2847
				$this->oldcopy= clone $this;
2848
				$this->billed=1;
2849
			}
2850
2851
			if (! $notrigger && empty($error))
2852
			{
2853
				// Call trigger
2854
				$result=$this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
2855
				if ($result < 0) $error++;
2856
				// End call triggers
2857
			}
2858
2859
			if (! $error)
2860
			{
2861
				$this->db->commit();
2862
				return 1;
2863
			}
2864
			else
2865
			{
2866
				foreach($this->errors as $errmsg)
2867
				{
2868
					dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
2869
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2870
				}
2871
				$this->db->rollback();
2872
				return -1*$error;
2873
			}
2874
		}
2875
		else
2876
		{
2877
			$this->error=$this->db->error();
2878
			$this->db->rollback();
2879
			return -1;
2880
		}
2881
	}
2882
2883
	/**
2884
	 * Classify the order as not invoiced
2885
	 *
2886
	 * @return     int     <0 if ko, >0 if ok
2887
	 */
2888
	public function classifyUnBilled()
2889
	{
2890
		global $conf, $user, $langs;
2891
		$error = 0;
2892
2893
		$this->db->begin();
2894
2895
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
2896
		$sql.= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2897
2898
		dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
2899
		if ($this->db->query($sql))
2900
		{
2901
			if (! $error)
2902
			{
2903
				$this->oldcopy= clone $this;
2904
				$this->billed=1;
2905
			}
2906
2907
			// Call trigger
2908
			$result=$this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
2909
			if ($result < 0) $error++;
2910
			// End call triggers
2911
2912
			if (! $error)
2913
			{
2914
				$this->billed=0;
2915
2916
				$this->db->commit();
2917
				return 1;
2918
			}
2919
			else
2920
			{
2921
				foreach($this->errors as $errmsg)
2922
				{
2923
					dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
2924
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2925
				}
2926
				$this->db->rollback();
2927
				return -1*$error;
2928
			}
2929
		}
2930
		else
2931
		{
2932
			$this->error=$this->db->error();
2933
			$this->db->rollback();
2934
			return -1;
2935
		}
2936
	}
2937
2938
2939
	/**
2940
	 *  Update a line in database
2941
	 *
2942
	 *  @param    	int				$rowid            	Id of line to update
2943
	 *  @param    	string			$desc             	Description of line
2944
	 *  @param    	float			$pu               	Unit price
2945
	 *  @param    	float			$qty              	Quantity
2946
	 *  @param    	float			$remise_percent   	Percent of discount
2947
	 *  @param    	float			$txtva           	Taux TVA
2948
	 * 	@param		float			$txlocaltax1		Local tax 1 rate
2949
	 *  @param		float			$txlocaltax2		Local tax 2 rate
2950
	 *  @param    	string			$price_base_type	HT or TTC
2951
	 *  @param    	int				$info_bits        	Miscellaneous informations on line
2952
	 *  @param    	int				$date_start        	Start date of the line
2953
	 *  @param    	int				$date_end          	End date of the line
2954
	 * 	@param		int				$type				Type of line (0=product, 1=service)
2955
	 * 	@param		int				$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
2956
	 * 	@param		int				$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
2957
	 *  @param		int				$fk_fournprice		Id of origin supplier price
2958
	 *  @param		int				$pa_ht				Price (without tax) of product when it was bought
2959
	 *  @param		string			$label				Label
2960
	 *  @param		int				$special_code		Special code (also used by externals modules!)
2961
	 *  @param		array			$array_options		extrafields array
2962
	 * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
2963
	 *  @param		double			$pu_ht_devise		Amount in currency
2964
	 * 	@param		int				$notrigger			disable line update trigger
2965
	 *  @return   	int              					< 0 if KO, > 0 if OK
2966
	 */
2967
	public 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)
2968
	{
2969
		global $conf, $mysoc, $langs, $user;
2970
2971
		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");
2972
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2973
2974
		if ($this->statut == Commande::STATUS_DRAFT)
2975
		{
2976
2977
			// Clean parameters
2978
			if (empty($qty)) $qty=0;
2979
			if (empty($info_bits)) $info_bits=0;
2980
			if (empty($txtva)) $txtva=0;
2981
			if (empty($txlocaltax1)) $txlocaltax1=0;
2982
			if (empty($txlocaltax2)) $txlocaltax2=0;
2983
			if (empty($remise_percent)) $remise_percent=0;
2984
			if (empty($special_code) || $special_code == 3) $special_code=0;
2985
2986
			if ($date_start && $date_end && $date_start > $date_end) {
2987
				$langs->load("errors");
2988
				$this->error=$langs->trans('ErrorStartDateGreaterEnd');
2989
				return -1;
2990
			}
2991
2992
			$remise_percent=price2num($remise_percent);
2993
			$qty=price2num($qty);
2994
			$pu = price2num($pu);
2995
			$pa_ht=price2num($pa_ht);
2996
			$pu_ht_devise=price2num($pu_ht_devise);
2997
			$txtva=price2num($txtva);
2998
			$txlocaltax1=price2num($txlocaltax1);
2999
			$txlocaltax2=price2num($txlocaltax2);
3000
3001
			$this->db->begin();
3002
3003
			// Calcul du total TTC et de la TVA pour la ligne a partir de
3004
			// qty, pu, remise_percent et txtva
3005
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3006
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3007
3008
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3009
3010
			// Clean vat code
3011
			$vat_src_code='';
3012
			if (preg_match('/\((.*)\)/', $txtva, $reg))
3013
			{
3014
				$vat_src_code = $reg[1];
3015
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
3016
			}
3017
3018
			$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);
3019
3020
			$total_ht  = $tabprice[0];
3021
			$total_tva = $tabprice[1];
3022
			$total_ttc = $tabprice[2];
3023
			$total_localtax1 = $tabprice[9];
3024
			$total_localtax2 = $tabprice[10];
3025
			$pu_ht  = $tabprice[3];
3026
			$pu_tva = $tabprice[4];
3027
			$pu_ttc = $tabprice[5];
3028
3029
			// MultiCurrency
3030
			$multicurrency_total_ht  = $tabprice[16];
3031
			$multicurrency_total_tva = $tabprice[17];
3032
			$multicurrency_total_ttc = $tabprice[18];
3033
			$pu_ht_devise = $tabprice[19];
3034
3035
			// Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3036
			$price = $pu_ht;
3037
			if ($price_base_type == 'TTC')
3038
			{
3039
				$subprice = $pu_ttc;
3040
			}
3041
			else
3042
			{
3043
				$subprice = $pu_ht;
3044
			}
3045
			$remise = 0;
3046
			if ($remise_percent > 0)
3047
			{
3048
				$remise = round(($pu * $remise_percent / 100), 2);
3049
				$price = ($pu - $remise);
3050
			}
3051
3052
			//Fetch current line from the database and then clone the object and set it in $oldline property
3053
			$line = new OrderLine($this->db);
3054
			$line->fetch($rowid);
3055
3056
			if (!empty($line->fk_product))
3057
			{
3058
				$product=new Product($this->db);
3059
				$result=$product->fetch($line->fk_product);
3060
				$product_type=$product->type;
3061
3062
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
3063
				{
3064
					$langs->load("errors");
3065
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3066
					dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3067
					$this->db->rollback();
3068
					return self::STOCK_NOT_ENOUGH_FOR_ORDER;
3069
				}
3070
			}
3071
3072
			$staticline = clone $line;
3073
3074
			$line->oldline = $staticline;
3075
			$this->line = $line;
3076
			$this->line->context = $this->context;
3077
3078
			// Reorder if fk_parent_line change
3079
			if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
3080
			{
3081
				$rangmax = $this->line_max($fk_parent_line);
3082
				$this->line->rang = $rangmax + 1;
3083
			}
3084
3085
			$this->line->rowid=$rowid;
1 ignored issue
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

3085
			/** @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...
3086
			$this->line->label=$label;
3087
			$this->line->desc=$desc;
3088
			$this->line->qty=$qty;
1 ignored issue
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...
3089
3090
			$this->line->vat_src_code	= $vat_src_code;
3091
			$this->line->tva_tx         = $txtva;
1 ignored issue
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...
3092
			$this->line->localtax1_tx   = $txlocaltax1;
1 ignored issue
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...
3093
			$this->line->localtax2_tx   = $txlocaltax2;
1 ignored issue
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...
3094
			$this->line->localtax1_type = $localtaxes_type[0];
3095
			$this->line->localtax2_type = $localtaxes_type[2];
3096
			$this->line->remise_percent = $remise_percent;
1 ignored issue
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...
3097
			$this->line->subprice       = $subprice;
3098
			$this->line->info_bits      = $info_bits;
3099
			$this->line->special_code   = $special_code;
3100
			$this->line->total_ht       = $total_ht;
3101
			$this->line->total_tva      = $total_tva;
3102
			$this->line->total_localtax1= $total_localtax1;
3103
			$this->line->total_localtax2= $total_localtax2;
3104
			$this->line->total_ttc      = $total_ttc;
3105
			$this->line->date_start     = $date_start;
3106
			$this->line->date_end       = $date_end;
3107
			$this->line->product_type   = $type;
3108
			$this->line->fk_parent_line = $fk_parent_line;
3109
			$this->line->skip_update_total=$skip_update_total;
3110
			$this->line->fk_unit        = $fk_unit;
3111
3112
			$this->line->fk_fournprice = $fk_fournprice;
3113
			$this->line->pa_ht = $pa_ht;
1 ignored issue
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...
3114
3115
			// Multicurrency
3116
			$this->line->multicurrency_subprice		= $pu_ht_devise;
3117
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
3118
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
3119
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
3120
3121
			// TODO deprecated
3122
			$this->line->price=$price;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

3122
			/** @scrutinizer ignore-deprecated */ $this->line->price=$price;
Loading history...
3123
			$this->line->remise=$remise;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

3123
			/** @scrutinizer ignore-deprecated */ $this->line->remise=$remise;
Loading history...
3124
3125
			if (is_array($array_options) && count($array_options)>0) {
3126
				$this->line->array_options=$array_options;
3127
			}
3128
3129
			$result=$this->line->update($user, $notrigger);
3130
			if ($result > 0)
3131
			{
3132
				// Reorder if child line
3133
				if (! empty($fk_parent_line)) $this->line_order(true, 'DESC');
3134
3135
				// Mise a jour info denormalisees
3136
				$this->update_price(1);
3137
3138
				$this->db->commit();
3139
				return $result;
3140
			}
3141
			else
3142
			{
3143
				$this->error=$this->line->error;
3144
3145
				$this->db->rollback();
3146
				return -1;
3147
			}
3148
		}
3149
		else
3150
		{
3151
			$this->error=get_class($this)."::updateline Order status makes operation forbidden";
3152
			$this->errors=array('OrderStatusMakeOperationForbidden');
3153
			return -2;
3154
		}
3155
	}
3156
3157
	/**
3158
	 *      Update database
3159
	 *
3160
	 *      @param      User	$user        	User that modify
3161
	 *      @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
3162
	 *      @return     int      			   	<0 if KO, >0 if OK
3163
	 */
3164
	public function update(User $user, $notrigger = 0)
3165
	{
3166
		global $conf;
3167
3168
		$error=0;
3169
3170
		// Clean parameters
3171
		if (isset($this->ref)) $this->ref=trim($this->ref);
3172
		if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
3173
		if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3174
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
3175
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
3176
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
3177
3178
		// Check parameters
3179
		// Put here code to add control on parameters values
3180
3181
		// Update request
3182
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3183
3184
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
3185
		$sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
3186
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
3187
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
3188
		$sql.= " date_commande=".(strval($this->date_commande)!='' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3189
		$sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3190
		$sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
3191
		$sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
3192
		$sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
3193
		$sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
3194
		$sql.= " total_ttc=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
3195
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
3196
		$sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
3197
		$sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
3198
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
3199
		$sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
3200
		$sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
3201
		$sql.= " fk_account=".($this->fk_account>0?$this->fk_account:"null").",";
3202
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
3203
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
3204
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
3205
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
3206
3207
		$sql.= " WHERE rowid=".$this->id;
3208
3209
		$this->db->begin();
3210
3211
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
3212
		$resql = $this->db->query($sql);
3213
		if (! $resql) {
3214
			$error++; $this->errors[]="Error ".$this->db->lasterror();
3215
		}
3216
3217
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
3218
		{
3219
			$result=$this->insertExtraFields();
3220
			if ($result < 0)
3221
			{
3222
				$error++;
3223
			}
3224
		}
3225
3226
		if (! $error && ! $notrigger)
3227
		{
3228
			// Call trigger
3229
			$result=$this->call_trigger('ORDER_MODIFY', $user);
3230
			if ($result < 0) $error++;
3231
			// End call triggers
3232
		}
3233
3234
		// Commit or rollback
3235
		if ($error)
3236
		{
3237
			foreach($this->errors as $errmsg)
3238
			{
3239
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3240
				$this->error.=($this->error?', '.$errmsg:$errmsg);
3241
			}
3242
			$this->db->rollback();
3243
			return -1*$error;
3244
		}
3245
		else
3246
		{
3247
			$this->db->commit();
3248
			return 1;
3249
		}
3250
	}
3251
3252
	/**
3253
	 *	Delete the customer order
3254
	 *
3255
	 *	@param	User	$user		User object
3256
	 *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
3257
	 * 	@return	int					<=0 if KO, >0 if OK
3258
	 */
3259
	public function delete($user, $notrigger = 0)
3260
	{
3261
		global $conf, $langs;
3262
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3263
3264
		$error = 0;
3265
3266
		dol_syslog(get_class($this) . "::delete ".$this->id, LOG_DEBUG);
3267
3268
		$this->db->begin();
3269
3270
		if (! $error && ! $notrigger)
3271
		{
3272
			// Call trigger
3273
			$result=$this->call_trigger('ORDER_DELETE', $user);
3274
			if ($result < 0) $error++;
3275
			// End call triggers
3276
		}
3277
3278
		if ($this->nb_expedition() != 0)
3279
		{
3280
			$this->errors[] = $langs->trans('SomeShipmentExists');
3281
			$error++;
3282
		}
3283
3284
		if (! $error)
3285
		{
3286
			// Delete order details
3287
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE fk_commande = ".$this->id;
3288
			if (! $this->db->query($sql) )
3289
			{
3290
				$error++;
3291
				$this->errors[]=$this->db->lasterror();
3292
			}
3293
		}
3294
3295
		if (! $error)
3296
		{
3297
			// Delete linked object
3298
			$res = $this->deleteObjectLinked();
3299
			if ($res < 0) $error++;
3300
		}
3301
3302
		if (! $error)
3303
		{
3304
			// Delete linked contacts
3305
			$res = $this->delete_linked_contact();
3306
			if ($res < 0) $error++;
3307
		}
3308
3309
		if (! $error)
3310
		{
3311
			// Remove extrafields
3312
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
3313
			{
3314
				$result=$this->deleteExtraFields();
3315
				if ($result < 0)
3316
				{
3317
					$error++;
3318
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3319
				}
3320
			}
3321
		}
3322
3323
		if (! $error)
3324
		{
3325
			// Delete object
3326
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande WHERE rowid = ".$this->id;
3327
			if (! $this->db->query($sql) )
3328
			{
3329
				$error++;
3330
				$this->errors[]=$this->db->lasterror();
3331
			}
3332
		}
3333
3334
		if (! $error)
3335
		{
3336
			// Remove directory with files
3337
			$comref = dol_sanitizeFileName($this->ref);
3338
			if ($conf->commande->dir_output && !empty($this->ref))
3339
			{
3340
				$dir = $conf->commande->dir_output . "/" . $comref ;
3341
				$file = $conf->commande->dir_output . "/" . $comref . "/" . $comref . ".pdf";
3342
				if (file_exists($file))	// We must delete all files before deleting directory
3343
				{
3344
					dol_delete_preview($this);
3345
3346
					if (! dol_delete_file($file, 0, 0, 0, $this)) // For triggers
3347
					{
3348
						$this->db->rollback();
3349
						return 0;
3350
					}
3351
				}
3352
				if (file_exists($dir))
3353
				{
3354
					if (! dol_delete_dir_recursive($dir))
3355
					{
3356
						$this->error=$langs->trans("ErrorCanNotDeleteDir", $dir);
3357
						$this->db->rollback();
3358
						return 0;
3359
					}
3360
				}
3361
			}
3362
		}
3363
3364
		if (! $error)
3365
		{
3366
			$this->db->commit();
3367
			return 1;
3368
		}
3369
		else
3370
		{
3371
			foreach($this->errors as $errmsg)
3372
			{
3373
				$this->error.=($this->error?', '.$errmsg:$errmsg);
3374
			}
3375
			$this->db->rollback();
3376
			return -1*$error;
3377
		}
3378
	}
3379
3380
3381
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3382
	/**
3383
	 *	Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3384
	 *
3385
	 *	@param		User	$user   Object user
3386
	 *	@return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
3387
	 */
3388
	public function load_board($user)
3389
	{
3390
        // phpcs:enable
3391
		global $conf, $langs;
3392
3393
		$clause = " WHERE";
3394
3395
		$sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3396
		$sql.= " FROM ".MAIN_DB_PREFIX."commande as c";
3397
		if (!$user->rights->societe->client->voir && !$user->societe_id)
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

3397
		if (!$user->rights->societe->client->voir && !/** @scrutinizer ignore-deprecated */ $user->societe_id)
Loading history...
3398
		{
3399
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3400
			$sql.= " WHERE sc.fk_user = " .$user->id;
3401
			$clause = " AND";
3402
		}
3403
		$sql.= $clause." c.entity IN (".getEntity('commande').")";
3404
		//$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3405
		$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
3406
		if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

3406
		if ($user->societe_id) $sql.=" AND c.fk_soc = "./** @scrutinizer ignore-deprecated */ $user->societe_id;
Loading history...
3407
3408
		$resql=$this->db->query($sql);
3409
		if ($resql)
3410
		{
3411
			$response = new WorkboardResponse();
3412
			$response->warning_delay=$conf->commande->client->warning_delay/60/60/24;
3413
			$response->label=$langs->trans("OrdersToProcess");
3414
			$response->url=DOL_URL_ROOT.'/commande/list.php?viewstatut=-3&mainmenu=commercial&leftmenu=orders';
3415
			$response->img=img_object('', "order");
3416
3417
			$generic_commande = new Commande($this->db);
3418
3419
			while ($obj=$this->db->fetch_object($resql))
3420
			{
3421
				$response->nbtodo++;
3422
				$response->total+= $obj->total_ht;
3423
3424
				$generic_commande->statut = $obj->fk_statut;
3425
				$generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3426
				$generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3427
3428
				if ($generic_commande->hasDelay()) {
3429
					$response->nbtodolate++;
3430
				}
3431
			}
3432
3433
			return $response;
3434
		}
3435
		else
3436
		{
3437
			$this->error=$this->db->error();
3438
			return -1;
3439
		}
3440
	}
3441
3442
	/**
3443
	 *	Return source label of order
3444
	 *
3445
	 *	@return     string      Label
3446
	 */
3447
	public function getLabelSource()
3448
	{
3449
		global $langs;
3450
3451
		$label=$langs->trans('OrderSource'.$this->source);
3452
3453
		if ($label == 'OrderSource') return '';
3454
		return $label;
3455
	}
3456
3457
	/**
3458
	 *	Return status label of Order
3459
	 *
3460
	 *	@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
3461
	 *	@return     string      		Label of status
3462
	 */
3463
	public function getLibStatut($mode)
3464
	{
3465
		return $this->LibStatut($this->statut, $this->billed, $mode);
3466
	}
3467
3468
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3469
	/**
3470
	 *	Return label of status
3471
	 *
3472
	 *	@param		int		$statut      	  Id statut
3473
	 *  @param      int		$billed    		  If invoiced
3474
	 *	@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
3475
	 *  @param      int     $donotshowbilled  Do not show billed status after order status
3476
	 *  @return     string					  Label of status
3477
	 */
3478
	public function LibStatut($statut, $billed, $mode, $donotshowbilled = 0)
3479
	{
3480
        // phpcs:enable
3481
		global $langs, $conf;
3482
3483
		$billedtext = '';
3484
		if (empty($donotshowbilled)) $billedtext .= ($billed?' - '.$langs->trans("Billed"):'');
3485
3486
		if ($statut==self::STATUS_CANCELED){
3487
		    $labelstatut = $langs->trans('StatusOrderCanceled');
3488
		    $labelstatutShort = $langs->trans('StatusOrderCanceledShort');
3489
		    $statusType='status5';
3490
		}
3491
		elseif ($statut==self::STATUS_DRAFT){
3492
		    $labelstatut = $langs->trans('StatusOrderDraft');
3493
		    $labelstatutShort = $langs->trans('StatusOrderDraftShort');
3494
		    $statusType='status0';
3495
		}
3496
		elseif ($statut==self::STATUS_VALIDATED){
3497
		    $labelstatut = $langs->trans('StatusOrderValidated').$billedtext;
3498
		    $labelstatutShort = $langs->trans('StatusOrderValidatedShort').$billedtext;
3499
		    $statusType='status1';
3500
		}
3501
		elseif ($statut==self::STATUS_SHIPMENTONPROCESS){
3502
		    $labelstatut = $langs->trans('StatusOrderSentShort').$billedtext;
3503
		    $labelstatutShort = $langs->trans('StatusOrderSentShort').$billedtext;
3504
		    $statusType='status3';
3505
		}
3506
		elseif ($statut==self::STATUS_CLOSED && (! $billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))){
3507
		    $labelstatut = $langs->trans('StatusOrderToBill');
3508
		    $labelstatutShort = $langs->trans('StatusOrderToBillShort');
3509
		    $statusType='status4';
3510
		}
3511
		elseif ($statut==self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))){
3512
		    $labelstatut = $langs->trans('StatusOrderProcessed').$billedtext;
3513
		    $labelstatutShort = $langs->trans('StatusOrderProcessed').$billedtext;
3514
		    $statusType='status6';
3515
		}
3516
		elseif ($statut==self::STATUS_CLOSED && (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))){
3517
		    $labelstatut = $langs->trans('StatusOrderDelivered');
3518
		    $labelstatutShort = $langs->trans('StatusOrderDelivered');
3519
		    $statusType='status6';
3520
		}
3521
		else{
3522
		    $labelstatut = $langs->trans('Unknown');
3523
		    $labelstatutShort = '';
3524
		    $statusType='';
3525
		    $mode = 0;
3526
		}
3527
3528
		return dolGetStatus($labelstatut, $labelstatutShort, '', $statusType, $mode);
3529
	}
3530
3531
3532
	/**
3533
	 *	Return clicable link of object (with eventually picto)
3534
	 *
3535
	 *	@param      int			$withpicto                Add picto into link
3536
	 *	@param      string	    $option                   Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3537
	 *	@param      int			$max          	          Max length to show
3538
	 *	@param      int			$short			          ???
3539
	 *  @param	    int   	    $notooltip		          1=Disable tooltip
3540
	 *  @param      int         $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3541
	 *	@return     string          			          String with URL
3542
	 */
3543
	public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1)
3544
	{
3545
		global $conf, $langs, $user;
3546
3547
		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
3548
3549
		$result='';
3550
3551
		if (! empty($conf->expedition->enabled) && ($option == '1' || $option == '2')) $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3552
		else $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3553
3554
		if (!$user->rights->commande->lire)
3555
			$option = 'nolink';
3556
3557
		if ($option !== 'nolink')
3558
		{
3559
			// Add param to save lastsearch_values or not
3560
			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3561
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3562
			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3563
		}
3564
3565
		if ($short) return $url;
3566
3567
		$label = '';
3568
3569
		if ($user->rights->commande->lire) {
3570
			$label = '<u>'.$langs->trans("ShowOrder").'</u>';
3571
			$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3572
			$label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
3573
			if (!empty($this->total_ht)) {
3574
				$label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3575
			}
3576
			if (!empty($this->total_tva)) {
3577
				$label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1,	$conf->currency);
3578
			}
3579
			if (!empty($this->total_ttc)) {
3580
				$label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3581
			}
3582
		}
3583
3584
		$linkclose='';
3585
		if (empty($notooltip) && $user->rights->commande->lire)
3586
		{
3587
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3588
			{
3589
				$label=$langs->trans("ShowOrder");
3590
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3591
			}
3592
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3593
			$linkclose.=' class="classfortooltip"';
3594
		}
3595
3596
		$linkstart = '<a href="'.$url.'"';
3597
		$linkstart.=$linkclose.'>';
3598
		$linkend='</a>';
3599
3600
		if ($option === 'nolink') {
3601
			$linkstart = '';
3602
			$linkend = '';
3603
		}
3604
3605
		$result .= $linkstart;
3606
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3607
		if ($withpicto != 2) $result.= $this->ref;
3608
		$result .= $linkend;
3609
3610
		return $result;
3611
	}
3612
3613
3614
	/**
3615
	 *	Charge les informations d'ordre info dans l'objet commande
3616
	 *
3617
	 *	@param  int		$id       Id of order
3618
	 *	@return	void
3619
	 */
3620
	public function info($id)
3621
	{
3622
		$sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3623
		$sql.= ' date_valid as datev,';
3624
		$sql.= ' date_cloture as datecloture,';
3625
		$sql.= ' fk_user_author, fk_user_valid, fk_user_cloture';
3626
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3627
		$sql.= ' WHERE c.rowid = '.$id;
3628
		$result=$this->db->query($sql);
3629
		if ($result)
3630
		{
3631
			if ($this->db->num_rows($result))
3632
			{
3633
				$obj = $this->db->fetch_object($result);
3634
				$this->id = $obj->rowid;
3635
				if ($obj->fk_user_author)
3636
				{
3637
					$cuser = new User($this->db);
3638
					$cuser->fetch($obj->fk_user_author);
3639
					$this->user_creation   = $cuser;
3640
				}
3641
3642
				if ($obj->fk_user_valid)
3643
				{
3644
					$vuser = new User($this->db);
3645
					$vuser->fetch($obj->fk_user_valid);
3646
					$this->user_validation = $vuser;
3647
				}
3648
3649
				if ($obj->fk_user_cloture)
3650
				{
3651
					$cluser = new User($this->db);
3652
					$cluser->fetch($obj->fk_user_cloture);
3653
					$this->user_cloture   = $cluser;
3654
				}
3655
3656
				$this->date_creation     = $this->db->jdate($obj->datec);
3657
				$this->date_modification = $this->db->jdate($obj->datem);
3658
				$this->date_validation   = $this->db->jdate($obj->datev);
3659
				$this->date_cloture      = $this->db->jdate($obj->datecloture);
3660
			}
3661
3662
			$this->db->free($result);
3663
		}
3664
		else
3665
		{
3666
			dol_print_error($this->db);
3667
		}
3668
	}
3669
3670
3671
	/**
3672
	 *  Initialise an instance with random values.
3673
	 *  Used to build previews or test instances.
3674
	 *	id must be 0 if object instance is a specimen.
3675
	 *
3676
	 *  @return	void
3677
	 */
3678
	public function initAsSpecimen()
3679
	{
3680
		global $langs;
3681
3682
		dol_syslog(get_class($this)."::initAsSpecimen");
3683
3684
		// Load array of products prodids
3685
		$num_prods = 0;
3686
		$prodids = array();
3687
		$sql = "SELECT rowid";
3688
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
3689
		$sql.= " WHERE entity IN (".getEntity('product').")";
3690
		$resql = $this->db->query($sql);
3691
		if ($resql)
3692
		{
3693
			$num_prods = $this->db->num_rows($resql);
3694
			$i = 0;
3695
			while ($i < $num_prods)
3696
			{
3697
				$i++;
3698
				$row = $this->db->fetch_row($resql);
3699
				$prodids[$i] = $row[0];
3700
			}
3701
		}
3702
3703
		// Initialise parametres
3704
		$this->id=0;
3705
		$this->ref = 'SPECIMEN';
3706
		$this->specimen=1;
3707
		$this->socid = 1;
3708
		$this->date = time();
3709
		$this->date_lim_reglement=$this->date+3600*24*30;
3710
		$this->cond_reglement_code = 'RECEP';
3711
		$this->mode_reglement_code = 'CHQ';
3712
		$this->availability_code   = 'DSP';
3713
		$this->demand_reason_code  = 'SRC_00';
3714
		$this->note_public='This is a comment (public)';
3715
		$this->note_private='This is a comment (private)';
3716
		// Lines
3717
		$nbp = 5;
3718
		$xnbp = 0;
3719
		while ($xnbp < $nbp)
3720
		{
3721
			$line=new OrderLine($this->db);
3722
3723
			$line->desc=$langs->trans("Description")." ".$xnbp;
3724
			$line->qty=1;
3725
			$line->subprice=100;
3726
			$line->price=100;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

3726
			/** @scrutinizer ignore-deprecated */ $line->price=100;
Loading history...
3727
			$line->tva_tx=20;
3728
			if ($xnbp == 2)
3729
			{
3730
				$line->total_ht=50;
3731
				$line->total_ttc=60;
3732
				$line->total_tva=10;
3733
				$line->remise_percent=50;
3734
			}
3735
			else
3736
			{
3737
				$line->total_ht=100;
3738
				$line->total_ttc=120;
3739
				$line->total_tva=20;
3740
				$line->remise_percent=0;
3741
			}
3742
			if ($num_prods > 0)
3743
			{
3744
				$prodid = mt_rand(1, $num_prods);
3745
				$line->fk_product=$prodids[$prodid];
3746
				$line->product_ref='SPECIMEN';
3747
			}
3748
3749
			$this->lines[$xnbp]=$line;
3750
3751
			$this->total_ht       += $line->total_ht;
3752
			$this->total_tva      += $line->total_tva;
3753
			$this->total_ttc      += $line->total_ttc;
3754
3755
			$xnbp++;
3756
		}
3757
	}
3758
3759
3760
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3761
	/**
3762
	 *	Charge indicateurs this->nb de tableau de bord
3763
	 *
3764
	 *	@return     int         <0 si ko, >0 si ok
3765
	 */
3766
	public function load_state_board()
3767
	{
3768
        // phpcs:enable
3769
		global $user;
3770
3771
		$this->nb=array();
3772
		$clause = "WHERE";
3773
3774
		$sql = "SELECT count(co.rowid) as nb";
3775
		$sql.= " FROM ".MAIN_DB_PREFIX."commande as co";
3776
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3777
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3778
		{
3779
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3780
			$sql.= " WHERE sc.fk_user = " .$user->id;
3781
			$clause = "AND";
3782
		}
3783
		$sql.= " ".$clause." co.entity IN (".getEntity('commande').")";
3784
3785
		$resql=$this->db->query($sql);
3786
		if ($resql)
3787
		{
3788
			while ($obj=$this->db->fetch_object($resql))
3789
			{
3790
				$this->nb["orders"]=$obj->nb;
3791
			}
3792
			$this->db->free($resql);
3793
			return 1;
3794
		}
3795
		else
3796
		{
3797
			dol_print_error($this->db);
3798
			$this->error=$this->db->error();
3799
			return -1;
3800
		}
3801
	}
3802
3803
	/**
3804
	 * 	Create an array of order lines
3805
	 *
3806
	 * 	@return int		>0 if OK, <0 if KO
3807
	 */
3808
	public function getLinesArray()
3809
	{
3810
		return $this->fetch_lines();
3811
	}
3812
3813
	/**
3814
	 *  Create a document onto disk according to template module.
3815
	 *
3816
	 *  @param	    string		$modele			Force template to use ('' to not force)
3817
	 *  @param		Translate	$outputlangs	objet lang a utiliser pour traduction
3818
	 *  @param      int			$hidedetails    Hide details of lines
3819
	 *  @param      int			$hidedesc       Hide description
3820
	 *  @param      int			$hideref        Hide ref
3821
	 *  @param      null|array  $moreparams     Array to provide more information
3822
	 *  @return     int         				0 if KO, 1 if OK
3823
	 */
3824
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3825
	{
3826
		global $conf,$langs;
3827
3828
		$langs->load("orders");
3829
3830
		if (! dol_strlen($modele)) {
3831
3832
			$modele = 'einstein';
3833
3834
			if ($this->modelpdf) {
3835
				$modele = $this->modelpdf;
3836
			} elseif (! empty($conf->global->COMMANDE_ADDON_PDF)) {
3837
				$modele = $conf->global->COMMANDE_ADDON_PDF;
3838
			}
3839
		}
3840
3841
		$modelpath = "core/modules/commande/doc/";
3842
3843
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3844
	}
3845
3846
3847
	/**
3848
	 * Function used to replace a thirdparty id with another one.
3849
	 *
3850
	 * @param DoliDB $db Database handler
3851
	 * @param int $origin_id Old thirdparty id
3852
	 * @param int $dest_id New thirdparty id
3853
	 * @return bool
3854
	 */
3855
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3856
	{
3857
		$tables = array(
3858
		'commande'
3859
		);
3860
3861
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3862
	}
3863
3864
	/**
3865
	 * Is the customer order delayed?
3866
	 *
3867
	 * @return bool     true if late, false if not
3868
	 */
3869
	public function hasDelay()
3870
	{
3871
		global $conf;
3872
3873
		if (! ($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
3874
			return false;   // Never late if not inside this status range
3875
		}
3876
3877
		$now = dol_now();
3878
3879
		return max($this->date_commande, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande has been deprecated. ( Ignorable by Annotation )

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

3879
		return max(/** @scrutinizer ignore-deprecated */ $this->date_commande, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
Loading history...
3880
	}
3881
3882
	/**
3883
	 * Show the customer delayed info
3884
	 *
3885
	 * @return string       Show delayed information
3886
	 */
3887
	public function showDelay()
3888
	{
3889
		global $conf, $langs;
3890
3891
		if (empty($this->date_livraison)) $text=$langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande has been deprecated. ( Ignorable by Annotation )

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

3891
		if (empty($this->date_livraison)) $text=$langs->trans("OrderDate").' '.dol_print_date(/** @scrutinizer ignore-deprecated */ $this->date_commande, 'day');
Loading history...
3892
		else $text=$text=$langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
3893
		$text.=' '.($conf->commande->client->warning_delay>0?'+':'-').' '.round(abs($conf->commande->client->warning_delay)/3600/24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3894
3895
		return $text;
3896
	}
3897
}
3898
3899
3900
/**
3901
 *  Class to manage order lines
3902
 */
3903
class OrderLine extends CommonOrderLine
3904
{
3905
	/**
3906
	 * @var string ID to identify managed object
3907
	 */
3908
	public $element='commandedet';
3909
3910
	public $table_element='commandedet';
3911
3912
	public $oldline;
3913
3914
	/**
3915
	 * Id of parent order
3916
	 * @var int
3917
	 */
3918
	public $fk_commande;
3919
3920
	/**
3921
	 * Id of parent order
3922
	 * @var int
3923
	 * @deprecated Use fk_commande
3924
	 * @see $fk_commande
3925
	 */
3926
	public $commande_id;
3927
3928
	// From llx_commandedet
3929
	public $fk_parent_line;
3930
	public $fk_facture;
3931
3932
	/**
3933
	 * @var string Order lines label
3934
	 */
3935
	public $label;
3936
3937
	public $fk_remise_except;
3938
	public $rang = 0;
3939
	public $fk_fournprice;
3940
3941
	/**
3942
	 * Buy price without taxes
3943
	 * @var float
3944
	 */
3945
	public $pa_ht;
3946
	public $marge_tx;
3947
	public $marque_tx;
3948
3949
	/**
3950
	 * @deprecated
3951
	 * @see $remise_percent, $fk_remise_except
3952
	 */
3953
	public $remise;
3954
3955
	// Start and end date of the line
3956
	public $date_start;
3957
	public $date_end;
3958
3959
	public $skip_update_total; // Skip update price total for special lines
3960
3961
3962
	/**
3963
	 *      Constructor
3964
	 *
3965
	 *      @param     DoliDB	$db      handler d'acces base de donnee
3966
	 */
3967
    public function __construct($db)
3968
    {
3969
        $this->db= $db;
3970
    }
3971
3972
	/**
3973
	 *  Load line order
3974
	 *
3975
	 *  @param  int		$rowid          Id line order
3976
	 *  @return	int						<0 if KO, >0 if OK
3977
	 */
3978
	public function fetch($rowid)
3979
	{
3980
		$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,';
3981
		$sql.= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice,';
3982
		$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,';
3983
		$sql.= ' cd.fk_unit,';
3984
		$sql.= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
3985
		$sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc, p.tobatch as product_tobatch,';
3986
		$sql.= ' cd.date_start, cd.date_end';
3987
		$sql.= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
3988
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3989
		$sql.= ' WHERE cd.rowid = '.$rowid;
3990
		$result = $this->db->query($sql);
3991
		if ($result)
3992
		{
3993
			$objp = $this->db->fetch_object($result);
3994
			$this->rowid            = $objp->rowid;
3995
			$this->id				= $objp->rowid;
3996
			$this->fk_commande      = $objp->fk_commande;
3997
			$this->fk_parent_line   = $objp->fk_parent_line;
3998
			$this->label            = $objp->custom_label;
3999
			$this->desc             = $objp->description;
4000
			$this->qty              = $objp->qty;
4001
			$this->price            = $objp->price;
4002
			$this->subprice         = $objp->subprice;
4003
			$this->vat_src_code     = $objp->vat_src_code;
4004
			$this->tva_tx           = $objp->tva_tx;
4005
			$this->localtax1_tx		= $objp->localtax1_tx;
4006
			$this->localtax2_tx		= $objp->localtax2_tx;
4007
			$this->remise           = $objp->remise;
4008
			$this->remise_percent   = $objp->remise_percent;
4009
			$this->fk_remise_except = $objp->fk_remise_except;
4010
			$this->fk_product       = $objp->fk_product;
4011
			$this->product_type     = $objp->product_type;
4012
			$this->info_bits        = $objp->info_bits;
4013
			$this->special_code		= $objp->special_code;
4014
			$this->total_ht         = $objp->total_ht;
4015
			$this->total_tva        = $objp->total_tva;
4016
			$this->total_localtax1  = $objp->total_localtax1;
4017
			$this->total_localtax2  = $objp->total_localtax2;
4018
			$this->total_ttc        = $objp->total_ttc;
4019
			$this->fk_fournprice	= $objp->fk_fournprice;
4020
			$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4021
			$this->pa_ht			= $marginInfos[0];
4022
			$this->marge_tx			= $marginInfos[1];
4023
			$this->marque_tx		= $marginInfos[2];
4024
			$this->special_code		= $objp->special_code;
4025
			$this->rang             = $objp->rang;
4026
4027
			$this->ref				= $objp->product_ref;      // deprecated
4028
			$this->product_ref		= $objp->product_ref;
4029
			$this->libelle			= $objp->product_libelle;  // deprecated
4030
			$this->product_label	= $objp->product_libelle;
4031
			$this->product_desc     = $objp->product_desc;
4032
			$this->product_tobatch  = $objp->product_tobatch;
4033
			$this->fk_unit          = $objp->fk_unit;
4034
4035
			$this->date_start       = $this->db->jdate($objp->date_start);
4036
			$this->date_end         = $this->db->jdate($objp->date_end);
4037
4038
			$this->fk_multicurrency			= $objp->fk_multicurrency;
4039
			$this->multicurrency_code		= $objp->multicurrency_code;
4040
			$this->multicurrency_subprice	= $objp->multicurrency_subprice;
4041
			$this->multicurrency_total_ht	= $objp->multicurrency_total_ht;
4042
			$this->multicurrency_total_tva	= $objp->multicurrency_total_tva;
4043
			$this->multicurrency_total_ttc	= $objp->multicurrency_total_ttc;
4044
4045
			$this->db->free($result);
4046
4047
			return 1;
4048
		}
4049
		else
4050
		{
4051
			$this->error = $this->db->lasterror();
4052
			return -1;
4053
		}
4054
	}
4055
4056
	/**
4057
	 * 	Delete line in database
4058
	 *
4059
	 *	@param      User	$user        	User that modify
4060
	 *  @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
4061
	 *	@return	 int  <0 si ko, >0 si ok
4062
	 */
4063
	public function delete(User $user, $notrigger = 0)
4064
	{
4065
		global $conf, $langs;
4066
4067
		$error=0;
4068
4069
        // check if order line is not in a shipment line before deleting
4070
        $sqlCheckShipmentLine  = "SELECT";
4071
        $sqlCheckShipmentLine .= " ed.rowid";
4072
        $sqlCheckShipmentLine .= " FROM " . MAIN_DB_PREFIX . "expeditiondet ed";
4073
        $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = " . $this->rowid;
1 ignored issue
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

4073
        $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = " . /** @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...
4074
4075
        $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4076
        if (!$resqlCheckShipmentLine) {
4077
            $error++;
4078
            $this->error    = $this->db->lasterror();
4079
            $this->errors[] = $this->error;
4080
        } else {
4081
            $langs->load('errors');
4082
            $num = $this->db->num_rows($resqlCheckShipmentLine);
4083
            if ($num > 0) {
4084
                $error++;
4085
                $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4086
                $this->error = $langs->trans('ErrorRecordAlreadyExists') . ' : ' . $langs->trans('ShipmentLine') . ' ' . $objCheckShipmentLine->rowid;
4087
                $this->errors[] = $this->error;
4088
            }
4089
            $this->db->free($resqlCheckShipmentLine);
4090
        }
4091
        if ($error) {
4092
            dol_syslog(__METHOD__ . 'Error ; ' . $this->error, LOG_ERR);
4093
            return -1;
4094
        }
4095
4096
		$this->db->begin();
4097
4098
		$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE rowid=".$this->rowid;
1 ignored issue
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

4098
		$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...
4099
4100
		dol_syslog("OrderLine::delete", LOG_DEBUG);
4101
		$resql=$this->db->query($sql);
4102
		if ($resql)
4103
		{
4104
			// Remove extrafields
4105
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
4106
			{
4107
				$this->id=$this->rowid;
4108
				$result=$this->deleteExtraFields();
4109
				if ($result < 0)
4110
				{
4111
					$error++;
4112
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4113
				}
4114
			}
4115
4116
			if (! $error && ! $notrigger)
4117
			{
4118
				// Call trigger
4119
				$result=$this->call_trigger('LINEORDER_DELETE', $user);
4120
				if ($result < 0) $error++;
4121
				// End call triggers
4122
			}
4123
4124
			if (!$error) {
4125
				$this->db->commit();
4126
				return 1;
4127
			}
4128
4129
			foreach($this->errors as $errmsg)
4130
			{
4131
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4132
				$this->error.=($this->error?', '.$errmsg:$errmsg);
4133
			}
4134
			$this->db->rollback();
4135
			return -1*$error;
4136
		}
4137
		else
4138
		{
4139
			$this->error=$this->db->lasterror();
4140
			return -1;
4141
		}
4142
	}
4143
4144
	/**
4145
	 *	Insert line into database
4146
	 *
4147
	 *	@param      User	$user        	User that modify
4148
	 *	@param      int		$notrigger		1 = disable triggers
4149
	 *	@return		int						<0 if KO, >0 if OK
4150
	 */
4151
	public function insert($user = null, $notrigger = 0)
4152
	{
4153
		global $langs, $conf;
4154
4155
		$error=0;
4156
4157
		$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'.
4158
4159
		dol_syslog(get_class($this)."::insert rang=".$this->rang);
4160
4161
		// Clean parameters
4162
		if (empty($this->tva_tx)) $this->tva_tx=0;
4163
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4164
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4165
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4166
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4167
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4168
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4169
		if (empty($this->rang)) $this->rang=0;
4170
		if (empty($this->remise)) $this->remise=0;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

4170
		if (empty($this->remise)) /** @scrutinizer ignore-deprecated */ $this->remise=0;
Loading history...
4171
		if (empty($this->remise_percent)) $this->remise_percent=0;
4172
		if (empty($this->info_bits)) $this->info_bits=0;
4173
		if (empty($this->special_code)) $this->special_code=0;
4174
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4175
		if (empty($this->pa_ht)) $this->pa_ht=0;
4176
4177
		// if buy price not defined, define buyprice as configured in margin admin
4178
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4179
		{
4180
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4181
			{
4182
				return $result;
4183
			}
4184
			else
4185
			{
4186
				$this->pa_ht = $result;
4187
			}
4188
		}
4189
4190
		// Check parameters
4191
		if ($this->product_type < 0) return -1;
4192
4193
		$this->db->begin();
4194
4195
		// Insertion dans base de la ligne
4196
		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4197
		$sql.= ' (fk_commande, fk_parent_line, label, description, qty, ';
4198
		$sql.= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4199
		$sql.= ' fk_product, product_type, remise_percent, subprice, price, remise, fk_remise_except,';
4200
		$sql.= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4201
		$sql.= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4202
		$sql.= ' fk_unit';
4203
		$sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4204
		$sql.= ')';
4205
		$sql.= " VALUES (".$this->fk_commande.",";
4206
		$sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
4207
		$sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
4208
		$sql.= " '".$this->db->escape($this->desc)."',";
4209
		$sql.= " '".price2num($this->qty)."',";
4210
		$sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
4211
		$sql.= " '".price2num($this->tva_tx)."',";
4212
		$sql.= " '".price2num($this->localtax1_tx)."',";
4213
		$sql.= " '".price2num($this->localtax2_tx)."',";
4214
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
4215
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
4216
		$sql.= ' '.(! empty($this->fk_product)?$this->fk_product:"null").',';
4217
		$sql.= " '".$this->db->escape($this->product_type)."',";
4218
		$sql.= " '".price2num($this->remise_percent)."',";
4219
		$sql.= " ".(price2num($this->subprice)!==''?price2num($this->subprice):"null").",";
4220
		$sql.= " ".($this->price!=''?"'".price2num($this->price)."'":"null").",";
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

4220
		$sql.= " ".(/** @scrutinizer ignore-deprecated */ $this->price!=''?"'".price2num($this->price)."'":"null").",";
Loading history...
4221
		$sql.= " '".price2num($this->remise)."',";
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

4221
		$sql.= " '".price2num(/** @scrutinizer ignore-deprecated */ $this->remise)."',";
Loading history...
4222
		$sql.= ' '.(! empty($this->fk_remise_except)?$this->fk_remise_except:"null").',';
4223
		$sql.= ' '.$this->special_code.',';
4224
		$sql.= ' '.$this->rang.',';
4225
		$sql.= ' '.(! empty($this->fk_fournprice)?$this->fk_fournprice:"null").',';
4226
		$sql.= ' '.price2num($this->pa_ht).',';
4227
		$sql.= " '".$this->db->escape($this->info_bits)."',";
4228
		$sql.= " ".price2num($this->total_ht).",";
4229
		$sql.= " ".price2num($this->total_tva).",";
4230
		$sql.= " ".price2num($this->total_localtax1).",";
4231
		$sql.= " ".price2num($this->total_localtax2).",";
4232
		$sql.= " ".price2num($this->total_ttc).",";
4233
		$sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
4234
		$sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null").',';
4235
		$sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4236
		$sql.= ", ".(! empty($this->fk_multicurrency) ? $this->fk_multicurrency : 'NULL');
4237
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
4238
		$sql.= ", ".$this->multicurrency_subprice;
4239
		$sql.= ", ".$this->multicurrency_total_ht;
4240
		$sql.= ", ".$this->multicurrency_total_tva;
4241
		$sql.= ", ".$this->multicurrency_total_ttc;
4242
		$sql.= ')';
4243
4244
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4245
		$resql=$this->db->query($sql);
4246
		if ($resql)
4247
		{
4248
			$this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4249
4250
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4251
			{
4252
				$this->id=$this->rowid;
4253
				$result=$this->insertExtraFields();
4254
				if ($result < 0)
4255
				{
4256
					$error++;
4257
				}
4258
			}
4259
4260
			if (! $error && ! $notrigger)
4261
			{
4262
				// Call trigger
4263
				$result=$this->call_trigger('LINEORDER_INSERT', $user);
4264
				if ($result < 0) $error++;
4265
				// End call triggers
4266
			}
4267
4268
			if (!$error) {
4269
				$this->db->commit();
4270
				return 1;
4271
			}
4272
4273
			foreach($this->errors as $errmsg)
4274
			{
4275
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4276
				$this->error.=($this->error?', '.$errmsg:$errmsg);
4277
			}
4278
			$this->db->rollback();
4279
			return -1*$error;
4280
		}
4281
		else
4282
		{
4283
			$this->error=$this->db->error();
4284
			$this->db->rollback();
4285
			return -2;
4286
		}
4287
	}
4288
4289
	/**
4290
	 *	Update the line object into db
4291
	 *
4292
	 *	@param      User	$user        	User that modify
4293
	 *	@param      int		$notrigger		1 = disable triggers
4294
	 *	@return		int		<0 si ko, >0 si ok
4295
	 */
4296
	public function update(User $user, $notrigger = 0)
4297
	{
4298
		global $conf,$langs;
4299
4300
		$error=0;
4301
4302
		$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'.
4303
4304
		// Clean parameters
4305
		if (empty($this->tva_tx)) $this->tva_tx=0;
4306
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4307
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4308
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4309
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4310
		if (empty($this->qty)) $this->qty=0;
4311
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4312
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4313
		if (empty($this->marque_tx)) $this->marque_tx=0;
4314
		if (empty($this->marge_tx)) $this->marge_tx=0;
4315
		if (empty($this->remise)) $this->remise=0;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

4315
		if (empty($this->remise)) /** @scrutinizer ignore-deprecated */ $this->remise=0;
Loading history...
4316
		if (empty($this->remise_percent)) $this->remise_percent=0;
4317
		if (empty($this->info_bits)) $this->info_bits=0;
4318
		if (empty($this->special_code)) $this->special_code=0;
4319
		if (empty($this->product_type)) $this->product_type=0;
4320
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4321
		if (empty($this->pa_ht)) $this->pa_ht=0;
4322
4323
		// if buy price not defined, define buyprice as configured in margin admin
4324
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4325
		{
4326
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4327
			{
4328
				return $result;
4329
			}
4330
			else
4331
			{
4332
				$this->pa_ht = $result;
4333
			}
4334
		}
4335
4336
		$this->db->begin();
4337
4338
		// Mise a jour ligne en base
4339
		$sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4340
		$sql.= " description='".$this->db->escape($this->desc)."'";
4341
		$sql.= " , label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4342
		$sql.= " , vat_src_code=".(! empty($this->vat_src_code)?"'".$this->db->escape($this->vat_src_code)."'":"''");
4343
		$sql.= " , tva_tx=".price2num($this->tva_tx);
4344
		$sql.= " , localtax1_tx=".price2num($this->localtax1_tx);
4345
		$sql.= " , localtax2_tx=".price2num($this->localtax2_tx);
4346
		$sql.= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4347
		$sql.= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4348
		$sql.= " , qty=".price2num($this->qty);
4349
		$sql.= " , subprice=".price2num($this->subprice)."";
4350
		$sql.= " , remise_percent=".price2num($this->remise_percent)."";
4351
		$sql.= " , price=".price2num($this->price)."";					// TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

4351
		$sql.= " , price=".price2num(/** @scrutinizer ignore-deprecated */ $this->price)."";					// TODO A virer
Loading history...
4352
		$sql.= " , remise=".price2num($this->remise)."";				// TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise has been deprecated. ( Ignorable by Annotation )

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

4352
		$sql.= " , remise=".price2num(/** @scrutinizer ignore-deprecated */ $this->remise)."";				// TODO A virer
Loading history...
4353
		if (empty($this->skip_update_total))
4354
		{
4355
			$sql.= " , total_ht=".price2num($this->total_ht)."";
4356
			$sql.= " , total_tva=".price2num($this->total_tva)."";
4357
			$sql.= " , total_ttc=".price2num($this->total_ttc)."";
4358
			$sql.= " , total_localtax1=".price2num($this->total_localtax1);
4359
			$sql.= " , total_localtax2=".price2num($this->total_localtax2);
4360
		}
4361
		$sql.= " , fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?$this->fk_fournprice:"null");
4362
		$sql.= " , buy_price_ht='".price2num($this->pa_ht)."'";
4363
		$sql.= " , info_bits=".$this->info_bits;
4364
		$sql.= " , special_code=".$this->special_code;
4365
		$sql.= " , date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4366
		$sql.= " , date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4367
		$sql.= " , product_type=".$this->product_type;
4368
		$sql.= " , fk_parent_line=".(! empty($this->fk_parent_line)?$this->fk_parent_line:"null");
4369
		if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4370
		$sql.= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4371
4372
		// Multicurrency
4373
		$sql.= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4374
		$sql.= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4375
		$sql.= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4376
		$sql.= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4377
4378
		$sql.= " WHERE rowid = ".$this->rowid;
1 ignored issue
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

4378
		$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...
4379
4380
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
4381
		$resql=$this->db->query($sql);
4382
		if ($resql)
4383
		{
4384
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4385
			{
4386
				$this->id=$this->rowid;
4387
				$result=$this->insertExtraFields();
4388
				if ($result < 0)
4389
				{
4390
					$error++;
4391
				}
4392
			}
4393
4394
			if (! $error && ! $notrigger)
4395
			{
4396
				// Call trigger
4397
				$result=$this->call_trigger('LINEORDER_UPDATE', $user);
4398
				if ($result < 0) $error++;
4399
				// End call triggers
4400
			}
4401
4402
			if (!$error) {
4403
				$this->db->commit();
4404
				return 1;
4405
			}
4406
4407
			foreach($this->errors as $errmsg)
4408
			{
4409
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4410
				$this->error.=($this->error?', '.$errmsg:$errmsg);
4411
			}
4412
			$this->db->rollback();
4413
			return -1*$error;
4414
		}
4415
		else
4416
		{
4417
			$this->error=$this->db->error();
4418
			$this->db->rollback();
4419
			return -2;
4420
		}
4421
	}
4422
4423
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4424
	/**
4425
	 *	Update DB line fields total_xxx
4426
	 *	Used by migration
4427
	 *
4428
	 *	@return		int		<0 if KO, >0 if OK
4429
	 */
4430
	public function update_total()
4431
	{
4432
        // phpcs:enable
4433
		$this->db->begin();
4434
4435
		// Clean parameters
4436
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4437
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4438
4439
		// Mise a jour ligne en base
4440
		$sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4441
		$sql.= " total_ht='".price2num($this->total_ht)."'";
4442
		$sql.= ",total_tva='".price2num($this->total_tva)."'";
4443
		$sql.= ",total_localtax1='".price2num($this->total_localtax1)."'";
4444
		$sql.= ",total_localtax2='".price2num($this->total_localtax2)."'";
4445
		$sql.= ",total_ttc='".price2num($this->total_ttc)."'";
4446
		$sql.= " WHERE rowid = ".$this->rowid;
1 ignored issue
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

4446
		$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...
4447
4448
		dol_syslog("OrderLine::update_total", LOG_DEBUG);
4449
4450
		$resql=$this->db->query($sql);
4451
		if ($resql)
4452
		{
4453
			$this->db->commit();
4454
			return 1;
4455
		}
4456
		else
4457
		{
4458
			$this->error=$this->db->error();
4459
			$this->db->rollback();
4460
			return -2;
4461
		}
4462
	}
4463
}
4464