Completed
Branch develop (d233c1)
by
unknown
31:52
created

Contrat::activateAll()   D

Complexity

Conditions 9
Paths 48

Size

Total Lines 44
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 22
nc 48
nop 2
dl 0
loc 44
rs 4.909
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2003		Rodolphe Quiedeville	<[email protected]>
3
 * Copyright (C) 2004-2012	Destailleur Laurent		<[email protected]>
4
 * Copyright (C) 2005-2014	Regis Houssin			<[email protected]>
5
 * Copyright (C) 2006		Andre Cianfarani		<[email protected]>
6
 * Copyright (C) 2008		Raphael Bertrand		<[email protected]>
7
 * Copyright (C) 2010-2016	Juanjo Menent			<[email protected]>
8
 * Copyright (C) 2013		Christophe Battarel		<[email protected]>
9
 * Copyright (C) 2013		Florian Henry			<[email protected]>
10
 * Copyright (C) 2014-2015	Marcos García			<[email protected]>
11
 * Copyright (C) 2015-2017	Ferran Marcet			<[email protected]>
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 3 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25
 */
26
27
/**
28
 *	\file       htdocs/contrat/class/contrat.class.php
29
 *	\ingroup    contrat
30
 *	\brief      File of class to manage contracts
31
 */
32
33
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
34
require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
35
require_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
36
require_once DOL_DOCUMENT_ROOT . '/margin/lib/margins.lib.php';
37
38
/**
39
 *	Class to manage contracts
40
 */
41
class Contrat extends CommonObject
42
{
43
	public $element='contrat';
44
	public $table_element='contrat';
45
	public $table_element_line='contratdet';
46
	public $fk_element='fk_contrat';
47
    public $picto='contract';
48
    /**
49
     * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
50
     * @var int
51
     */
52
    public $ismultientitymanaged = 1;
53
    /**
54
     * 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
55
     * @var integer
56
     */
57
    public $restrictiononfksoc = 1;
58
59
	/**
60
	 * {@inheritdoc}
61
	 */
62
	protected $table_ref_field = 'ref';
63
64
	/**
65
	 * Customer reference of the contract
66
	 * @var string
67
	 */
68
	var $ref_customer;
69
70
	/**
71
	 * Supplier reference of the contract
72
	 * @var string
73
	 */
74
	var $ref_supplier;
75
76
	/**
77
	 * Client id linked to the contract
78
	 * @var int
79
	 */
80
	var $socid;
81
	var $societe;		// Objet societe
82
83
	/**
84
	 * Status of the contract
85
	 * @var int
86
	 */
87
	var $statut=0;		// 0=Draft,
88
	var $product;
89
90
	/**
91
	 * @var int		Id of user author of the contract
92
	 */
93
	public $fk_user_author;
94
95
	/**
96
	 * TODO: Which is the correct one?
97
	 * Author of the contract
98
	 * @var int
99
	 */
100
	public $user_author_id;
101
102
	/**
103
	 * @var User 	Object user that create the contract. Set by the info method.
104
	 */
105
	public $user_creation;
106
107
	/**
108
	 * @var User 	Object user that close the contract. Set by the info method.
109
	 */
110
	public $user_cloture;
111
112
	/**
113
	 * @var int		Date of creation
114
	 */
115
	var $date_creation;
116
117
	/**
118
	 * @var int		Date of last modification. Not filled until you call ->info()
119
	 */
120
	public $date_modification;
121
122
	/**
123
	 * @var int		Date of validation
124
	 */
125
	var $date_validation;
126
127
	/**
128
	 * @var int		Date when contract was signed
129
	 */
130
	var $date_contrat;
131
132
	/**
133
	 * @var int		Date of contract closure
134
	 * @deprecated we close contract lines, not a contract
135
	 */
136
	var $date_cloture;
137
138
	var $commercial_signature_id;
139
	var $commercial_suivi_id;
140
141
	/**
142
	 * @deprecated Use fk_project instead
143
	 * @see fk_project
144
	 */
145
	var $fk_projet;
146
147
	var $extraparams=array();
148
149
	/**
150
	 * @var ContratLigne[]		Contract lines
151
	 */
152
	var $lines=array();
153
154
	/**
155
	 * Maps ContratLigne IDs to $this->lines indexes
156
	 * @var int[]
157
	 */
158
	protected $lines_id_index_mapper=array();
159
160
161
	/**
162
	 *	Constructor
163
	 *
164
	 *  @param		DoliDB		$db      Database handler
165
	 */
166
	function __construct($db)
167
	{
168
		$this->db = $db;
169
	}
170
171
	/**
172
	 *	Return next contract ref
173
	 *
174
	 *	@param	Societe		$soc		Thirdparty object
175
	 *	@return string					free reference for contract
176
	 */
177
	function getNextNumRef($soc)
178
	{
179
		global $db, $langs, $conf;
180
		$langs->load("contracts");
181
182
		if (!empty($conf->global->CONTRACT_ADDON))
183
		{
184
			$mybool = false;
185
186
			$file = $conf->global->CONTRACT_ADDON.".php";
187
			$classname = $conf->global->CONTRACT_ADDON;
188
189
			// Include file with class
190
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
191
192
			foreach ($dirmodels as $reldir) {
193
194
				$dir = dol_buildpath($reldir."core/modules/contract/");
195
196
				// Load file with numbering class (if found)
197
				$mybool|=@include_once $dir.$file;
198
			}
199
200
			if (! $mybool)
201
			{
202
				dol_print_error('',"Failed to include file ".$file);
203
				return '';
204
			}
205
206
			$obj = new $classname();
207
			$numref = $obj->getNextValue($soc,$this);
208
209
			if ( $numref != "")
210
			{
211
				return $numref;
212
			}
213
			else
214
			{
215
				$this->error = $obj->error;
216
				dol_print_error($db,get_class($this)."::getNextValue ".$obj->error);
217
				return "";
218
			}
219
		}
220
		else
221
		{
222
			$langs->load("errors");
223
			print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
224
			return "";
225
		}
226
	}
227
228
	/**
229
	 *  Activate a contract line
230
	 *
231
	 *  @param	User		$user       Objet User who activate contract
232
	 *  @param  int			$line_id    Id of line to activate
233
	 *  @param  int			$date       Date d'ouverture
234
	 *  @param  int|string	$date_end   Date fin prevue
235
	 * 	@param	string		$comment	A comment typed by user
236
	 *  @return int         			<0 if KO, >0 if OK
237
	 */
238
	function active_line($user, $line_id, $date, $date_end='', $comment='')
239
	{
240
		return $this->lines[$this->lines_id_index_mapper[$line_id]]->active_line($user, $date, $date_end, $comment);
241
	}
242
243
244
	/**
245
	 *  Close a contract line
246
	 *
247
	 *  @param	User		$user       Objet User who close contract
248
	 *  @param  int			$line_id    Id of line to close
249
	 *  @param  int			$date_end	Date end
250
	 * 	@param	string		$comment	A comment typed by user
251
	 *  @return int         			<0 if KO, >0 if OK
252
	 */
253
	function close_line($user, $line_id, $date_end, $comment='')
254
	{
255
		return $this->lines[$this->lines_id_index_mapper[$line_id]]->close_line($user, $date_end, $comment);
256
	}
257
258
259
	/**
260
	 *  Open all lines of a contract
261
	 *
262
	 *  @param	User		$user      		Object User making action
263
	 *  @param	int|string	$date_start		Date start (now if empty)
264
	 *	@return	int							<0 if KO, >0 if OK
265
	 */
266
	function activateAll($user, $date_start='')
267
	{
268
		if (empty($date_start)) $date_start = dol_now();
269
270
		$this->db->begin();
271
272
		// Load lines
273
		$this->fetch_lines();
274
275
		$error=0;
276
277
		foreach($this->lines as $contratline)
278
		{
279
			// Open lines not already open
280
			if ($contratline->statut != 4)
281
			{
282
				$result = $contratline->active_line($user, $date_start, -1);
283
				if ($result < 0)
284
				{
285
					$error++;
286
					$this->errors = $contratline->error;
0 ignored issues
show
Documentation Bug introduced by
It seems like $contratline->error of type string is incompatible with the declared type array<integer,string> of property $errors.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
287
					$this->errors = $contratline->errors;
288
					break;
289
				}
290
			}
291
		}
292
293
		if (! $error && $this->statut == 0)
294
		{
295
			$result=$this->validate($user);
296
			if ($result < 0) $error++;
297
		}
298
299
		if (! $error)
300
		{
301
			$this->db->commit();
302
			return 1;
303
		}
304
		else
305
		{
306
			$this->db->rollback();
307
			return -1;
308
		}
309
	}
310
311
	/**
312
	 * Close all lines of a contract
313
	 *
314
	 * @param	User		$user      		Object User making action
315
     * @param	int			$notrigger		1=Does not execute triggers, 0=Execute triggers
316
	 * @return	int							<0 if KO, >0 if OK
317
	 */
318
	function closeAll(User $user, $notrigger=0)
319
	{
320
		$this->db->begin();
321
322
		// Load lines
323
		$this->fetch_lines();
324
325
		$now = dol_now();
326
327
		$error = 0;
328
329
		foreach($this->lines as $contratline)
330
		{
331
			// Close lines not already closed
332
	        if ($contratline->statut != 5)
333
	        {
334
				$contratline->date_cloture=$now;
335
				$contratline->fk_user_cloture=$user->id;
336
				$contratline->statut='5';
337
				$result=$contratline->close_line($user, $now);
338
				if ($result < 0)
339
				{
340
					$error++;
341
					$this->errors = $contratline->error;
0 ignored issues
show
Documentation Bug introduced by
It seems like $contratline->error of type string is incompatible with the declared type array<integer,string> of property $errors.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
342
					$this->errors = $contratline->errors;
343
					break;
344
				}
345
	        }
346
		}
347
348
		if (! $error && $this->statut == 0)
349
		{
350
			$result=$this->validate($user, '', $notrigger);
351
			if ($result < 0) $error++;
352
		}
353
354
        if (! $error)
355
        {
356
            $this->db->commit();
357
            return 1;
358
        }
359
        else
360
        {
361
            $this->db->rollback();
362
            return -1;
363
        }
364
	}
365
366
	/**
367
	 * Validate a contract
368
	 *
369
	 * @param	User	$user      		Objet User
370
	 * @param   string	$force_number	Reference to force on contract (not implemented yet)
371
     * @param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
372
	 * @return	int						<0 if KO, >0 if OK
373
	 */
374
	function validate(User $user, $force_number='', $notrigger=0)
375
	{
376
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
377
		global $langs, $conf;
378
379
		$now=dol_now();
380
381
		$error=0;
382
		dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number);
383
384
385
		$this->db->begin();
386
387
		$this->fetch_thirdparty();
388
389
		// A contract is validated so we can move thirdparty to status customer
390
		$result=$this->thirdparty->set_as_client();
391
392
		// Define new ref
393
		if ($force_number)
394
		{
395
			$num = $force_number;
396
		}
397
		else if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
398
		{
399
			$num = $this->getNextNumRef($this->thirdparty);
400
		}
401
		else
402
		{
403
			$num = $this->ref;
404
		}
405
        $this->newref = $num;
406
407
		if ($num)
408
		{
409
			$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET ref = '".$num."', statut = 1";
410
			//$sql.= ", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
411
			$sql .= " WHERE rowid = ".$this->id . " AND statut = 0";
412
413
			dol_syslog(get_class($this)."::validate", LOG_DEBUG);
414
			$resql = $this->db->query($sql);
415
			if (! $resql)
416
			{
417
				dol_print_error($this->db);
418
				$error++;
419
				$this->error=$this->db->lasterror();
420
			}
421
422
			// Trigger calls
423
			if (! $error && ! $notrigger)
424
			{
425
                // Call trigger
426
                $result=$this->call_trigger('CONTRACT_VALIDATE',$user);
427
                if ($result < 0) { $error++; }
428
                // End call triggers
429
			}
430
431
			if (! $error)
432
			{
433
            	$this->oldref = $this->ref;
434
435
				// Rename directory if dir was a temporary ref
436
				if (preg_match('/^[\(]?PROV/i', $this->ref))
437
				{
438
					// Rename of object directory ($this->ref = old ref, $num = new ref)
439
					// to  not lose the linked files
440
					$oldref = dol_sanitizeFileName($this->ref);
441
					$newref = dol_sanitizeFileName($num);
442
					$dirsource = $conf->contract->dir_output.'/'.$oldref;
443
					$dirdest = $conf->contract->dir_output.'/'.$newref;
444
					if (file_exists($dirsource))
445
					{
446
						dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
447
448
						if (@rename($dirsource, $dirdest))
449
						{
450
							dol_syslog("Rename ok");
451
						    // Rename docs starting with $oldref with $newref
452
            				$listoffiles=dol_dir_list($conf->contract->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
453
            				foreach($listoffiles as $fileentry)
454
            				{
455
            					$dirsource=$fileentry['name'];
456
            					$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
457
            					$dirsource=$fileentry['path'].'/'.$dirsource;
458
            					$dirdest=$fileentry['path'].'/'.$dirdest;
459
            					@rename($dirsource, $dirdest);
460
            				}
461
						}
462
					}
463
				}
464
			}
465
466
			// Set new ref and define current statut
467
			if (! $error)
468
			{
469
				$this->ref = $num;
470
				$this->statut = 1;
471
				$this->brouillon = 0;
472
				$this->date_validation = $now;
473
			}
474
		}
475
		else
476
		{
477
			$error++;
478
		}
479
480
		if (! $error)
481
		{
482
			$this->db->commit();
483
			return 1;
484
		}
485
		else
486
		{
487
			$this->db->rollback();
488
			return -1;
489
		}
490
491
	}
492
493
	/**
494
	 * Unvalidate a contract
495
	 *
496
	 * @param	User	$user      		Objet User
497
     * @param	int		$notrigger		1=Does not execute triggers, 0=execute triggers
498
	 * @return	int						<0 if KO, >0 if OK
499
	 */
500
	function reopen($user, $notrigger=0)
501
	{
502
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
503
		global $langs, $conf;
504
505
		$now=dol_now();
506
507
		$error=0;
508
		dol_syslog(get_class($this).'::reopen user='.$user->id);
509
510
		$this->db->begin();
511
512
		$this->fetch_thirdparty();
513
514
		$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET statut = 0";
515
		//$sql.= ", fk_user_valid = null, date_valid = null";
516
		$sql .= " WHERE rowid = ".$this->id . " AND statut = 1";
517
518
		dol_syslog(get_class($this)."::validate", LOG_DEBUG);
519
		$resql = $this->db->query($sql);
520
		if (! $resql)
521
		{
522
			dol_print_error($this->db);
523
			$error++;
524
			$this->error=$this->db->lasterror();
525
		}
526
527
		// Trigger calls
528
		if (! $error && ! $notrigger)
529
		{
530
			// Call trigger
531
			$result=$this->call_trigger('CONTRACT_REOPEN',$user);
532
			if ($result < 0) {
533
				$error++;
534
			}
535
			// End call triggers
536
		}
537
538
		// Set new ref and define current statut
539
		if (! $error)
540
		{
541
			$this->statut=0;
542
			$this->brouillon=1;
543
			$this->date_validation=$now;
544
		}
545
546
		if (! $error)
547
		{
548
			$this->db->commit();
549
			return 1;
550
		}
551
		else
552
		{
553
			$this->db->rollback();
554
			return -1;
555
		}
556
	}
557
558
	/**
559
	 *    Load a contract from database
560
	 *
561
	 *    @param	int		$id     		Id of contract to load
562
	 *    @param	string	$ref			Ref
563
	 *    @param	string	$ref_customer	Customer ref
564
	 *    @param	string	$ref_supplier	Supplier ref
565
	 *    @return   int     				<0 if KO, 0 if not found, Id of contract if OK
566
	 */
567
	function fetch($id, $ref='', $ref_customer='', $ref_supplier='')
568
	{
569
		$sql = "SELECT rowid, statut, ref, fk_soc, mise_en_service as datemise,";
570
		$sql.= " ref_supplier, ref_customer,";
571
		$sql.= " ref_ext,";
572
		$sql.= " fk_user_mise_en_service, date_contrat as datecontrat,";
573
		$sql.= " fk_user_author, fin_validite, date_cloture,";
574
		$sql.= " fk_projet,";
575
		$sql.= " fk_commercial_signature, fk_commercial_suivi,";
576
		$sql.= " note_private, note_public, model_pdf, extraparams";
577
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat";
578
		if (! $id) $sql.=" WHERE entity IN (".getEntity('contract').")";
579
		else $sql.= " WHERE rowid=".$id;
580
		if ($ref_customer)
581
		{
582
			$sql.= " AND ref_customer = '".$this->db->escape($ref_customer)."'";
583
		}
584
		if ($ref_supplier)
585
		{
586
			$sql.= " AND ref_supplier = '".$this->db->escape($ref_supplier)."'";
587
		}
588
		if ($ref)
589
		{
590
			$sql.= " AND ref='".$this->db->escape($ref)."'";
591
		}
592
593
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
594
		$resql = $this->db->query($sql);
595
		if ($resql)
596
		{
597
			$result = $this->db->fetch_array($resql);
598
599
			if ($result)
1 ignored issue
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
600
			{
601
				$this->id						= $result["rowid"];
602
				$this->ref						= (!isset($result["ref"]) || !$result["ref"]) ? $result["rowid"] : $result["ref"];
603
				$this->ref_customer				= $result["ref_customer"];
604
				$this->ref_supplier				= $result["ref_supplier"];
605
				$this->ref_ext					= $result["ref_ext"];
606
				$this->statut					= $result["statut"];
607
				$this->mise_en_service			= $this->db->jdate($result["datemise"]);
608
609
				$this->date_contrat				= $this->db->jdate($result["datecontrat"]);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($result['datecontrat']) can also be of type string. However, the property $date_contrat is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
610
				$this->date_creation				= $this->db->jdate($result["datecontrat"]);
611
612
				$this->fin_validite				= $this->db->jdate($result["fin_validite"]);
613
				$this->date_cloture				= $this->db->jdate($result["date_cloture"]);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($result['date_cloture']) can also be of type string. However, the property $date_cloture is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
614
615
616
				$this->user_author_id			= $result["fk_user_author"];
617
618
				$this->commercial_signature_id	= $result["fk_commercial_signature"];
619
				$this->commercial_suivi_id		= $result["fk_commercial_suivi"];
620
621
				$this->note_private				= $result["note_private"];
622
				$this->note_public				= $result["note_public"];
623
				$this->modelpdf					= $result["model_pdf"];
624
625
				$this->fk_projet				= $result["fk_projet"]; // deprecated
626
				$this->fk_project				= $result["fk_projet"];
627
628
				$this->socid					= $result["fk_soc"];
629
				$this->fk_soc					= $result["fk_soc"];
630
631
				$this->extraparams				= (array) json_decode($result["extraparams"], true);
632
633
				$this->db->free($resql);
634
635
				// Retreive all extrafield for thirdparty
636
				// fetch optionals attributes and labels
637
				require_once(DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php');
638
				$extrafields=new ExtraFields($this->db);
639
				$extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
640
				$this->fetch_optionals($this->id,$extralabels);
641
642
				/*
643
				 * Lines
644
				*/
645
646
				$this->lines  = array();
647
648
				$result=$this->fetch_lines();
649
				if ($result < 0)
650
				{
651
					$this->error=$this->db->lasterror();
652
					return -3;
653
				}
654
655
				return $this->id;
656
			}
657
			else
658
			{
659
				dol_syslog(get_class($this)."::Fetch Erreur contrat non trouve");
660
				$this->error="Contract not found";
661
				return 0;
662
			}
663
		}
664
		else
665
		{
666
			dol_syslog(get_class($this)."::Fetch Erreur lecture contrat");
667
			$this->error=$this->db->error();
668
			return -1;
669
		}
670
671
	}
672
673
	/**
674
	 *  Load lines array into this->lines
675
	 *
676
	 *  @return ContratLigne[]   Return array of contract lines
677
	 */
678
	function fetch_lines()
679
	{
680
		$this->nbofserviceswait=0;
681
		$this->nbofservicesopened=0;
682
		$this->nbofservicesexpired=0;
683
		$this->nbofservicesclosed=0;
684
685
		$total_ttc=0;
686
		$total_vat=0;
687
		$total_ht=0;
688
689
		$now=dol_now();
690
691
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
692
		$extrafieldsline=new ExtraFields($this->db);
693
		$line = new ContratLigne($this->db);
694
		$extralabelsline=$extrafieldsline->fetch_name_optionals_label($line->table_element,true);
695
696
		$this->lines=array();
697
        $pos = 0;
698
699
		// Selectionne les lignes contrats liees a un produit
700
		$sql = "SELECT p.label as product_label, p.description as product_desc, p.ref as product_ref,";
701
		$sql.= " d.rowid, d.fk_contrat, d.statut, d.description, d.price_ht, d.vat_src_code, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.localtax1_type, d.localtax2_type, d.qty, d.remise_percent, d.subprice, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht,";
702
		$sql.= " d.total_ht,";
703
		$sql.= " d.total_tva,";
704
		$sql.= " d.total_localtax1,";
705
		$sql.= " d.total_localtax2,";
706
		$sql.= " d.total_ttc,";
707
		$sql.= " d.info_bits, d.fk_product,";
708
		$sql.= " d.date_ouverture_prevue, d.date_ouverture,";
709
		$sql.= " d.date_fin_validite, d.date_cloture,";
710
		$sql.= " d.fk_user_author,";
711
		$sql.= " d.fk_user_ouverture,";
712
		$sql.= " d.fk_user_cloture,";
713
		$sql.= " d.fk_unit";
714
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as d LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
715
		$sql.= " WHERE d.fk_contrat = ".$this->id;
716
		$sql.= " ORDER by d.rowid ASC";
717
718
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
719
		$result = $this->db->query($sql);
720
		if ($result)
721
		{
722
			$num = $this->db->num_rows($result);
723
			$i = 0;
724
725
			while ($i < $num)
726
			{
727
				$objp					= $this->db->fetch_object($result);
728
729
				$line					= new ContratLigne($this->db);
730
				$line->id				= $objp->rowid;
731
				$line->ref				= $objp->rowid;
732
				$line->fk_contrat		= $objp->fk_contrat;
733
				$line->desc				= $objp->description;  // Description ligne
734
				$line->qty				= $objp->qty;
735
				$line->vat_src_code 	= $objp->vat_src_code ;
736
				$line->tva_tx			= $objp->tva_tx;
737
				$line->localtax1_tx		= $objp->localtax1_tx;
738
				$line->localtax2_tx		= $objp->localtax2_tx;
739
				$line->localtax1_type	= $objp->localtax1_type;
740
				$line->localtax2_type	= $objp->localtax2_type;
741
				$line->subprice			= $objp->subprice;
742
				$line->statut			= $objp->statut;
743
				$line->remise_percent	= $objp->remise_percent;
744
				$line->price_ht			= $objp->price_ht;
745
				$line->price			= $objp->price_ht;	// For backward compatibility
746
				$line->total_ht			= $objp->total_ht;
747
				$line->total_tva		= $objp->total_tva;
748
				$line->total_localtax1	= $objp->total_localtax1;
749
				$line->total_localtax2	= $objp->total_localtax2;
750
				$line->total_ttc		= $objp->total_ttc;
751
				$line->fk_product		= (($objp->fk_product > 0)?$objp->fk_product:0);
752
				$line->info_bits		= $objp->info_bits;
753
754
				$line->fk_fournprice 	= $objp->fk_fournprice;
755
				$marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
756
				$line->pa_ht 			= $marginInfos[0];
757
758
				$line->fk_user_author	= $objp->fk_user_author;
759
				$line->fk_user_ouverture= $objp->fk_user_ouverture;
760
				$line->fk_user_cloture  = $objp->fk_user_cloture;
761
				$line->fk_unit           = $objp->fk_unit;
762
763
				$line->ref				= $objp->product_ref;						// deprecated
764
				if (empty($objp->fk_product))
765
				{
766
					$line->label			= '';         			// deprecated
767
					$line->libelle 			= $objp->description;	// deprecated
768
				}
769
				else
770
				{
771
					$line->label			= $objp->product_label;         			// deprecated
772
					$line->libelle			= $objp->product_label;         		// deprecated
773
				}
774
				$line->product_ref		= $objp->product_ref;   // Ref product
775
				$line->product_desc		= $objp->product_desc;  // Description product
776
				$line->product_label	= $objp->product_label; // Label product
777
778
				$line->description		= $objp->description;
779
780
				$line->date_start            = $this->db->jdate($objp->date_ouverture_prevue);
781
				$line->date_start_real       = $this->db->jdate($objp->date_ouverture);
782
				$line->date_end              = $this->db->jdate($objp->date_fin_validite);
783
				$line->date_end_real         = $this->db->jdate($objp->date_cloture);
784
				// For backward compatibility
785
				$line->date_ouverture_prevue = $this->db->jdate($objp->date_ouverture_prevue);
786
				$line->date_ouverture        = $this->db->jdate($objp->date_ouverture);
787
				$line->date_fin_validite     = $this->db->jdate($objp->date_fin_validite);
788
				$line->date_cloture          = $this->db->jdate($objp->date_cloture);
789
				$line->date_debut_prevue = $this->db->jdate($objp->date_ouverture_prevue);
790
				$line->date_debut_reel   = $this->db->jdate($objp->date_ouverture);
791
				$line->date_fin_prevue   = $this->db->jdate($objp->date_fin_validite);
792
				$line->date_fin_reel     = $this->db->jdate($objp->date_cloture);
793
794
				// Retreive all extrafield for contract
795
				// fetch optionals attributes and labels
796
				$line->fetch_optionals($line->id,$extralabelsline);
797
798
				$this->lines[$pos]			= $line;
799
				$this->lines_id_index_mapper[$line->id] = $pos;
800
801
				//dol_syslog("1 ".$line->desc);
802
				//dol_syslog("2 ".$line->product_desc);
803
804
				if ($line->statut == 0) $this->nbofserviceswait++;
805
				if ($line->statut == 4 && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) $this->nbofservicesopened++;
806
				if ($line->statut == 4 && (! empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) $this->nbofservicesexpired++;
807
				if ($line->statut == 5) $this->nbofservicesclosed++;
808
809
				$total_ttc+=$objp->total_ttc;   // TODO Not saved into database
810
                $total_vat+=$objp->total_tva;
811
                $total_ht+=$objp->total_ht;
812
813
				$i++;
814
				$pos++;
815
			}
816
			$this->db->free($result);
817
		}
818
		else
819
		{
820
			dol_syslog(get_class($this)."::Fetch Erreur lecture des lignes de contrats liees aux produits");
821
			return -3;
822
		}
823
824
		$this->nbofservices=count($this->lines);
825
        $this->total_ttc = price2num($total_ttc);   // TODO For the moment value is false as value is not stored in database for line linked to products
1 ignored issue
show
Documentation Bug introduced by
It seems like price2num($total_ttc) can also be of type string. However, the property $total_ttc 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...
826
        $this->total_vat = price2num($total_vat);   // TODO For the moment value is false as value is not stored in database for line linked to products
827
        $this->total_ht = price2num($total_ht);     // TODO For the moment value is false as value is not stored in database for line linked to products
1 ignored issue
show
Documentation Bug introduced by
It seems like price2num($total_ht) can also be of type string. However, the property $total_ht 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...
828
829
		return $this->lines;
830
	}
831
832
	/**
833
	 *  Create a contract into database
834
	 *
835
	 *  @param	User	$user       User that create
836
	 *  @return int  				<0 if KO, id of contract if OK
837
	 */
838
	function create($user)
839
	{
840
		global $conf,$langs,$mysoc;
841
842
		// Check parameters
843
		$paramsok=1;
844
		if ($this->commercial_signature_id <= 0)
845
		{
846
			$langs->load("commercial");
847
			$this->error.=$langs->trans("ErrorFieldRequired",$langs->trans("SalesRepresentativeSignature"));
848
			$paramsok=0;
849
		}
850
		if ($this->commercial_suivi_id <= 0)
851
		{
852
			$langs->load("commercial");
853
			$this->error.=($this->error?"<br>":'');
854
			$this->error.=$langs->trans("ErrorFieldRequired",$langs->trans("SalesRepresentativeFollowUp"));
855
			$paramsok=0;
856
		}
857
		if (! $paramsok) return -1;
858
859
860
		$this->db->begin();
861
862
		$now=dol_now();
863
864
		// Insert contract
865
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."contrat (datec, fk_soc, fk_user_author, date_contrat,";
866
		$sql.= " fk_commercial_signature, fk_commercial_suivi, fk_projet,";
867
		$sql.= " ref, entity, note_private, note_public, ref_customer, ref_supplier, ref_ext)";
868
		$sql.= " VALUES ('".$this->db->idate($now)."',".$this->socid.",".$user->id;
869
		$sql.= ", ".(dol_strlen($this->date_contrat)!=0 ? "'".$this->db->idate($this->date_contrat)."'" : "NULL");
870
		$sql.= ",".($this->commercial_signature_id>0?$this->commercial_signature_id:"NULL");
871
		$sql.= ",".($this->commercial_suivi_id>0?$this->commercial_suivi_id:"NULL");
872
		$sql.= ",".($this->fk_project>0?$this->fk_project:"NULL");
873
		$sql.= ", ".(dol_strlen($this->ref)<=0 ? "null" : "'".$this->db->escape($this->ref)."'");
874
		$sql.= ", ".$conf->entity;
875
		$sql.= ", ".(!empty($this->note_private)?("'".$this->db->escape($this->note_private)."'"):"NULL");
876
		$sql.= ", ".(!empty($this->note_public)?("'".$this->db->escape($this->note_public)."'"):"NULL");
877
		$sql.= ", ".(!empty($this->ref_customer)?("'".$this->db->escape($this->ref_customer)."'"):"NULL");
878
		$sql.= ", ".(!empty($this->ref_supplier)?("'".$this->db->escape($this->ref_supplier)."'"):"NULL");
879
		$sql.= ", ".(!empty($this->ref_ext)?("'".$this->db->escape($this->ref_ext)."'"):"NULL");
880
		$sql.= ")";
881
		$resql=$this->db->query($sql);
882
		if ($resql)
883
		{
884
			$error=0;
885
886
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."contrat");
887
888
			// Load object modContract
889
			$module=(! empty($conf->global->CONTRACT_ADDON)?$conf->global->CONTRACT_ADDON:'mod_contract_serpis');
890
			if (substr($module, 0, 13) == 'mod_contract_' && substr($module, -3) == 'php')
891
			{
892
				$module = substr($module, 0, dol_strlen($module)-4);
893
			}
894
			$result=dol_include_once('/core/modules/contract/'.$module.'.php');
895
			if ($result > 0)
896
			{
897
				$modCodeContract = new $module();
898
			}
899
900
			if (!empty($modCodeContract->code_auto)) {
0 ignored issues
show
Bug introduced by
The variable $modCodeContract does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
901
				// Mise a jour ref
902
				$sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".$this->id;
903
				if ($this->db->query($sql))
904
				{
905
					if ($this->id)
906
					{
907
						$this->ref="(PROV".$this->id.")";
908
					}
909
				}
910
			}
911
912
			if (! $error)
913
			{
914
			    if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
915
			    {
916
			    	$result=$this->insertExtraFields();
917
			    	if ($result < 0)
918
			        {
919
			            $error++;
920
			        }
921
			    }
922
			}
923
924
			// Insert contacts commerciaux ('SALESREPSIGN','contrat')
925
			if (! $error)
926
			{
927
    			$result=$this->add_contact($this->commercial_signature_id,'SALESREPSIGN','internal');
928
    			if ($result < 0) $error++;
929
			}
930
931
			// Insert contacts commerciaux ('SALESREPFOLL','contrat')
932
			if (! $error)
933
			{
934
                $result=$this->add_contact($this->commercial_suivi_id,'SALESREPFOLL','internal');
935
			    if ($result < 0) $error++;
936
			}
937
938
			if (! $error)
939
			{
940
				if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
941
				{
942
					$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
943
				}
944
945
				// Add object linked
946
    			if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
947
    			{
948
    			    foreach($this->linked_objects as $origin => $tmp_origin_id)
949
    			    {
950
    			        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, ...))
951
    			        {
952
    			            foreach($tmp_origin_id as $origin_id)
953
    			            {
954
    			                $ret = $this->add_object_linked($origin, $origin_id);
955
    			                if (! $ret)
956
    			                {
957
    			                    $this->error=$this->db->lasterror();
958
    			                    $error++;
959
    			                }
960
    			            }
961
    			        }
962
    			        else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
963
    			        {
964
    			            $origin_id = $tmp_origin_id;
965
    			            $ret = $this->add_object_linked($origin, $origin_id);
966
    			            if (! $ret)
967
    			            {
968
    			                $this->error=$this->db->lasterror();
969
    			                $error++;
970
    			            }
971
    			        }
972
    			    }
973
    			}
974
975
    			if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id))   // Get contact from origin object
976
    			{
977
    			    $originforcontact = $this->origin;
978
    			    $originidforcontact = $this->origin_id;
979
    			    if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
980
    			    {
981
    			        require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
982
    			        $exp = new Expedition($db);
0 ignored issues
show
Bug introduced by
The variable $db does not exist. Did you forget to declare it?

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

Loading history...
983
    			        $exp->fetch($this->origin_id);
984
    			        $exp->fetchObjectLinked();
985
    			        if (count($exp->linkedObjectsIds['commande']) > 0)
986
    			        {
987
    			            foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
988
    			            {
989
    			                $originforcontact = 'commande';
990
    			                $originidforcontact = $value->id;
991
    			                break; // We take first one
992
    			            }
993
    			        }
994
    			    }
995
996
    			    $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";
997
    			    $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
998
999
    			    $resqlcontact = $this->db->query($sqlcontact);
1000
    			    if ($resqlcontact)
1001
    			    {
1002
    			        while($objcontact = $this->db->fetch_object($resqlcontact))
1003
    			        {
1004
    			            if ($objcontact->source == 'internal' && in_array($objcontact->code, array('SALESREPSIGN', 'SALESREPFOLL'))) continue;    // ignore this, already forced previously
1005
1006
    			            //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1007
    			            $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
1008
    			        }
1009
    			    }
1010
    			    else dol_print_error($resqlcontact);
1011
    			}
1012
			}
1013
1014
			if (! $error)
1015
			{
1016
                // Call trigger
1017
                $result=$this->call_trigger('CONTRACT_CREATE',$user);
1018
                if ($result < 0) { $error++; }
1019
                // End call triggers
1020
1021
				if (! $error)
1022
				{
1023
					$this->db->commit();
1024
					return $this->id;
1025
				}
1026
				else
1027
				{
1028
					dol_syslog(get_class($this)."::create - 30 - ".$this->error, LOG_ERR);
1029
					$this->db->rollback();
1030
					return -3;
1031
				}
1032
			}
1033
			else
1034
			{
1035
				$this->error="Failed to add contact";
1036
				dol_syslog(get_class($this)."::create - 20 - ".$this->error, LOG_ERR);
1037
				$this->db->rollback();
1038
				return -2;
1039
			}
1040
		}
1041
		else
1042
		{
1043
			$this->error=$langs->trans("UnknownError: ".$this->db->error()." -", LOG_DEBUG);
1044
1045
			$this->db->rollback();
1046
			return -1;
1047
		}
1048
	}
1049
1050
1051
	/**
1052
	 *  Supprime l'objet de la base
1053
	 *
1054
	 *  @param	User		$user       Utilisateur qui supprime
1055
	 *  @return int         			< 0 si erreur, > 0 si ok
1056
	 */
1057
	function delete($user)
1058
	{
1059
		global $conf, $langs;
1060
		require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1061
1062
		$error=0;
1063
1064
		$this->db->begin();
1065
1066
	    // Call trigger
1067
	    $result=$this->call_trigger('CONTRACT_DELETE',$user);
1068
	    if ($result < 0) { $error++; }
1069
	    // End call triggers
1070
1071
		if (! $error)
1072
		{
1073
			// Delete linked contacts
1074
			$res = $this->delete_linked_contact();
1075
			if ($res < 0)
1076
			{
1077
				dol_syslog(get_class($this)."::delete error", LOG_ERR);
1078
				$error++;
1079
			}
1080
		}
1081
1082
		if (! $error)
1083
		{
1084
			// Delete linked object
1085
			$res = $this->deleteObjectLinked();
1086
			if ($res < 0) $error++;
1087
		}
1088
1089
		if (! $error)
1090
		{
1091
			// Delete contratdet_log
1092
			/*
1093
			$sql = "DELETE cdl";
1094
			$sql.= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1095
			$sql.= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".$this->id;
1096
			*/
1097
			$sql = "SELECT cdl.rowid as cdlrowid ";
1098
			$sql.= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1099
			$sql.= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".$this->id;
1100
1101
			dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1102
			$resql=$this->db->query($sql);
1103
			if (! $resql)
1104
			{
1105
				$this->error=$this->db->error();
1106
				$error++;
1107
			}
1108
			$numressql=$this->db->num_rows($resql);
1109
			if (! $error && $numressql )
1110
			{
1111
				$tab_resql=array();
1112
				for($i=0;$i<$numressql;$i++)
1113
				{
1114
					$objresql=$this->db->fetch_object($resql);
1115
					$tab_resql[]= $objresql->cdlrowid;
1116
				}
1117
				$this->db->free($resql);
1118
1119
				$sql= "DELETE FROM ".MAIN_DB_PREFIX."contratdet_log ";
1120
				$sql.= " WHERE ".MAIN_DB_PREFIX."contratdet_log.rowid IN (".implode(",",$tab_resql).")";
1121
1122
				dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1123
				$resql=$this->db->query($sql);
1124
				if (! $resql)
1125
				{
1126
					$this->error=$this->db->error();
1127
					$error++;
1128
				}
1129
			}
1130
		}
1131
1132
		if (! $error)
1133
		{
1134
			// Delete contratdet
1135
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet";
1136
			$sql.= " WHERE fk_contrat=".$this->id;
1137
1138
			dol_syslog(get_class($this)."::delete contratdet", LOG_DEBUG);
1139
			$resql=$this->db->query($sql);
1140
			if (! $resql)
1141
			{
1142
				$this->error=$this->db->error();
1143
				$error++;
1144
			}
1145
		}
1146
1147
		if (! $error)
1148
		{
1149
			// Delete contrat
1150
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."contrat";
1151
			$sql.= " WHERE rowid=".$this->id;
1152
1153
			dol_syslog(get_class($this)."::delete contrat", LOG_DEBUG);
1154
			$resql=$this->db->query($sql);
1155
			if (! $resql)
1156
			{
1157
				$this->error=$this->db->error();
1158
				$error++;
1159
			}
1160
		}
1161
1162
		// Removed extrafields
1163
		if (! $error) {
1164
			$result=$this->deleteExtraFields();
1165
			if ($result < 0)
1166
			{
1167
				$error++;
1168
				dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1169
			}
1170
		}
1171
1172
		if (! $error)
1173
		{
1174
			// We remove directory
1175
			$ref = dol_sanitizeFileName($this->ref);
1176
			if ($conf->contrat->dir_output)
1177
			{
1178
				$dir = $conf->contrat->dir_output . "/" . $ref;
1179
				if (file_exists($dir))
1180
				{
1181
					$res=@dol_delete_dir_recursive($dir);
1182
					if (! $res)
1183
					{
1184
						$this->error='ErrorFailToDeleteDir';
1185
						$error++;
1186
					}
1187
				}
1188
			}
1189
		}
1190
1191
		if (! $error)
1192
		{
1193
			$this->db->commit();
1194
			return 1;
1195
		}
1196
		else
1197
		{
1198
			$this->error=$this->db->lasterror();
1199
			$this->db->rollback();
1200
			return -1;
1201
		}
1202
	}
1203
1204
	/**
1205
	 *  Update object into database
1206
	 *
1207
	 *  @param	User	$user        User that modifies
1208
	 *  @param  int		$notrigger	 0=launch triggers after, 1=disable triggers
1209
	 *  @return int     		   	 <0 if KO, >0 if OK
1210
	 */
1211
	function update($user, $notrigger=0)
1212
	{
1213
		global $conf, $langs;
1214
		$error=0;
1215
1216
		// Clean parameters
1217
		if (empty($this->fk_commercial_signature) && $this->commercial_signature_id > 0) $this->fk_commercial_signature = $this->commercial_signature_id;
1218
		if (empty($this->fk_commercial_suivi) && $this->commercial_suivi_id > 0) $this->fk_commercial_suivi = $this->commercial_suivi_id;
1219
		if (empty($this->fk_soc) && $this->socid > 0) $this->fk_soc = $this->socid;
1220
		if (empty($this->fk_project) && $this->projet > 0) $this->fk_project = $this->projet;
1221
1222
		if (isset($this->ref)) $this->ref=trim($this->ref);
1223
		if (isset($this->ref_customer)) $this->ref_customer=trim($this->ref_customer);
1224
		if (isset($this->ref_supplier)) $this->ref_supplier=trim($this->ref_supplier);
1225
		if (isset($this->ref_ext)) $this->ref_ext=trim($this->ref_ext);
1226
		if (isset($this->entity)) $this->entity=trim($this->entity);
1227
		if (isset($this->statut)) $this->statut=(int) $this->statut;
1228
		if (isset($this->fk_soc)) $this->fk_soc=trim($this->fk_soc);
1229
		if (isset($this->fk_commercial_signature)) $this->fk_commercial_signature=trim($this->fk_commercial_signature);
1230
		if (isset($this->fk_commercial_suivi)) $this->fk_commercial_suivi=trim($this->fk_commercial_suivi);
1231
		if (isset($this->fk_user_mise_en_service)) $this->fk_user_mise_en_service=trim($this->fk_user_mise_en_service);
1232
		if (isset($this->fk_user_cloture)) $this->fk_user_cloture=trim($this->fk_user_cloture);
1233
		if (isset($this->note_private)) $this->note_private=trim($this->note_private);
1234
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1235
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1236
		//if (isset($this->extraparams)) $this->extraparams=trim($this->extraparams);
1237
1238
		// Check parameters
1239
		// Put here code to add a control on parameters values
1240
1241
		// Update request
1242
    	$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET";
1243
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1244
		$sql.= " ref_customer=".(isset($this->ref_customer)?"'".$this->db->escape($this->ref_customer)."'":"null").",";
1245
		$sql.= " ref_supplier=".(isset($this->ref_supplier)?"'".$this->db->escape($this->ref_supplier)."'":"null").",";
1246
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
1247
		$sql.= " entity=".$conf->entity.",";
1248
		$sql.= " date_contrat=".(dol_strlen($this->date_contrat)!=0 ? "'".$this->db->idate($this->date_contrat)."'" : 'null').",";
1249
		$sql.= " statut=".(isset($this->statut)?$this->statut:"null").",";
1250
		$sql.= " mise_en_service=".(dol_strlen($this->mise_en_service)!=0 ? "'".$this->db->idate($this->mise_en_service)."'" : 'null').",";
1251
		$sql.= " fin_validite=".(dol_strlen($this->fin_validite)!=0 ? "'".$this->db->idate($this->fin_validite)."'" : 'null').",";
1252
		$sql.= " date_cloture=".(dol_strlen($this->date_cloture)!=0 ? "'".$this->db->idate($this->date_cloture)."'" : 'null').",";
1253
		$sql.= " fk_soc=".($this->fk_soc > 0 ? $this->fk_soc:"null").",";
1254
		$sql.= " fk_projet=".($this->fk_project > 0 ? $this->fk_project:"null").",";
1255
		$sql.= " fk_commercial_signature=".(isset($this->fk_commercial_signature)?$this->fk_commercial_signature:"null").",";
1256
		$sql.= " fk_commercial_suivi=".(isset($this->fk_commercial_suivi)?$this->fk_commercial_suivi:"null").",";
1257
		$sql.= " fk_user_mise_en_service=".(isset($this->fk_user_mise_en_service)?$this->fk_user_mise_en_service:"null").",";
1258
		$sql.= " fk_user_cloture=".(isset($this->fk_user_cloture)?$this->fk_user_cloture:"null").",";
1259
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1260
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1261
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
1262
		//$sql.= " extraparams=".(isset($this->extraparams)?"'".$this->db->escape($this->extraparams)."'":"null")."";
1263
		$sql.= " WHERE rowid=".$this->id;
1264
1265
		$this->db->begin();
1266
1267
		$resql = $this->db->query($sql);
1268
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
1269
1270
		if (! $error)
1271
		{
1272
			if (! $notrigger)
1273
			{
1274
				// Call triggers
1275
				$result=$this->call_trigger('CONTRACT_MODIFY',$user);
1276
				if ($result < 0) { $error++; }
1277
				// End call triggers
1278
				}
1279
			}
1280
1281
			if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
1282
			{
1283
				$result=$this->insertExtraFields();
1284
				if ($result < 0)
1285
				{
1286
					$error++;
1287
				}
1288
			}
1289
1290
			// Commit or rollback
1291
			if ($error)
1292
			{
1293
				foreach($this->errors as $errmsg)
1294
				{
1295
					dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1296
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1297
				}
1298
				$this->db->rollback();
1299
				return -1*$error;
1300
			}
1301
			else
1302
			{
1303
				$this->db->commit();
1304
				return 1;
1305
			}
1306
		}
1307
1308
1309
	/**
1310
	 *  Ajoute une ligne de contrat en base
1311
	 *
1312
	 *  @param	string		$desc            	Description de la ligne
1313
	 *  @param  float		$pu_ht              Prix unitaire HT
1314
	 *  @param  int			$qty             	Quantite
1315
	 *  @param  float		$txtva           	Taux tva
1316
	 *  @param  float		$txlocaltax1        Local tax 1 rate
1317
	 *  @param  float		$txlocaltax2        Local tax 2 rate
1318
	 *  @param  int			$fk_product      	Id produit
1319
	 *  @param  float		$remise_percent  	Pourcentage de remise de la ligne
1320
	 *  @param  int			$date_start      	Date de debut prevue
1321
	 *  @param  int			$date_end        	Date de fin prevue
1322
	 *	@param	string		$price_base_type	HT or TTC
1323
	 * 	@param  float		$pu_ttc             Prix unitaire TTC
1324
	 * 	@param  int			$info_bits			Bits de type de lignes
1325
	 * 	@param  int			$fk_fournprice		Fourn price id
1326
	 *  @param  int			$pa_ht				Buying price HT
1327
	 *  @param	array		$array_options		extrafields array
1328
	 * 	@param 	string		$fk_unit 			Code of the unit to use. Null to use the default one
1329
	 * 	@param 	string		$rang 				Position
1330
	 *  @return int             				<0 if KO, >0 if OK
1331
	 */
1332
	function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type='HT', $pu_ttc=0.0, $info_bits=0, $fk_fournprice=null, $pa_ht = 0,$array_options=0, $fk_unit = null, $rang=0)
1333
	{
1334
		global $user, $langs, $conf, $mysoc;
1335
1336
		dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type, $pu_ttc, $info_bits, $rang");
1337
1338
		// Check parameters
1339
		if ($fk_product <= 0 && empty($desc))
1340
		{
1341
			$this->error="DescRequiredForFreeProductLines";
1342
			return -1;
1343
		}
1344
1345
		if ($this->statut >= 0)
1346
		{
1347
			$this->db->begin();
1348
1349
			// Clean parameters
1350
			$pu_ht=price2num($pu_ht);
1351
			$pu_ttc=price2num($pu_ttc);
1352
			$pa_ht=price2num($pa_ht);
1353
			$txtva=price2num($txtva);
1354
			$txlocaltax1=price2num($txlocaltax1);
1355
			$txlocaltax2=price2num($txlocaltax2);
1356
			$remise_percent=price2num($remise_percent);
1357
			$qty=price2num($qty);
1358
			if (empty($qty)) $qty=1;
1359
			if (empty($info_bits)) $info_bits=0;
1360
			if (empty($pu_ht) || ! is_numeric($pu_ht))  $pu_ht=0;
1361
			if (empty($pu_ttc)) $pu_ttc=0;
1362
			if (empty($txtva) || ! is_numeric($txtva)) $txtva=0;
1363
			if (empty($txlocaltax1) || ! is_numeric($txlocaltax1)) $txlocaltax1=0;
1364
            if (empty($txlocaltax2) || ! is_numeric($txlocaltax2)) $txlocaltax2=0;
1365
1366
			if ($price_base_type=='HT')
1367
			{
1368
				$pu=$pu_ht;
1369
			}
1370
			else
1371
			{
1372
				$pu=$pu_ttc;
1373
			}
1374
1375
			// Check parameters
1376
			if (empty($remise_percent)) $remise_percent=0;
1377
1378
			// Calcul du total TTC et de la TVA pour la ligne a partir de
1379
			// qty, pu, remise_percent et txtva
1380
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1381
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1382
1383
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->societe, $mysoc);
1384
1385
		    // Clean vat code
1386
    		$vat_src_code='';
1387
    		if (preg_match('/\((.*)\)/', $txtva, $reg))
1388
    		{
1389
    		    $vat_src_code = $reg[1];
1390
    		    $txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
1391
    		}
1392
1393
			$tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, 1,$mysoc, $localtaxes_type);
1394
			$total_ht  = $tabprice[0];
1395
			$total_tva = $tabprice[1];
1396
			$total_ttc = $tabprice[2];
1397
			$total_localtax1= $tabprice[9];
1398
			$total_localtax2= $tabprice[10];
1399
1400
			$localtax1_type=$localtaxes_type[0];
1401
			$localtax2_type=$localtaxes_type[2];
1402
1403
			// TODO A virer
1404
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1405
			$remise = 0;
1406
			$price = price2num(round($pu_ht, 2));
1407
			if (dol_strlen($remise_percent) > 0)
1408
			{
1409
				$remise = round(($pu_ht * $remise_percent / 100), 2);
1410
				$price = $pu_ht - $remise;
1411
			}
1412
1413
		    if (empty($pa_ht)) $pa_ht=0;
1414
1415
1416
			// if buy price not defined, define buyprice as configured in margin admin
1417
			if ($this->pa_ht == 0)
1418
			{
1419
				if (($result = $this->defineBuyPrice($pu_ht, $remise_percent, $fk_product)) < 0)
1420
				{
1421
					return $result;
1422
				}
1423
				else
1424
				{
1425
					$pa_ht = $result;
1426
				}
1427
			}
1428
1429
			// Insertion dans la base
1430
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
1431
			$sql.= " (fk_contrat, label, description, fk_product, qty, tva_tx, vat_src_code,";
1432
			$sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
1433
			$sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
1434
			$sql.= " info_bits,";
1435
			$sql.= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
1436
			if ($date_start > 0) { $sql.= ",date_ouverture_prevue"; }
1437
			if ($date_end > 0)   { $sql.= ",date_fin_validite"; }
1438
			$sql.= ", fk_unit";
1439
			$sql.= ") VALUES (";
1440
			$sql.= $this->id.", '', '" . $this->db->escape($desc) . "',";
1441
			$sql.= ($fk_product>0 ? $fk_product : "null").",";
1442
			$sql.= " ".$qty.",";
1443
			$sql.= " ".$txtva.",";
1444
			$sql.= " ".($vat_src_code?"'".$vat_src_code."'":"null").",";
1445
			$sql.= " ".$txlocaltax1.",";
1446
			$sql.= " ".$txlocaltax2.",";
1447
			$sql.= " '".$localtax1_type."',";
1448
			$sql.= " '".$localtax2_type."',";
1449
			$sql.= " ".price2num($remise_percent).",";
1450
			$sql.= " ".price2num($pu_ht).",";
1451
			$sql.= " ".price2num($total_ht).",".price2num($total_tva).",".price2num($total_localtax1).",".price2num($total_localtax2).",".price2num($total_ttc).",";
1452
			$sql.= " '".$info_bits."',";
1453
			$sql.= " ".price2num($price).",".price2num($remise).",";
1454
			if (isset($fk_fournprice)) $sql.= ' '.$fk_fournprice.',';
1455
			else $sql.= ' null,';
1456
			if (isset($pa_ht)) $sql.= ' '.price2num($pa_ht);
1457
			else $sql.= ' null';
1458
			if ($date_start > 0) { $sql.= ",'".$this->db->idate($date_start)."'"; }
1459
			if ($date_end > 0) { $sql.= ",'".$this->db->idate($date_end)."'"; }
1460
			$sql.= ", ".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
1461
			$sql.= ")";
1462
1463
			$resql=$this->db->query($sql);
1464
			if ($resql)
1465
			{
1466
				$contractlineid = $this->db->last_insert_id(MAIN_DB_PREFIX."contratdet");
1467
1468
				$result=$this->update_statut($user);
1469
				if ($result > 0)
1470
				{
1471
1472
					if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1473
					{
1474
						$contractline = new ContratLigne($this->db);
1475
						$contractline->array_options=$array_options;
1476
						$contractline->id=$contractlineid;
1477
						$result=$contractline->insertExtraFields();
1478
						if ($result < 0)
1479
						{
1480
							$this->error[]=$contractline->error;
1481
							$error++;
0 ignored issues
show
Bug introduced by
The variable $error does not exist. Did you forget to declare it?

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

Loading history...
1482
						}
1483
					}
1484
1485
					if (empty($error)) {
1486
					    // Call trigger
1487
					    $result=$this->call_trigger('LINECONTRACT_INSERT',$user);
1488
					    if ($result < 0)
1489
					    {
1490
					    	$error++;
1491
					    }
1492
					    // End call triggers
1493
					}
1494
1495
					if ($error)
1496
					{
1497
						$this->db->rollback();
1498
						return -1;
1499
					}
1500
					else
1501
					{
1502
						$this->db->commit();
1503
						return $contractlineid;
1504
					}
1505
				}
1506
				else
1507
				{
1508
					$this->db->rollback();
1509
					return -1;
1510
				}
1511
			}
1512
			else
1513
			{
1514
				$this->db->rollback();
1515
				$this->error=$this->db->error()." sql=".$sql;
1516
				return -1;
1517
			}
1518
		}
1519
		else
1520
		{
1521
			dol_syslog(get_class($this)."::addline ErrorTryToAddLineOnValidatedContract", LOG_ERR);
1522
			return -2;
1523
		}
1524
	}
1525
1526
	/**
1527
	 *  Mets a jour une ligne de contrat
1528
	 *
1529
	 *  @param	int			$rowid            	Id de la ligne de facture
1530
	 *  @param  string		$desc             	Description de la ligne
1531
	 *  @param  float		$pu               	Prix unitaire
1532
	 *  @param  int			$qty              	Quantite
1533
	 *  @param  float		$remise_percent   	Pourcentage de remise de la ligne
1534
	 *  @param  int			$date_start       	Date de debut prevue
1535
	 *  @param  int			$date_end         	Date de fin prevue
1536
	 *  @param  float		$tvatx            	Taux TVA
1537
	 *  @param  float		$localtax1tx      	Local tax 1 rate
1538
	 *  @param  float		$localtax2tx      	Local tax 2 rate
1539
	 *  @param  int|string	$date_debut_reel  	Date de debut reelle
1540
	 *  @param  int|string	$date_fin_reel    	Date de fin reelle
1541
	 *	@param	string		$price_base_type	HT or TTC
1542
	 * 	@param  int			$info_bits			Bits de type de lignes
1543
	 * 	@param  int			$fk_fournprice		Fourn price id
1544
	 *  @param  int			$pa_ht				Buying price HT
1545
	 *  @param	array		$array_options		extrafields array
1546
	 * 	@param 	string		$fk_unit 			Code of the unit to use. Null to use the default one
1547
	 *  @return int              				< 0 si erreur, > 0 si ok
1548
	 */
1549
	function updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $tvatx, $localtax1tx=0.0, $localtax2tx=0.0, $date_debut_reel='', $date_fin_reel='', $price_base_type='HT', $info_bits=0, $fk_fournprice=null, $pa_ht = 0,$array_options=0, $fk_unit = null)
1550
	{
1551
		global $user, $conf, $langs, $mysoc;
1552
1553
		// Clean parameters
1554
		$qty=trim($qty);
1555
		$desc=trim($desc);
1556
		$desc=trim($desc);
1557
		$price = price2num($pu);
1558
		$tvatx = price2num($tvatx);
1559
		$localtax1tx = price2num($localtax1tx);
1560
		$localtax2tx = price2num($localtax2tx);
1561
		$pa_ht=price2num($pa_ht);
1562
		if (empty($fk_fournprice)) $fk_fournprice=0;
1563
1564
		$subprice = $price;
1565
		$remise = 0;
1566
		if (dol_strlen($remise_percent) > 0)
1567
		{
1568
			$remise = round(($pu * $remise_percent / 100), 2);
1569
			$price = $pu - $remise;
1570
		}
1571
		else
1572
		{
1573
			$remise_percent=0;
1574
		}
1575
1576
		dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $date_debut_reel, $date_fin_reel, $tvatx, $localtax1tx, $localtax2tx, $price_base_type, $info_bits");
1577
1578
		$this->db->begin();
1579
1580
		// Calcul du total TTC et de la TVA pour la ligne a partir de
1581
		// qty, pu, remise_percent et tvatx
1582
		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1583
		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1584
1585
		$localtaxes_type=getLocalTaxesFromRate($tvatx, 0, $this->societe, $mysoc);
1586
		$tvatx = preg_replace('/\s*\(.*\)/','',$tvatx);  // Remove code into vatrate.
1587
1588
		$tabprice=calcul_price_total($qty, $pu, $remise_percent, $tvatx, $localtax1tx, $localtax2tx, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1589
		$total_ht  = $tabprice[0];
1590
		$total_tva = $tabprice[1];
1591
		$total_ttc = $tabprice[2];
1592
		$total_localtax1= $tabprice[9];
1593
		$total_localtax2= $tabprice[10];
1594
1595
		$localtax1_type=$localtaxes_type[0];
1596
		$localtax2_type=$localtaxes_type[2];
1597
1598
		// TODO A virer
1599
		// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1600
		$remise = 0;
1601
		$price = price2num(round($pu, 2));
1602
		if (dol_strlen($remise_percent) > 0)
1603
		{
1604
		    $remise = round(($pu * $remise_percent / 100), 2);
1605
		    $price = $pu - $remise;
1606
		}
1607
1608
	    if (empty($pa_ht)) $pa_ht=0;
1609
1610
		// if buy price not defined, define buyprice as configured in margin admin
1611
		if ($this->pa_ht == 0)
1612
		{
1613
			if (($result = $this->defineBuyPrice($pu_ht, $remise_percent)) < 0)
0 ignored issues
show
Bug introduced by
The variable $pu_ht does not exist. Did you forget to declare it?

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

Loading history...
1614
			{
1615
				return $result;
1616
			}
1617
			else
1618
			{
1619
				$pa_ht = $result;
1620
			}
1621
		}
1622
1623
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet set description='".$this->db->escape($desc)."'";
1624
		$sql.= ",price_ht='" .     price2num($price)."'";
1625
		$sql.= ",subprice='" .     price2num($subprice)."'";
1626
		$sql.= ",remise='" .       price2num($remise)."'";
1627
		$sql.= ",remise_percent='".price2num($remise_percent)."'";
1628
		$sql.= ",qty='".$qty."'";
1629
		$sql.= ",tva_tx='".        price2num($tvatx)."'";
1630
		$sql.= ",localtax1_tx='".  price2num($localtax1tx)."'";
1631
		$sql.= ",localtax2_tx='".  price2num($localtax2tx)."'";
1632
		$sql.= ",localtax1_type='".$localtax1_type."'";
1633
		$sql.= ",localtax2_type='".$localtax2_type."'";
1634
		$sql.= ", total_ht='".     price2num($total_ht)."'";
1635
		$sql.= ", total_tva='".    price2num($total_tva)."'";
1636
		$sql.= ", total_localtax1='".price2num($total_localtax1)."'";
1637
		$sql.= ", total_localtax2='".price2num($total_localtax2)."'";
1638
		$sql.= ", total_ttc='".      price2num($total_ttc)."'";
1639
		$sql.= ", fk_product_fournisseur_price=".($fk_fournprice > 0 ? $fk_fournprice : "null");
1640
		$sql.= ", buy_price_ht='".price2num($pa_ht)."'";
1641
		if ($date_start > 0) { $sql.= ",date_ouverture_prevue='".$this->db->idate($date_start)."'"; }
1642
		else { $sql.=",date_ouverture_prevue=null"; }
1643
		if ($date_end > 0) { $sql.= ",date_fin_validite='".$this->db->idate($date_end)."'"; }
1644
		else { $sql.=",date_fin_validite=null"; }
1645
		if ($date_debut_reel > 0) { $sql.= ",date_ouverture='".$this->db->idate($date_debut_reel)."'"; }
1646
		else { $sql.=",date_ouverture=null"; }
1647
		if ($date_fin_reel > 0) { $sql.= ",date_cloture='".$this->db->idate($date_fin_reel)."'"; }
1648
		else { $sql.=",date_cloture=null"; }
1649
		$sql .= ", fk_unit=".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
1650
		$sql .= " WHERE rowid = ".$rowid;
1651
1652
		dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
1653
		$result = $this->db->query($sql);
1654
		if ($result)
1655
		{
1656
			$result=$this->update_statut($user);
1657
			if ($result >= 0)
1658
			{
1659
1660
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1661
				{
1662
					$contractline = new ContratLigne($this->db);
1663
					$contractline->array_options=$array_options;
1664
					$contractline->id= $rowid;
1665
					$result=$contractline->insertExtraFields();
1666
					if ($result < 0)
1667
					{
1668
						$this->error[]=$contractline->error;
1669
						$error++;
0 ignored issues
show
Bug introduced by
The variable $error does not exist. Did you forget to declare it?

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

Loading history...
1670
					}
1671
				}
1672
1673
				if (empty($error)) {
1674
			        // Call trigger
1675
			        $result=$this->call_trigger('LINECONTRACT_UPDATE',$user);
1676
			        if ($result < 0)
1677
			        {
1678
			            $this->db->rollback();
1679
			            return -3;
1680
			        }
1681
			        // End call triggers
1682
1683
					$this->db->commit();
1684
					return 1;
1685
				}
1686
			}
1687
			else
1688
			{
1689
				$this->db->rollback();
1690
				dol_syslog(get_class($this)."::updateligne Erreur -2");
1691
				return -2;
1692
			}
1693
		}
1694
		else
1695
		{
1696
			$this->db->rollback();
1697
			$this->error=$this->db->error();
1698
			dol_syslog(get_class($this)."::updateligne Erreur -1");
1699
			return -1;
1700
		}
1701
	}
1702
1703
	/**
1704
	 *  Delete a contract line
1705
	 *
1706
	 *  @param	int		$idline		Id of line to delete
1707
	 *	@param  User	$user       User that delete
1708
	 *  @return int         		>0 if OK, <0 if KO
1709
	 */
1710
	function deleteline($idline, User $user)
1711
	{
1712
		global $conf, $langs;
1713
1714
		$error=0;
1715
1716
		if ($this->statut >= 0)
1717
		{
1718
1719
		    // Call trigger
1720
		    $result=$this->call_trigger('LINECONTRACT_DELETE',$user);
1721
		    if ($result < 0) return -1;
1722
		    // End call triggers
1723
1724
		    $this->db->begin();
1725
1726
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet";
1727
			$sql.= " WHERE rowid=".$idline;
1728
1729
			dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1730
			$resql = $this->db->query($sql);
1731
			if (! $resql)
1732
			{
1733
				$this->error="Error ".$this->db->lasterror();
1734
				$error++;
1735
			}
1736
1737
			if (empty($error)) {
1738
				// Remove extrafields
1739
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1740
				{
1741
					$contractline = new ContratLigne($this->db);
1742
					$contractline->id= $idline;
1743
					$result=$contractline->deleteExtraFields();
1744
					if ($result < 0)
1745
					{
1746
						$error++;
1747
						$this->error="Error ".get_class($this)."::delete deleteExtraFields error -4 ".$contractline->error;
1748
					}
1749
				}
1750
			}
1751
1752
			if (empty($error)) {
1753
				$this->db->commit();
1754
				return 1;
1755
			} else {
1756
				dol_syslog(get_class($this)."::delete ERROR:".$this->error, LOG_ERR);
1757
				$this->db->rollback();
1758
				return -1;
1759
			}
1760
		}
1761
		else
1762
		{
1763
			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1764
			return -2;
1765
		}
1766
	}
1767
1768
1769
	/**
1770
	 *  Update statut of contract according to services
1771
	 *
1772
	 *	@param	User	$user		Object user
1773
	 *	@return int     			<0 if KO, >0 if OK
1774
	 *  @deprecated					This function will never be used. Status of a contract is status of its lines.
1775
	 */
1776
	function update_statut($user)
1777
	{
1778
		dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING);
1779
1780
		// If draft, we keep it (should not happen)
1781
		if ($this->statut == 0) return 1;
1782
1783
		// Load $this->lines array
1784
		//		$this->fetch_lines();
1785
1786
//		$newstatut=1;
1787
//		foreach($this->lines as $key => $contractline)
1788
//		{
1789
//			//			if ($contractline)         // Loop on each service
1790
//		}
1791
1792
		return 1;
1793
	}
1794
1795
1796
	/**
1797
	 *  Return label of a contract status
1798
	 *
1799
	 *  @param	int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Long label of all services, 5=Libelle court + Picto, 6=Picto of all services, 7=Same than 6 with fixed length
1800
	 *  @return string      		Label
1801
	 */
1802
	function getLibStatut($mode)
1803
	{
1804
		return $this->LibStatut($this->statut,$mode);
1805
	}
1806
1807
	/**
1808
	 *  Renvoi label of a given contrat status
1809
	 *
1810
	 *  @param	int		$statut      	Status id
1811
	 *  @param  int		$mode          	0=Long label, 1=Short label, 2=Picto + Libelle court, 3=Picto, 4=Picto + Long label of all services, 5=Libelle court + Picto, 6=Picto of all services, 7=Same than 6 with fixed length
1812
	 *	@return string      			Label
1813
	 */
1814
	function LibStatut($statut,$mode)
1815
	{
1816
		global $langs;
1817
		$langs->load("contracts");
1818
		if ($mode == 0)
1819
		{
1820
			if ($statut == 0) { return $langs->trans("ContractStatusDraft"); }
1821
			if ($statut == 1) { return $langs->trans("ContractStatusValidated"); }
1822
			if ($statut == 2) { return $langs->trans("ContractStatusClosed"); }
1823
		}
1824
		if ($mode == 1)
1825
		{
1826
			if ($statut == 0) { return $langs->trans("ContractStatusDraft"); }
1827
			if ($statut == 1) { return $langs->trans("ContractStatusValidated"); }
1828
			if ($statut == 2) { return $langs->trans("ContractStatusClosed"); }
1829
		}
1830
		if ($mode == 2)
1831
		{
1832
			if ($statut == 0) { return img_picto($langs->trans('ContractStatusDraft'),'statut0').' '.$langs->trans("ContractStatusDraft"); }
1833
			if ($statut == 1) { return img_picto($langs->trans('ContractStatusValidated'),'statut4').' '.$langs->trans("ContractStatusValidated"); }
1834
			if ($statut == 2) { return img_picto($langs->trans('ContractStatusClosed'),'statut6').' '.$langs->trans("ContractStatusClosed"); }
1835
		}
1836
		if ($mode == 3)
1837
		{
1838
			if ($statut == 0) { return img_picto($langs->trans('ContractStatusDraft'),'statut0'); }
1839
			if ($statut == 1) { return img_picto($langs->trans('ContractStatusValidated'),'statut4'); }
1840
			if ($statut == 2) { return img_picto($langs->trans('ContractStatusClosed'),'statut6'); }
1841
		}
1842
		if ($mode == 4 || $mode == 6 || $mode == 7)
1843
		{
1844
			$line=new ContratLigne($this->db);
1845
			$text='';
1846
			if ($mode == 4)
1847
			{
1848
				$text ='<span class="hideonsmartphone">';
1849
				$text.=($this->nbofserviceswait+$this->nbofservicesopened+$this->nbofservicesexpired+$this->nbofservicesclosed);
1850
				$text.=' '.$langs->trans("Services");
1851
				$text.=': &nbsp; &nbsp; ';
1852
				$text.='</span>';
1853
			}
1854
			$text.=($mode == 7?'<div class="inline-block">':'');
1855
			$text.=($mode != 7 || $this->nbofserviceswait > 0) ? ($this->nbofserviceswait.$line->LibStatut(0,3,-1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesopened || $this->nbofservicesexpired || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1856
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1857
			$text.=($mode != 7 || $this->nbofservicesopened > 0) ? ($this->nbofservicesopened.$line->LibStatut(4,3,0,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesexpired || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1858
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1859
			$text.=($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.$line->LibStatut(4,3,1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1860
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1861
			$text.=($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.$line->LibStatut(5,3,-1,'class="paddingleft2 inline-block valigntextbottom"')) : '';
1862
			$text.=($mode == 7?'</div>':'');
1863
			return $text;
1864
		}
1865
		if ($mode == 5)
1866
		{
1867
			if ($statut == 0) { return $langs->trans("ContractStatusDraft").' '.img_picto($langs->trans('ContractStatusDraft'),'statut0'); }
1868
			if ($statut == 1) { return $langs->trans("ContractStatusValidated").' '.img_picto($langs->trans('ContractStatusValidated'),'statut4'); }
1869
			if ($statut == 2) { return $langs->trans("ContractStatusClosed").' '.img_picto($langs->trans('ContractStatusClosed'),'statut6'); }
1870
		}
1871
	}
1872
1873
1874
	/**
1875
	 *	Return clicable name (with picto eventually)
1876
	 *
1877
	 *	@param	int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
1878
	 *	@param	int		$maxlength					Max length of ref
1879
     *  @param	int     $notooltip					1=Disable tooltip
1880
     *  @param  int     $save_lastsearch_value		-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1881
	 *	@return	string								Chaine avec URL
1882
	 */
1883
	function getNomUrl($withpicto=0, $maxlength=0, $notooltip=0, $save_lastsearch_value=-1)
1884
	{
1885
		global $conf, $langs, $user;
1886
1887
		$result='';
1888
1889
		$url = DOL_URL_ROOT.'/contrat/card.php?id='.$this->id;
1890
1891
		//if ($option !== 'nolink')
1892
		//{
1893
			// Add param to save lastsearch_values or not
1894
			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1895
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1896
			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1897
		//}
1898
1899
        $label = '';
1900
1901
        if ($user->rights->contrat->lire) {
1902
            $label = '<u>'.$langs->trans("ShowContract").'</u>';
1903
            $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1904
            $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
1905
            $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
1906
            if (!empty($this->total_ht)) {
1907
                $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1908
            }
1909
            if (!empty($this->total_tva)) {
1910
                $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1,	$conf->currency);
1911
            }
1912
            if (!empty($this->total_ttc)) {
1913
                $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1914
            }
1915
        }
1916
1917
        $linkclose='';
1918
        if (empty($notooltip) && $user->rights->contrat->lire)
1919
        {
1920
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1921
            {
1922
                $label=$langs->trans("ShowOrder");
1923
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1924
            }
1925
            $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1926
            $linkclose.=' class="classfortooltip"';
1927
        }
1928
1929
		$linkstart = '<a href="'.$url.'"';
1930
		$linkstart.=$linkclose.'>';
1931
		$linkend='</a>';
1932
1933
		$result .= $linkstart;
1934
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1935
		if ($withpicto != 2) $result.= $this->ref;
1936
		$result .= $linkend;
1937
1938
		return $result;
1939
	}
1940
1941
	/**
1942
	 *  Charge les informations d'ordre info dans l'objet contrat
1943
	 *
1944
	 *  @param  int		$id     id du contrat a charger
1945
	 *  @return	void
1946
	 */
1947
	function info($id)
1948
	{
1949
		$sql = "SELECT c.rowid, c.ref, c.datec, c.date_cloture,";
1950
		$sql.= " c.tms as date_modification,";
1951
		$sql.= " fk_user_author, fk_user_cloture";
1952
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
1953
		$sql.= " WHERE c.rowid = ".$id;
1954
1955
		$result=$this->db->query($sql);
1956
		if ($result)
1957
		{
1958
			if ($this->db->num_rows($result))
1959
			{
1960
				$obj = $this->db->fetch_object($result);
1961
1962
				$this->id = $obj->rowid;
1963
1964
				if ($obj->fk_user_author) {
1965
					$cuser = new User($this->db);
1966
					$cuser->fetch($obj->fk_user_author);
1967
					$this->user_creation     = $cuser;
1968
				}
1969
1970
				if ($obj->fk_user_cloture) {
1971
					$cuser = new User($this->db);
1972
					$cuser->fetch($obj->fk_user_cloture);
1973
					$this->user_cloture = $cuser;
1974
				}
1975
				$this->ref			     = (! $obj->ref) ? $obj->rowid : $obj->ref;
1976
				$this->date_creation     = $this->db->jdate($obj->datec);
1977
				$this->date_modification = $this->db->jdate($obj->date_modification);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_modification) can also be of type string. However, the property $date_modification is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1978
				$this->date_cloture      = $this->db->jdate($obj->date_cloture);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_cloture) can also be of type string. However, the property $date_cloture is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1979
			}
1980
1981
			$this->db->free($result);
1982
1983
		}
1984
		else
1985
		{
1986
			dol_print_error($this->db);
1987
		}
1988
	}
1989
1990
	/**
1991
	 *  Return list of line rowid
1992
	 *
1993
	 *  @param	int		$statut     Status of lines to get
1994
	 *  @return array       		Array of line's rowid
1995
	 */
1996
	function array_detail($statut=-1)
1997
	{
1998
		$tab=array();
1999
2000
		$sql = "SELECT cd.rowid";
2001
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as cd";
2002
		$sql.= " WHERE fk_contrat =".$this->id;
2003
		if ($statut >= 0) $sql.= " AND statut = '$statut'";
2004
2005
		dol_syslog(get_class($this)."::array_detail()", LOG_DEBUG);
2006
		$resql=$this->db->query($sql);
2007
		if ($resql)
2008
		{
2009
			$num=$this->db->num_rows($resql);
2010
			$i=0;
2011
			while ($i < $num)
2012
			{
2013
				$obj = $this->db->fetch_object($resql);
2014
				$tab[$i]=$obj->rowid;
2015
				$i++;
2016
			}
2017
			return $tab;
2018
		}
2019
		else
2020
		{
2021
			$this->error=$this->db->error();
2022
			return -1;
2023
		}
2024
	}
2025
2026
	/**
2027
	 *  Return list of other contracts for same company than current contract
2028
	 *
2029
	 *	@param	string		$option		'all' or 'others'
2030
	 *  @return array   				Array of contracts id
2031
	 */
2032
	function getListOfContracts($option='all')
2033
	{
2034
		$tab=array();
2035
2036
		$sql = "SELECT c.rowid, c.ref";
2037
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2038
		$sql.= " WHERE fk_soc =".$this->socid;
2039
		if ($option == 'others') $sql.= " AND c.rowid != ".$this->id;
2040
2041
		dol_syslog(get_class($this)."::getOtherContracts()", LOG_DEBUG);
2042
		$resql=$this->db->query($sql);
2043
		if ($resql)
2044
		{
2045
			$num=$this->db->num_rows($resql);
2046
			$i=0;
2047
			while ($i < $num)
2048
			{
2049
				$obj = $this->db->fetch_object($resql);
2050
				$contrat=new Contrat($this->db);
2051
				$contrat->fetch($obj->rowid);
2052
				$tab[]=$contrat;
2053
				$i++;
2054
			}
2055
			return $tab;
2056
		}
2057
		else
2058
		{
2059
			$this->error=$this->db->error();
2060
			return -1;
2061
		}
2062
	}
2063
2064
2065
	/**
2066
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2067
     *
2068
     *      @param	User	$user           Objet user
2069
     *      @param  string	$mode           "inactive" pour services a activer, "expired" pour services expires
2070
     *      @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
2071
	 */
2072
	function load_board($user,$mode)
2073
	{
2074
		global $conf, $langs;
2075
2076
		$this->from = " FROM ".MAIN_DB_PREFIX."contrat as c";
2077
		$this->from.= ", ".MAIN_DB_PREFIX."contratdet as cd";
2078
		$this->from.= ", ".MAIN_DB_PREFIX."societe as s";
2079
		if (!$user->rights->societe->client->voir && !$user->societe_id) $this->from.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2080
2081
		if ($mode == 'inactives')
2082
		{
2083
			$sql = "SELECT cd.rowid, cd.date_ouverture_prevue as datefin";
2084
			$sql.= $this->from;
2085
			$sql.= " WHERE c.statut = 1";
2086
			$sql.= " AND c.rowid = cd.fk_contrat";
2087
			$sql.= " AND cd.statut = 0";
2088
		}
2089
		if ($mode == 'expired')
2090
		{
2091
			$sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2092
			$sql.= $this->from;
2093
			$sql.= " WHERE c.statut = 1";
2094
			$sql.= " AND c.rowid = cd.fk_contrat";
2095
			$sql.= " AND cd.statut = 4";
2096
			$sql.= " AND cd.date_fin_validite < '".$this->db->idate(time())."'";
2097
		}
2098
		$sql.= " AND c.fk_soc = s.rowid";
0 ignored issues
show
Bug introduced by
The variable $sql does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2099
		$sql.= " AND c.entity = ".$conf->entity;
2100
		if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
2101
		if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id;
2102
		$resql=$this->db->query($sql);
2103
		if ($resql)
2104
		{
2105
			$langs->load("contracts");
2106
			$now=dol_now();
2107
2108
			if ($mode == 'inactives') {
2109
				$warning_delay = $conf->contrat->services->inactifs->warning_delay;
2110
				$label = $langs->trans("BoardNotActivatedServices");
2111
				$url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&amp;leftmenu=contracts&amp;mode=0';
2112
			} else {
2113
				$warning_delay = $conf->contrat->services->expires->warning_delay;
2114
				$url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&amp;leftmenu=contracts&amp;mode=4&amp;filter=expired';
2115
				$label = $langs->trans("BoardRunningServices");
2116
			}
2117
2118
			$response = new WorkboardResponse();
2119
			$response->warning_delay = $warning_delay/60/60/24;
2120
			$response->label = $label;
2121
			$response->url = $url;
2122
			$response->img = img_object('',"contract");
2123
2124
			while ($obj=$this->db->fetch_object($resql))
2125
			{
2126
				$response->nbtodo++;
2127
2128
				if ($obj->datefin && $this->db->jdate($obj->datefin) < ($now - $warning_delay)) {
2129
					$response->nbtodolate++;
2130
				}
2131
			}
2132
2133
			return $response;
2134
		}
2135
		else
2136
		{
2137
			dol_print_error($this->db);
2138
			$this->error=$this->db->error();
2139
			return -1;
2140
		}
2141
	}
2142
2143
	/**
2144
	 *   Charge indicateurs this->nb de tableau de bord
2145
	 *
2146
	 *   @return     int         <0 si ko, >0 si ok
2147
	 */
2148
	function load_state_board()
2149
	{
2150
		global $conf, $user;
2151
2152
		$this->nb=array();
2153
		$clause = "WHERE";
2154
2155
		$sql = "SELECT count(c.rowid) as nb";
2156
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2157
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
2158
		if (!$user->rights->societe->client->voir && !$user->societe_id)
2159
		{
2160
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2161
			$sql.= " WHERE sc.fk_user = " .$user->id;
2162
			$clause = "AND";
2163
		}
2164
		$sql.= " ".$clause." c.entity = ".$conf->entity;
2165
2166
		$resql=$this->db->query($sql);
2167
		if ($resql)
2168
		{
2169
			while ($obj=$this->db->fetch_object($resql))
2170
			{
2171
				$this->nb["Contracts"]=$obj->nb;
2172
			}
2173
            $this->db->free($resql);
2174
			return 1;
2175
		}
2176
		else
2177
		{
2178
			dol_print_error($this->db);
2179
			$this->error=$this->db->error();
2180
			return -1;
2181
		}
2182
	}
2183
2184
2185
	/* gestion des contacts d'un contrat */
2186
2187
	/**
2188
	 *  Return id des contacts clients de facturation
2189
	 *
2190
	 *  @return     array       Liste des id contacts facturation
2191
	 */
2192
	function getIdBillingContact()
2193
	{
2194
		return $this->getIdContact('external','BILLING');
2195
	}
2196
2197
	/**
2198
	 *  Return id des contacts clients de prestation
2199
	 *
2200
	 *  @return     array       Liste des id contacts prestation
2201
	 */
2202
	function getIdServiceContact()
2203
	{
2204
		return $this->getIdContact('external','SERVICE');
2205
	}
2206
2207
2208
	/**
2209
     *  Initialise an instance with random values.
2210
     *  Used to build previews or test instances.
2211
     *	id must be 0 if object instance is a specimen.
2212
     *
2213
     *  @return	void
2214
	 */
2215
	function initAsSpecimen()
2216
	{
2217
		global $user,$langs,$conf;
2218
2219
        // Load array of products prodids
2220
		$num_prods = 0;
2221
		$prodids = array();
2222
		$sql = "SELECT rowid";
2223
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
2224
		$sql.= " WHERE entity IN (".getEntity('product').")";
2225
		$sql.= " AND tosell = 1";
2226
		$resql = $this->db->query($sql);
2227
		if ($resql)
2228
		{
2229
			$num_prods = $this->db->num_rows($resql);
2230
			$i = 0;
2231
			while ($i < $num_prods)
2232
			{
2233
				$i++;
2234
				$row = $this->db->fetch_row($resql);
2235
				$prodids[$i] = $row[0];
2236
			}
2237
		}
2238
2239
		// Initialise parametres
2240
		$this->id=0;
2241
		$this->specimen=1;
2242
2243
		$this->ref = 'SPECIMEN';
2244
		$this->ref_customer = 'SPECIMENCUST';
2245
		$this->ref_supplier = 'SPECIMENSUPP';
2246
		$this->socid = 1;
2247
		$this->statut= 0;
2248
		$this->date_creation = (dol_now() - 3600 * 24 * 7);
2249
		$this->date_contrat = dol_now();
2250
		$this->commercial_signature_id = 1;
2251
		$this->commercial_suivi_id = 1;
2252
		$this->note_private='This is a comment (private)';
2253
		$this->note_public='This is a comment (public)';
2254
		$this->fk_projet = 0;
2255
		// Lines
2256
		$nbp = 5;
2257
		$xnbp = 0;
2258
		while ($xnbp < $nbp)
2259
		{
2260
			$line=new ContratLigne($this->db);
2261
			$line->qty=1;
2262
			$line->subprice=100;
2263
			$line->price=100;
2264
			$line->tva_tx=19.6;
2265
			$line->remise_percent=10;
2266
			$line->total_ht=90;
2267
			$line->total_ttc=107.64;	// 90 * 1.196
2268
			$line->total_tva=17.64;
2269
			$line->date_start = dol_now() - 500000;
2270
			$line->date_start_real = dol_now() - 200000;
2271
			$line->date_end = dol_now() + 500000;
2272
			$line->date_end_real = dol_now() - 100000;
2273
			if ($num_prods > 0)
2274
            {
2275
				$prodid = mt_rand(1, $num_prods);
2276
				$line->fk_product=$prodids[$prodid];
2277
            }
2278
			$this->lines[$xnbp]=$line;
2279
			$xnbp++;
2280
		}
2281
	}
2282
2283
	/**
2284
	 * 	Create an array of order lines
2285
	 *
2286
	 * 	@return int		>0 if OK, <0 if KO
2287
	 */
2288
	function getLinesArray()
2289
	{
2290
		return $this->fetch_lines();
2291
	}
2292
2293
2294
	/**
2295
	 *  Create a document onto disk according to template module.
2296
	 *
2297
	 * 	@param	    string		$modele			Force model to use ('' to not force)
2298
	 * 	@param		Translate	$outputlangs	Object langs to use for output
2299
	 *  @param      int			$hidedetails    Hide details of lines
2300
	 *  @param      int			$hidedesc       Hide description
2301
	 *  @param      int			$hideref        Hide ref
2302
	 * 	@return     int         				0 if KO, 1 if OK
2303
	 */
2304
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
2305
	{
2306
		global $conf,$langs;
2307
2308
		$langs->load("contracts");
2309
2310
		if (! dol_strlen($modele)) {
2311
2312
			$modele = 'strato';
2313
2314
			if ($this->modelpdf) {
2315
				$modele = $this->modelpdf;
2316
			} elseif (! empty($conf->global->CONTRACT_ADDON_PDF)) {
2317
				$modele = $conf->global->CONTRACT_ADDON_PDF;
2318
			}
2319
		}
2320
2321
		$modelpath = "core/modules/contract/doc/";
2322
2323
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2324
	}
2325
2326
	/**
2327
	 * Function used to replace a thirdparty id with another one.
2328
	 *
2329
	 * @param DoliDB $db Database handler
2330
	 * @param int $origin_id Old thirdparty id
2331
	 * @param int $dest_id New thirdparty id
2332
	 * @return bool
2333
	 */
2334
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2335
	{
2336
		$tables = array(
2337
			'contrat'
2338
		);
2339
2340
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2341
	}
2342
2343
	/**
2344
	 * Load an object from its id and create a new one in database
2345
	 *
2346
	 * @param int $socid Id of thirdparty
2347
	 * @param int $notrigger	1=Does not execute triggers, 0= execute triggers
2348
	 * @return int New id of clone
2349
	 */
2350
	function createFromClone($socid = 0, $notrigger=0) {
2351
		global $db, $user, $langs, $conf, $hookmanager;
2352
2353
		dol_include_once('/projet/class/project.class.php');
2354
2355
		$this->context['createfromclone'] = 'createfromclone';
2356
2357
		$error = 0;
2358
2359
		$this->fetch($this->id);
2360
		// Load dest object
2361
		$clonedObj = clone $this;
2362
        $clonedObj->socid = $socid;
2363
2364
		$this->db->begin();
2365
2366
		$objsoc = new Societe($this->db);
2367
2368
		$objsoc->fetch($clonedObj->socid);
2369
2370
		// $clonedObj->id=0;
2371
		$clonedObj->statut = 0;
2372
2373
		if (empty($conf->global->CONTRACT_ADDON) || ! is_readable(DOL_DOCUMENT_ROOT . "/core/modules/contract/" . $conf->global->CONTRACT_ADDON . ".php")) {
2374
			$this->error = 'ErrorSetupNotComplete';
2375
			dol_syslog($this->error);
2376
			return - 1;
2377
		}
2378
2379
		// Set ref
2380
		require_once DOL_DOCUMENT_ROOT . "/core/modules/contract/" . $conf->global->CONTRACT_ADDON . '.php';
2381
		$obj = $conf->global->CONTRACT_ADDON;
2382
		$modContract = new $obj();
2383
		$clonedObj->ref = $modContract->getNextValue($objsoc, $clonedObj);
2384
2385
		// get extrafields so they will be clone
2386
		foreach ( $this->lines as $line ) {
2387
			$line->fetch_optionals($line->rowid);
2388
		}
2389
2390
		// Create clone
2391
		$result = $clonedObj->create($user);
2392
		if ($result < 0) {
2393
			$error ++;
2394
			$this->error = $clonedObj->error;
2395
			$this->errors[] = $clonedObj->error;
2396
		} else {
2397
            // copy external contacts if same company
2398
            if ($this->socid == $clonedObj->socid) {
2399
                if ($clonedObj->copy_linked_contact($this, 'external') < 0) {
2400
                    $error++;
2401
                }
2402
            }
2403
        }
2404
2405
		if (! $error) {
2406
			foreach ( $this->lines as $line ) {
2407
				$result = $clonedObj->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, $line->date_ouverture, $line->date_cloture, 'HT', 0, $line->info_bits, $line->fk_fournprice, $line->pa_ht, $line->array_options, $line->fk_unit);
2408
				if ($result < 0) {
2409
					$error ++;
2410
					$this->error = $clonedObj->error;
2411
					$this->errors[] = $clonedObj->error;
2412
				}
2413
			}
2414
		}
2415
2416
		if (! $error) {
2417
			// Hook of thirdparty module
2418
			if (is_object($hookmanager)) {
2419
				$parameters = array (
2420
						'objFrom' => $this,
2421
						'clonedObj' => $clonedObj
2422
				);
2423
				$action = '';
2424
				$reshook = $hookmanager->executeHooks('createFrom', $parameters, $clonedObj, $action); // Note that $action and $object may have been modified by some hooks
2425
				if ($reshook < 0)
2426
					$error ++;
2427
			}
2428
2429
		}
2430
2431
		if (! $notrigger && empty($error))
2432
		{
2433
			// Call trigger
2434
			$clonedObj->old_copy=$this;
2435
			$result = $clonedObj->call_trigger('CONTRACT_CLONE', $user);
2436
			if ($result < 0) {
2437
				$error ++;
2438
			}
2439
			// End call triggers
2440
		}
2441
2442
		unset($this->context['createfromclone']);
2443
2444
		// End
2445
		if (! $error) {
2446
			$this->db->commit();
2447
			return $clonedObj->id;
2448
		} else {
2449
			$this->db->rollback();
2450
			return - 1;
2451
		}
2452
	}
2453
}
2454
2455
2456
/**
2457
 *	Classe permettant la gestion des lignes de contrats
2458
 */
2459
class ContratLigne extends CommonObjectLine
2460
{
2461
    public $element='contratdet';
2462
    public $table_element='contratdet';
2463
2464
	var $id;
2465
	var $ref;
2466
	var $tms;
2467
2468
	var $fk_contrat;
2469
	var $fk_product;
2470
	var $statut;					// 0 inactive, 4 active, 5 closed
2471
	var $type;                     // 0 for product, 1 for service
2472
	var $label;
2473
	/**
2474
	 * @var string
2475
	 * @deprecated Use $label instead
2476
	 * @see label
2477
	 */
2478
	public $libelle;
2479
2480
	var $description;
2481
2482
	var $product_ref;
2483
	var $product_label;
2484
2485
	var $date_commande;
2486
2487
	var $date_start;				// date start planned
2488
	var $date_start_real;			// date start real
2489
	var $date_end;					// date end planned
2490
	var $date_end_real;				// date end real
2491
	// For backward compatibility
2492
	var $date_ouverture_prevue;		// date start planned
2493
	var $date_ouverture;			// date start real
2494
	var $date_fin_validite;			// date end planned
2495
	var $date_cloture;				// date end real
2496
	var $tva_tx;
2497
	var $localtax1_tx;
2498
	var $localtax2_tx;
2499
	var $localtax1_type;	// Local tax 1 type
2500
	var $localtax2_type;	// Local tax 2 type
2501
	var $qty;
2502
	var $remise_percent;
2503
	var $remise;
2504
	var $fk_remise_except;
2505
2506
	var $subprice;					// Unit price HT
2507
2508
	/**
2509
	 * @var float
2510
	 * @deprecated Use $price_ht instead
2511
	 * @see price_ht
2512
	 */
2513
	public $price;
2514
2515
	var $price_ht;
2516
2517
	var $total_ht;
2518
	var $total_tva;
2519
	var $total_localtax1;
2520
	var $total_localtax2;
2521
	var $total_ttc;
2522
2523
	var $fk_fournprice;
2524
	var $pa_ht;
2525
2526
	var $info_bits;
2527
	var $fk_user_author;
2528
	var $fk_user_ouverture;
2529
	var $fk_user_cloture;
2530
	var $commentaire;
2531
2532
2533
	/**
2534
     *  Constructor
2535
     *
2536
     *  @param      DoliDb		$db      Database handler
2537
	 */
2538
	function __construct($db)
2539
	{
2540
		$this->db = $db;
2541
	}
2542
2543
2544
	/**
2545
	 *  Return label of this contract line status
2546
	 *
2547
	 *	@param	int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
2548
	 *  @return string      		Libelle
2549
	 */
2550
	function getLibStatut($mode)
2551
	{
2552
		return $this->LibStatut($this->statut,$mode,((! empty($this->date_fin_validite))?($this->date_fin_validite < dol_now()?1:0):-1));
2553
	}
2554
2555
	/**
2556
	 *  Return label of a contract line status
2557
	 *
2558
	 *  @param	int		$statut     Id statut
2559
	 *	@param  int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
2560
	 *	@param	int		$expired	0=Not expired, 1=Expired, -1=Both or unknown
2561
	 *  @param	string	$moreatt	More attribute
2562
	 *  @return string      		Libelle
2563
	 */
2564
	function LibStatut($statut,$mode,$expired=-1,$moreatt='')
2565
	{
2566
		global $langs;
2567
		$langs->load("contracts");
2568
		if ($mode == 0)
2569
		{
2570
			if ($statut == 0) { return $langs->trans("ServiceStatusInitial"); }
2571
			if ($statut == 4 && $expired == -1) { return $langs->trans("ServiceStatusRunning"); }
2572
			if ($statut == 4 && $expired == 0)  { return $langs->trans("ServiceStatusNotLate"); }
2573
			if ($statut == 4 && $expired == 1)  { return $langs->trans("ServiceStatusLate"); }
2574
			if ($statut == 5) { return $langs->trans("ServiceStatusClosed");  }
2575
		}
2576
		if ($mode == 1)
2577
		{
2578
			if ($statut == 0) { return $langs->trans("ServiceStatusInitial"); }
2579
			if ($statut == 4 && $expired == -1) { return $langs->trans("ServiceStatusRunning"); }
2580
			if ($statut == 4 && $expired == 0)  { return $langs->trans("ServiceStatusNotLateShort"); }
2581
			if ($statut == 4 && $expired == 1)  { return $langs->trans("ServiceStatusLateShort"); }
2582
			if ($statut == 5) { return $langs->trans("ServiceStatusClosed");  }
2583
		}
2584
		if ($mode == 2)
2585
		{
2586
			if ($statut == 0) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); }
2587
			if ($statut == 4 && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); }
2588
			if ($statut == 4 && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLateShort"); }
2589
			if ($statut == 4 && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLateShort"); }
2590
			if ($statut == 5) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); }
2591
		}
2592
		if ($mode == 3)
2593
		{
2594
			if ($statut == 0) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0',$moreatt); }
2595
			if ($statut == 4 && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4',$moreatt); }
2596
			if ($statut == 4 && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4',$moreatt); }
2597
			if ($statut == 4 && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'),'statut3',$moreatt); }
2598
			if ($statut == 5) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6',$moreatt); }
2599
		}
2600
		if ($mode == 4)
2601
		{
2602
			if ($statut == 0) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); }
2603
			if ($statut == 4 && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); }
2604
			if ($statut == 4 && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLate"); }
2605
			if ($statut == 4 && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLate"); }
2606
			if ($statut == 5) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); }
2607
		}
2608
		if ($mode == 5)
2609
		{
2610
			if ($statut == 0) { return $langs->trans("ServiceStatusInitial").' '.img_picto($langs->trans('ServiceStatusInitial'),'statut0'); }
2611
			if ($statut == 4 && $expired == -1) { return $langs->trans("ServiceStatusRunning").' '.img_picto($langs->trans('ServiceStatusRunning'),'statut4'); }
2612
			if ($statut == 4 && $expired == 0)  { return $langs->trans("ServiceStatusNotLateShort").' '.img_picto($langs->trans('ServiceStatusNotLateShort'),'statut4'); }
2613
			if ($statut == 4 && $expired == 1)  { return $langs->trans("ServiceStatusLateShort").' '.img_picto($langs->trans('ServiceStatusLate'),'statut3'); }
2614
			if ($statut == 5) { return $langs->trans("ServiceStatusClosed").' '.img_picto($langs->trans('ServiceStatusClosed'),'statut6'); }
2615
		}
2616
	}
2617
2618
	/**
2619
	 *	Return clicable name (with picto eventually)
2620
	 *
2621
	 *  @param	int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
2622
	 *  @param	int		$maxlength		Max length
2623
	 *  @return	string					Chaine avec URL
2624
 	 */
2625
	function getNomUrl($withpicto=0,$maxlength=0)
2626
	{
2627
		global $langs;
2628
2629
		$result='';
2630
        $label=$langs->trans("ShowContractOfService").': '.$this->label;
2631
        if (empty($label)) $label=$this->description;
2632
2633
        $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
2634
		$linkend='</a>';
2635
2636
		$picto='service';
2637
		if ($this->type == 0) $picto='product';
2638
2639
        if ($withpicto) $result.=($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
2640
		if ($withpicto && $withpicto != 2) $result.=' ';
2641
		if ($withpicto != 2) $result.=$link.($this->product_ref?$this->product_ref.' ':'').($this->label?$this->label:$this->description).$linkend;
2642
		return $result;
2643
	}
2644
2645
	/**
2646
	 *    	Load object in memory from database
2647
	 *
2648
	 *    	@param	int		$id         Id object
2649
	 * 		@param	string	$ref		Ref of contract
2650
	 *    	@return int         		<0 if KO, >0 if OK
2651
	 */
2652
	function fetch($id, $ref='')
2653
	{
2654
2655
		// Check parameters
2656
		if (empty($id) && empty($ref)) return -1;
2657
2658
		$sql = "SELECT";
2659
		$sql.= " t.rowid,";
2660
2661
		$sql.= " t.tms,";
2662
		$sql.= " t.fk_contrat,";
2663
		$sql.= " t.fk_product,";
2664
		$sql.= " t.statut,";
2665
		$sql.= " t.label,";			// This field is not used. Only label of product
2666
		$sql.= " p.ref as product_ref,";
2667
		$sql.= " p.label as product_label,";
2668
		$sql.= " p.description as product_desc,";
2669
		$sql.= " p.fk_product_type as product_type,";
2670
		$sql.= " t.description,";
2671
		$sql.= " t.date_commande,";
2672
		$sql.= " t.date_ouverture_prevue as date_ouverture_prevue,";
2673
		$sql.= " t.date_ouverture as date_ouverture,";
2674
		$sql.= " t.date_fin_validite as date_fin_validite,";
2675
		$sql.= " t.date_cloture as date_cloture,";
2676
		$sql.= " t.tva_tx,";
2677
		$sql.= " t.vat_src_code,";
2678
		$sql.= " t.localtax1_tx,";
2679
		$sql.= " t.localtax2_tx,";
2680
		$sql.= " t.localtax1_type,";
2681
		$sql.= " t.localtax2_type,";
2682
		$sql.= " t.qty,";
2683
		$sql.= " t.remise_percent,";
2684
		$sql.= " t.remise,";
2685
		$sql.= " t.fk_remise_except,";
2686
		$sql.= " t.subprice,";
2687
		$sql.= " t.price_ht,";
2688
		$sql.= " t.total_ht,";
2689
		$sql.= " t.total_tva,";
2690
		$sql.= " t.total_localtax1,";
2691
		$sql.= " t.total_localtax2,";
2692
		$sql.= " t.total_ttc,";
2693
		$sql.= " t.fk_product_fournisseur_price as fk_fournprice,";
2694
		$sql.= " t.buy_price_ht as pa_ht,";
2695
		$sql.= " t.info_bits,";
2696
		$sql.= " t.fk_user_author,";
2697
		$sql.= " t.fk_user_ouverture,";
2698
		$sql.= " t.fk_user_cloture,";
2699
		$sql.= " t.commentaire,";
2700
		$sql.= " t.fk_unit";
2701
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
2702
		if ($id)  $sql.= " WHERE t.rowid = ".$id;
2703
		if ($ref) $sql.= " WHERE t.rowid = '".$this->db->escape($ref)."'";
2704
2705
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2706
		$resql=$this->db->query($sql);
2707
		if ($resql)
2708
		{
2709
			if ($this->db->num_rows($resql))
2710
			{
2711
				$obj = $this->db->fetch_object($resql);
2712
2713
				$this->id    = $obj->rowid;
2714
				$this->ref   = $obj->rowid;
2715
2716
				$this->tms = $this->db->jdate($obj->tms);
2717
				$this->fk_contrat = $obj->fk_contrat;
2718
				$this->fk_product = $obj->fk_product;
2719
				$this->statut = $obj->statut;
2720
				$this->product_ref = $obj->product_ref;
2721
				$this->product_label = $obj->product_label;
2722
				$this->product_description = $obj->product_description;
2723
				$this->product_type = $obj->product_type;
2724
				$this->label = $obj->label;					// deprecated. We do not use this field. Only ref and label of product, and description of contract line
2725
				$this->description = $obj->description;
2726
				$this->date_commande = $this->db->jdate($obj->date_commande);
2727
2728
				$this->date_start = $this->db->jdate($obj->date_ouverture_prevue);
2729
				$this->date_start_real = $this->db->jdate($obj->date_ouverture);
2730
				$this->date_end = $this->db->jdate($obj->date_fin_validite);
2731
				$this->date_end_real = $this->db->jdate($obj->date_cloture);
2732
				// For backward compatibility
2733
				$this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
2734
				$this->date_ouverture = $this->db->jdate($obj->date_ouverture);
2735
				$this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
2736
				$this->date_cloture = $this->db->jdate($obj->date_cloture);
2737
2738
				$this->tva_tx = $obj->tva_tx;
2739
				$this->vat_src_code = $obj->vat_src_code;
2740
				$this->localtax1_tx = $obj->localtax1_tx;
2741
				$this->localtax2_tx = $obj->localtax2_tx;
2742
				$this->localtax1_type = $obj->localtax1_type;
2743
				$this->localtax2_type = $obj->localtax2_type;
2744
				$this->qty = $obj->qty;
2745
				$this->remise_percent = $obj->remise_percent;
2746
				$this->remise = $obj->remise;
2747
				$this->fk_remise_except = $obj->fk_remise_except;
2748
				$this->subprice = $obj->subprice;
2749
				$this->price_ht = $obj->price_ht;
2750
				$this->total_ht = $obj->total_ht;
2751
				$this->total_tva = $obj->total_tva;
2752
				$this->total_localtax1 = $obj->total_localtax1;
2753
				$this->total_localtax2 = $obj->total_localtax2;
2754
				$this->total_ttc = $obj->total_ttc;
2755
				$this->info_bits = $obj->info_bits;
2756
				$this->fk_user_author = $obj->fk_user_author;
2757
				$this->fk_user_ouverture = $obj->fk_user_ouverture;
2758
				$this->fk_user_cloture = $obj->fk_user_cloture;
2759
				$this->commentaire = $obj->commentaire;
2760
				$this->fk_fournprice = $obj->fk_fournprice;
2761
				$marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
2762
				$this->pa_ht = $marginInfos[0];
2763
				$this->fk_unit     = $obj->fk_unit;
2764
2765
			}
2766
			$this->db->free($resql);
2767
2768
			return 1;
2769
		}
2770
		else
2771
		{
2772
			$this->error="Error ".$this->db->lasterror();
2773
			return -1;
2774
		}
2775
	}
2776
2777
2778
	/**
2779
	 *      Update database for contract line
2780
	 *
2781
	 *      @param	User	$user        	User that modify
2782
	 *      @param  int		$notrigger	    0=no, 1=yes (no update trigger)
2783
	 *      @return int         			<0 if KO, >0 if OK
2784
	 */
2785
	function update($user, $notrigger=0)
2786
	{
2787
		global $conf, $langs, $mysoc;
2788
2789
		$error=0;
2790
2791
		// Clean parameters
2792
		$this->fk_contrat=trim($this->fk_contrat);
2793
		$this->fk_product=trim($this->fk_product);
2794
		$this->statut=(int) $this->statut;
2795
		$this->label=trim($this->label);
2796
		$this->description=trim($this->description);
2797
		$this->vat_src_code=trim($this->vat_src_code);
2798
		$this->tva_tx=trim($this->tva_tx);
2799
		$this->localtax1_tx=trim($this->localtax1_tx);
2800
		$this->localtax2_tx=trim($this->localtax2_tx);
2801
		$this->qty=trim($this->qty);
2802
		$this->remise_percent=trim($this->remise_percent);
2803
		$this->remise=trim($this->remise);
2804
		$this->fk_remise_except=trim($this->fk_remise_except);
2805
		$this->subprice=price2num($this->subprice);
2806
		$this->price_ht=price2num($this->price_ht);
2807
		$this->total_ht=trim($this->total_ht);
2808
		$this->total_tva=trim($this->total_tva);
2809
		$this->total_localtax1=trim($this->total_localtax1);
2810
		$this->total_localtax2=trim($this->total_localtax2);
2811
		$this->total_ttc=trim($this->total_ttc);
2812
		$this->info_bits=trim($this->info_bits);
2813
		$this->fk_user_author=trim($this->fk_user_author);
2814
		$this->fk_user_ouverture=trim($this->fk_user_ouverture);
2815
		$this->fk_user_cloture=trim($this->fk_user_cloture);
2816
		$this->commentaire=trim($this->commentaire);
2817
		//if (empty($this->subprice)) $this->subprice = 0;
2818
		if (empty($this->price_ht)) $this->price_ht = 0;
2819
		if (empty($this->total_ht)) $this->total_ht = 0;
2820
		if (empty($this->total_tva)) $this->total_tva = 0;
2821
		if (empty($this->total_ttc)) $this->total_ttc = 0;
2822
		if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
2823
		if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
2824
		if (empty($this->remise_percent)) $this->remise_percent = 0;
2825
		// For backward compatibility
2826
		if (empty($this->date_start))      $this->date_start=$this->date_ouverture_prevue;
2827
		if (empty($this->date_start_real)) $this->date_start=$this->date_ouverture;
2828
		if (empty($this->date_end))        $this->date_start=$this->date_fin_validite;
2829
		if (empty($this->date_end_real))   $this->date_start=$this->date_cloture;
2830
2831
2832
		// Check parameters
2833
		// Put here code to add control on parameters values
2834
2835
		// Calcul du total TTC et de la TVA pour la ligne a partir de
2836
		// qty, pu, remise_percent et txtva
2837
		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2838
		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2839
		$localtaxes_type = getLocalTaxesFromRate($this->txtva, 0, $this->societe, $mysoc);
2840
2841
		$tabprice=calcul_price_total($this->qty, $this->price_ht, $this->remise_percent, $this->tva_tx, $this->localtax1_tx, $this->localtax2_tx, 0, 'HT', 0, 1, $mysoc, $localtaxes_type);
2842
		$this->total_ht  = $tabprice[0];
2843
		$this->total_tva = $tabprice[1];
2844
		$this->total_ttc = $tabprice[2];
2845
		$this->total_localtax1= $tabprice[9];
2846
		$this->total_localtax2= $tabprice[10];
2847
2848
	    if (empty($this->pa_ht)) $this->pa_ht=0;
2849
2850
		// if buy price not defined, define buyprice as configured in margin admin
2851
		if ($this->pa_ht == 0)
2852
		{
2853
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
2854
			{
2855
				return $result;
2856
			}
2857
			else
2858
			{
2859
				$this->pa_ht = $result;
2860
			}
2861
		}
2862
2863
2864
		$this->db->begin();
2865
2866
		$this->oldcopy = new ContratLigne($this->db);
2867
		$this->oldcopy->fetch($this->id);
2868
2869
		// Update request
2870
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
2871
		$sql.= " fk_contrat=".$this->fk_contrat.",";
2872
		$sql.= " fk_product=".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":'null').",";
2873
		$sql.= " statut=".$this->statut.",";
2874
		$sql.= " label='".$this->db->escape($this->label)."',";
2875
		$sql.= " description='".$this->db->escape($this->description)."',";
2876
		$sql.= " date_commande=".($this->date_commande!=''?"'".$this->db->idate($this->date_commande)."'":"null").",";
2877
		$sql.= " date_ouverture_prevue=".($this->date_ouverture_prevue!=''?"'".$this->db->idate($this->date_ouverture_prevue)."'":"null").",";
2878
		$sql.= " date_ouverture=".($this->date_ouverture!=''?"'".$this->db->idate($this->date_ouverture)."'":"null").",";
2879
		$sql.= " date_fin_validite=".($this->date_fin_validite!=''?"'".$this->db->idate($this->date_fin_validite)."'":"null").",";
2880
		$sql.= " date_cloture=".($this->date_cloture!=''?"'".$this->db->idate($this->date_cloture)."'":"null").",";
2881
		$sql.= " vat_src_code='".$this->db->escape($this->vat_src_code)."',";
2882
		$sql.= " tva_tx=".price2num($this->tva_tx).",";
2883
		$sql.= " localtax1_tx=".price2num($this->localtax1_tx).",";
2884
		$sql.= " localtax2_tx=".price2num($this->localtax2_tx).",";
2885
		$sql.= " qty=".price2num($this->qty).",";
2886
		$sql.= " remise_percent=".price2num($this->remise_percent).",";
2887
		$sql.= " remise=".($this->remise?price2num($this->remise):"null").",";
2888
		$sql.= " fk_remise_except=".($this->fk_remise_except > 0?$this->fk_remise_except:"null").",";
2889
		$sql.= " subprice=".($this->subprice != '' ? $this->subprice : "null").",";
2890
		$sql.= " price_ht=".($this->price_ht != '' ? $this->price_ht : "null").",";
2891
		$sql.= " total_ht=".$this->total_ht.",";
2892
		$sql.= " total_tva=".$this->total_tva.",";
2893
		$sql.= " total_localtax1=".$this->total_localtax1.",";
2894
		$sql.= " total_localtax2=".$this->total_localtax2.",";
2895
		$sql.= " total_ttc=".$this->total_ttc.",";
2896
		$sql.= " fk_product_fournisseur_price=".(!empty($this->fk_fournprice)?$this->fk_fournprice:"NULL").",";
2897
		$sql.= " buy_price_ht='".price2num($this->pa_ht)."',";
2898
		$sql.= " info_bits='".$this->db->escape($this->info_bits)."',";
2899
		$sql.= " fk_user_author=".($this->fk_user_author >= 0?$this->fk_user_author:"NULL").",";
2900
		$sql.= " fk_user_ouverture=".($this->fk_user_ouverture > 0?$this->fk_user_ouverture:"NULL").",";
2901
		$sql.= " fk_user_cloture=".($this->fk_user_cloture > 0?$this->fk_user_cloture:"NULL").",";
2902
		$sql.= " commentaire='".$this->db->escape($this->commentaire)."',";
2903
		$sql.= " fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
2904
		$sql.= " WHERE rowid=".$this->id;
2905
2906
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
2907
		$resql = $this->db->query($sql);
2908
		if (! $resql)
2909
		{
2910
			$this->error="Error ".$this->db->lasterror();
2911
			$error++;
2912
		}
2913
2914
		if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
2915
		{
2916
			$result=$this->insertExtraFields();
2917
			if ($result < 0)
2918
			{
2919
				$error++;
2920
			}
2921
		}
2922
2923
		// If we change a planned date (start or end), sync dates for all services
2924
		if (! $error && ! empty($conf->global->CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES))
2925
		{
2926
			if ($this->date_ouverture_prevue != $this->oldcopy->date_ouverture_prevue)
2927
			{
2928
				$sql ='UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
2929
				$sql.= " date_ouverture_prevue = ".($this->date_ouverture_prevue!=''?"'".$this->db->idate($this->date_ouverture_prevue)."'":"null");
2930
				$sql.= " WHERE fk_contrat = ".$this->fk_contrat;
2931
2932
				$resql = $this->db->query($sql);
2933
				if (! $resql)
2934
				{
2935
					$error++;
2936
					$this->error="Error ".$this->db->lasterror();
2937
				}
2938
			}
2939
			if ($this->date_fin_validite != $this->oldcopy->date_fin_validite)
2940
			{
2941
				$sql ='UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
2942
				$sql.= " date_fin_validite = ".($this->date_fin_validite!=''?"'".$this->db->idate($this->date_fin_validite)."'":"null");
2943
				$sql.= " WHERE fk_contrat = ".$this->fk_contrat;
2944
2945
				$resql = $this->db->query($sql);
2946
				if (! $resql)
2947
				{
2948
					$error++;
2949
					$this->error="Error ".$this->db->lasterror();
2950
				}
2951
			}
2952
		}
2953
2954
		if (! $error)
2955
		{
2956
			if (! $notrigger)
2957
			{
2958
	            // Call trigger
2959
	            $result=$this->call_trigger('LINECONTRACT_UPDATE', $user);
2960
	            if ($result < 0) { $error++; $this->db->rollback(); }
2961
	            // End call triggers
2962
			}
2963
		}
2964
2965
		if (! $error)
2966
		{
2967
        	$this->db->commit();
2968
			return 1;
2969
		} else {
2970
			$this->db->rollback();
2971
			$this->errors[]=$this->error;
2972
			return -1;
2973
		}
2974
	}
2975
2976
2977
	/**
2978
	 *      Mise a jour en base des champs total_xxx de ligne
2979
	 *		Used by migration process
2980
	 *
2981
	 *		@return		int		<0 if KO, >0 if OK
2982
	 */
2983
	function update_total()
2984
	{
2985
		$this->db->begin();
2986
2987
		// Mise a jour ligne en base
2988
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
2989
		$sql.= " total_ht=".price2num($this->total_ht,'MT')."";
2990
		$sql.= ",total_tva=".price2num($this->total_tva,'MT')."";
2991
		$sql.= ",total_localtax1=".price2num($this->total_localtax1,'MT')."";
2992
		$sql.= ",total_localtax2=".price2num($this->total_localtax2,'MT')."";
2993
		$sql.= ",total_ttc=".price2num($this->total_ttc,'MT')."";
2994
		$sql.= " WHERE rowid = ".$this->id;
2995
2996
		dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
2997
2998
		$resql=$this->db->query($sql);
2999
		if ($resql)
3000
		{
3001
			$this->db->commit();
3002
			return 1;
3003
		}
3004
		else
3005
		{
3006
			$this->error=$this->db->error();
3007
			$this->db->rollback();
3008
			return -2;
3009
		}
3010
	}
3011
3012
3013
	/**
3014
	 * Inserts a contrat line into database
3015
	 *
3016
	 * @param int $notrigger Set to 1 if you don't want triggers to be fired
3017
	 * @return int <0 if KO, >0 if OK
3018
	 */
3019
	public function insert($notrigger = 0)
3020
	{
3021
		global $conf, $user;
3022
3023
		// Insertion dans la base
3024
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
3025
		$sql.= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
3026
		$sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
3027
		$sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
3028
		$sql.= " info_bits,";
3029
		$sql.= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
3030
		if ($this->date_ouverture_prevue > 0) { $sql.= ",date_ouverture_prevue"; }
3031
		if ($this->date_fin_validite > 0)     { $sql.= ",date_fin_validite"; }
3032
		$sql.= ") VALUES ($this->fk_contrat, '', '" . $this->db->escape($this->description) . "',";
3033
		$sql.= ($this->fk_product>0 ? $this->fk_product : "null").",";
3034
		$sql.= " '".$this->db->escape($this->qty)."',";
3035
		$sql.= " '".$this->db->escape($this->vat_src_code)."',";
3036
		$sql.= " '".$this->db->escape($this->tva_tx)."',";
3037
		$sql.= " '".$this->db->escape($this->localtax1_tx)."',";
3038
		$sql.= " '".$this->db->escape($this->localtax2_tx)."',";
3039
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
3040
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
3041
		$sql.= " ".price2num($this->remise_percent).",".price2num($this->subprice).",";
3042
		$sql.= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
3043
		$sql.= " '".$this->db->escape($this->info_bits)."',";
3044
		$sql.= " ".price2num($this->price_ht).",".price2num($this->remise).",";
3045
		if ($this->fk_fournprice > 0) $sql.= ' '.$this->fk_fournprice.',';
3046
		else $sql.= ' null,';
3047
		if ($this->pa_ht > 0) $sql.= ' '.price2num($this->pa_ht);
3048
		else $sql.= ' null';
3049
		if ($this->date_ouverture > 0) { $sql.= ",'".$this->db->idate($this->date_ouverture)."'"; }
3050
		if ($this->date_cloture > 0)   { $sql.= ",'".$this->db->idate($this->date_cloture)."'"; }
3051
		$sql.= ")";
3052
3053
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3054
3055
		$resql=$this->db->query($sql);
3056
		if ($resql)
3057
		{
3058
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
3059
3060
			// Insert of extrafields
3061
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
3062
			{
3063
				$result = $this->insertExtraFields();
3064
				if ($result < 0)
3065
				{
3066
					$this->db->rollback();
3067
					return -1;
3068
				}
3069
			}
3070
3071
			if (!$notrigger)
3072
			{
3073
				// Call trigger
3074
				$result = $this->call_trigger('LINECONTRACT_INSERT', $user);
3075
				if ($result < 0) {
3076
					$this->db->rollback();
3077
					return -1;
3078
				}
3079
				// End call triggers
3080
			}
3081
3082
			$this->db->commit();
3083
			return 1;
3084
		}
3085
		else
3086
		{
3087
			$this->db->rollback();
3088
			$this->error=$this->db->error()." sql=".$sql;
3089
			return -1;
3090
		}
3091
	}
3092
3093
	/**
3094
	 *  Activate a contract line
3095
	 *
3096
	 * @param   User 		$user 		Objet User who activate contract
3097
	 * @param  	int 		$date 		Date activation
3098
	 * @param  	int|string 	$date_end 	Date planned end. Use '-1' to keep it unchanged.
3099
	 * @param   string 		$comment 	A comment typed by user
3100
	 * @return 	int                    	<0 if KO, >0 if OK
3101
	 */
3102
	function active_line($user, $date, $date_end = '', $comment = '')
3103
	{
3104
		global $langs, $conf;
3105
3106
		// Update object
3107
		$this->date_ouverture = $date;
3108
		$this->date_fin_validite = $date_end;
3109
		$this->fk_user_ouverture = $user->id;
3110
		$this->date_cloture = null;
3111
		$this->commentaire = $comment;
3112
3113
		$error = 0;
3114
3115
		$this->db->begin();
3116
3117
		$sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = 4,";
3118
		$sql .= " date_ouverture = " . (dol_strlen($date) != 0 ? "'" . $this->db->idate($date) . "'" : "null") . ",";
3119
		if ($date_end >= 0) $sql .= " date_fin_validite = " . (dol_strlen($date_end) != 0 ? "'" . $this->db->idate($date_end) . "'" : "null") . ",";
3120
		$sql .= " fk_user_ouverture = " . $user->id . ",";
3121
		$sql .= " date_cloture = null,";
3122
		$sql .= " commentaire = '" . $this->db->escape($comment) . "'";
3123
		$sql .= " WHERE rowid = " . $this->id . " AND (statut = 0 OR statut = 3 OR statut = 5)";
3124
3125
		dol_syslog(get_class($this) . "::active_line", LOG_DEBUG);
3126
		$resql = $this->db->query($sql);
3127
		if ($resql) {
3128
			// Call trigger
3129
			$result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
3130
			if ($result < 0) {
3131
				$error++;
3132
				$this->db->rollback();
3133
				return -1;
3134
			}
3135
			// End call triggers
3136
3137
			$this->db->commit();
3138
			return 1;
3139
		} else {
3140
			$this->error = $this->db->lasterror();
3141
			$this->db->rollback();
3142
			return -1;
3143
		}
3144
	}
3145
3146
	/**
3147
	 *  Close a contract line
3148
	 *
3149
	 * @param    User 	$user 			Objet User who close contract
3150
	 * @param  	 int 	$date_end 		Date end
3151
	 * @param    string $comment 		A comment typed by user
3152
	 * @return int                    	<0 if KO, >0 if OK
3153
	 */
3154
	function close_line($user, $date_end, $comment = '')
3155
	{
3156
		global $langs, $conf;
3157
3158
		// Update object
3159
		$this->date_cloture = $date_end;
3160
		$this->fk_user_cloture = $user->id;
3161
		$this->commentaire = $comment;
3162
3163
		$error = 0;
3164
3165
		// statut actif : 4
3166
3167
		$this->db->begin();
3168
3169
		$sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = 5,";
3170
		$sql .= " date_cloture = '" . $this->db->idate($date_end) . "',";
3171
		$sql .= " fk_user_cloture = " . $user->id . ",";
3172
		$sql .= " commentaire = '" . $this->db->escape($comment) . "'";
3173
		$sql .= " WHERE rowid = " . $this->id . " AND statut = 4";
3174
3175
		$resql = $this->db->query($sql);
3176
		if ($resql) {
3177
			// Call trigger
3178
			$result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
3179
			if ($result < 0) {
3180
				$error++;
3181
				$this->db->rollback();
3182
				return -1;
3183
			}
3184
			// End call triggers
3185
3186
			$this->db->commit();
3187
			return 1;
3188
		} else {
3189
			$this->error = $this->db->lasterror();
3190
			$this->db->rollback();
3191
			return -1;
3192
		}
3193
	}
3194
}
3195