Completed
Branch develop (a0152f)
by
unknown
34:49
created

Contrat::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
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
     *  @param	int			$notrigger		1=Does not execute triggers, 0=Execute triggers
265
     *  @param	string		$comment		Comment
266
	 *	@return	int							<0 if KO, >0 if OK
267
	 *  @see closeAll
268
	 */
269
	function activateAll($user, $date_start='', $notrigger=0, $comment='')
270
	{
271
		if (empty($date_start)) $date_start = dol_now();
272
273
		$this->db->begin();
274
275
		// Load lines
276
		$this->fetch_lines();
277
278
		$error=0;
279
280
		foreach($this->lines as $contratline)
281
		{
282
			// Open lines not already open
283
			if ($contratline->statut != ContratLigne::STATUS_OPEN)
284
			{
285
				$contratline->context = $this->context;
286
287
				$result = $contratline->active_line($user, $date_start, -1, $comment);
288
				if ($result < 0)
289
				{
290
					$error++;
291
					$this->error = $contratline->error;
292
					$this->errors = $contratline->errors;
293
					break;
294
				}
295
			}
296
		}
297
298
		if (! $error && $this->statut == 0)
299
		{
300
			$result=$this->validate($user, '', $notrigger);
301
			if ($result < 0) $error++;
302
		}
303
304
		if (! $error)
305
		{
306
			$this->db->commit();
307
			return 1;
308
		}
309
		else
310
		{
311
			$this->db->rollback();
312
			return -1;
313
		}
314
	}
315
316
	/**
317
	 * Close all lines of a contract
318
	 *
319
	 * @param	User		$user      		Object User making action
320
     * @param	int			$notrigger		1=Does not execute triggers, 0=Execute triggers
321
     * @param	string		$comment		Comment
322
	 * @return	int							<0 if KO, >0 if OK
323
	 * @see activateAll
324
	 */
325
	function closeAll(User $user, $notrigger=0, $comment='')
326
	{
327
		$this->db->begin();
328
329
		// Load lines
330
		$this->fetch_lines();
331
332
		$now = dol_now();
333
334
		$error = 0;
335
336
		foreach($this->lines as $contratline)
337
		{
338
			// Close lines not already closed
339
			if ($contratline->statut != ContratLigne::STATUS_CLOSED)
340
	        {
341
				$contratline->date_cloture=$now;
342
				$contratline->fk_user_cloture=$user->id;
343
				$contratline->statut=ContratLigne::STATUS_CLOSED;
344
				$result=$contratline->close_line($user, $now, $comment, $notrigger);
345
				if ($result < 0)
346
				{
347
					$error++;
348
					$this->error = $contratline->error;
349
					$this->errors = $contratline->errors;
350
					break;
351
				}
352
	        }
353
		}
354
355
		if (! $error && $this->statut == 0)
356
		{
357
			$result=$this->validate($user, '', $notrigger);
358
			if ($result < 0) $error++;
359
		}
360
361
        if (! $error)
362
        {
363
            $this->db->commit();
364
            return 1;
365
        }
366
        else
367
        {
368
            $this->db->rollback();
369
            return -1;
370
        }
371
	}
372
373
	/**
374
	 * Validate a contract
375
	 *
376
	 * @param	User	$user      		Objet User
377
	 * @param   string	$force_number	Reference to force on contract (not implemented yet)
378
     * @param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
379
	 * @return	int						<0 if KO, >0 if OK
380
	 */
381
	function validate(User $user, $force_number='', $notrigger=0)
382
	{
383
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
384
		global $langs, $conf;
385
386
		$now=dol_now();
387
388
		$error=0;
389
		dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number);
390
391
392
		$this->db->begin();
393
394
		$this->fetch_thirdparty();
395
396
		// A contract is validated so we can move thirdparty to status customer
397
		$result=$this->thirdparty->set_as_client();
398
399
		// Define new ref
400
		if ($force_number)
401
		{
402
			$num = $force_number;
403
		}
404
		else if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
405
		{
406
			$num = $this->getNextNumRef($this->thirdparty);
407
		}
408
		else
409
		{
410
			$num = $this->ref;
411
		}
412
        $this->newref = $num;
413
414
		if ($num)
415
		{
416
			$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET ref = '".$num."', statut = 1";
417
			//$sql.= ", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
418
			$sql .= " WHERE rowid = ".$this->id . " AND statut = 0";
419
420
			dol_syslog(get_class($this)."::validate", LOG_DEBUG);
421
			$resql = $this->db->query($sql);
422
			if (! $resql)
423
			{
424
				dol_print_error($this->db);
425
				$error++;
426
				$this->error=$this->db->lasterror();
427
			}
428
429
			// Trigger calls
430
			if (! $error && ! $notrigger)
431
			{
432
                // Call trigger
433
                $result=$this->call_trigger('CONTRACT_VALIDATE',$user);
434
                if ($result < 0) { $error++; }
435
                // End call triggers
436
			}
437
438
			if (! $error)
439
			{
440
            	$this->oldref = $this->ref;
441
442
				// Rename directory if dir was a temporary ref
443
				if (preg_match('/^[\(]?PROV/i', $this->ref))
444
				{
445
					// Rename of object directory ($this->ref = old ref, $num = new ref)
446
					// to  not lose the linked files
447
					$oldref = dol_sanitizeFileName($this->ref);
448
					$newref = dol_sanitizeFileName($num);
449
					$dirsource = $conf->contract->dir_output.'/'.$oldref;
450
					$dirdest = $conf->contract->dir_output.'/'.$newref;
451
					if (file_exists($dirsource))
452
					{
453
						dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
454
455
						if (@rename($dirsource, $dirdest))
456
						{
457
							dol_syslog("Rename ok");
458
						    // Rename docs starting with $oldref with $newref
459
            				$listoffiles=dol_dir_list($conf->contract->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
460
            				foreach($listoffiles as $fileentry)
461
            				{
462
            					$dirsource=$fileentry['name'];
463
            					$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
464
            					$dirsource=$fileentry['path'].'/'.$dirsource;
465
            					$dirdest=$fileentry['path'].'/'.$dirdest;
466
            					@rename($dirsource, $dirdest);
467
            				}
468
						}
469
					}
470
				}
471
			}
472
473
			// Set new ref and define current statut
474
			if (! $error)
475
			{
476
				$this->ref = $num;
477
				$this->statut = 1;
478
				$this->brouillon = 0;
479
				$this->date_validation = $now;
480
			}
481
		}
482
		else
483
		{
484
			$error++;
485
		}
486
487
		if (! $error)
488
		{
489
			$this->db->commit();
490
			return 1;
491
		}
492
		else
493
		{
494
			$this->db->rollback();
495
			return -1;
496
		}
497
498
	}
499
500
	/**
501
	 * Unvalidate a contract
502
	 *
503
	 * @param	User	$user      		Objet User
504
     * @param	int		$notrigger		1=Does not execute triggers, 0=execute triggers
505
	 * @return	int						<0 if KO, >0 if OK
506
	 */
507
	function reopen($user, $notrigger=0)
508
	{
509
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
510
		global $langs, $conf;
511
512
		$now=dol_now();
513
514
		$error=0;
515
		dol_syslog(get_class($this).'::reopen user='.$user->id);
516
517
		$this->db->begin();
518
519
		$this->fetch_thirdparty();
520
521
		$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET statut = 0";
522
		//$sql.= ", fk_user_valid = null, date_valid = null";
523
		$sql .= " WHERE rowid = ".$this->id . " AND statut = 1";
524
525
		dol_syslog(get_class($this)."::validate", LOG_DEBUG);
526
		$resql = $this->db->query($sql);
527
		if (! $resql)
528
		{
529
			dol_print_error($this->db);
530
			$error++;
531
			$this->error=$this->db->lasterror();
532
		}
533
534
		// Trigger calls
535
		if (! $error && ! $notrigger)
536
		{
537
			// Call trigger
538
			$result=$this->call_trigger('CONTRACT_REOPEN',$user);
539
			if ($result < 0) {
540
				$error++;
541
			}
542
			// End call triggers
543
		}
544
545
		// Set new ref and define current statut
546
		if (! $error)
547
		{
548
			$this->statut=0;
549
			$this->brouillon=1;
550
			$this->date_validation=$now;
551
		}
552
553
		if (! $error)
554
		{
555
			$this->db->commit();
556
			return 1;
557
		}
558
		else
559
		{
560
			$this->db->rollback();
561
			return -1;
562
		}
563
	}
564
565
	/**
566
	 *    Load a contract from database
567
	 *
568
	 *    @param	int		$id     		Id of contract to load
569
	 *    @param	string	$ref			Ref
570
	 *    @param	string	$ref_customer	Customer ref
571
	 *    @param	string	$ref_supplier	Supplier ref
572
	 *    @return   int     				<0 if KO, 0 if not found, Id of contract if OK
573
	 */
574
	function fetch($id, $ref='', $ref_customer='', $ref_supplier='')
575
	{
576
		$sql = "SELECT rowid, statut, ref, fk_soc, mise_en_service as datemise,";
577
		$sql.= " ref_supplier, ref_customer,";
578
		$sql.= " ref_ext,";
579
		$sql.= " fk_user_mise_en_service, date_contrat as datecontrat,";
580
		$sql.= " fk_user_author, fin_validite, date_cloture,";
581
		$sql.= " fk_projet,";
582
		$sql.= " fk_commercial_signature, fk_commercial_suivi,";
583
		$sql.= " note_private, note_public, model_pdf, extraparams";
584
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat";
585
		if (! $id) $sql.=" WHERE entity IN (".getEntity('contract').")";
586
		else $sql.= " WHERE rowid=".$id;
587
		if ($ref_customer)
588
		{
589
			$sql.= " AND ref_customer = '".$this->db->escape($ref_customer)."'";
590
		}
591
		if ($ref_supplier)
592
		{
593
			$sql.= " AND ref_supplier = '".$this->db->escape($ref_supplier)."'";
594
		}
595
		if ($ref)
596
		{
597
			$sql.= " AND ref='".$this->db->escape($ref)."'";
598
		}
599
600
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
601
		$resql = $this->db->query($sql);
602
		if ($resql)
603
		{
604
			$result = $this->db->fetch_array($resql);
605
606
			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...
607
			{
608
				$this->id						= $result["rowid"];
609
				$this->ref						= (!isset($result["ref"]) || !$result["ref"]) ? $result["rowid"] : $result["ref"];
610
				$this->ref_customer				= $result["ref_customer"];
611
				$this->ref_supplier				= $result["ref_supplier"];
612
				$this->ref_ext					= $result["ref_ext"];
613
				$this->statut					= $result["statut"];
614
				$this->mise_en_service			= $this->db->jdate($result["datemise"]);
615
616
				$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...
617
				$this->date_creation			= $this->db->jdate($result["datecontrat"]);
618
619
				$this->fin_validite				= $this->db->jdate($result["fin_validite"]);
620
				$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...
621
622
623
				$this->user_author_id			= $result["fk_user_author"];
624
625
				$this->commercial_signature_id	= $result["fk_commercial_signature"];
626
				$this->commercial_suivi_id		= $result["fk_commercial_suivi"];
627
628
				$this->note_private				= $result["note_private"];
629
				$this->note_public				= $result["note_public"];
630
				$this->modelpdf					= $result["model_pdf"];
631
632
				$this->fk_projet				= $result["fk_projet"]; // deprecated
633
				$this->fk_project				= $result["fk_projet"];
634
635
				$this->socid					= $result["fk_soc"];
636
				$this->fk_soc					= $result["fk_soc"];
637
638
				$this->extraparams				= (array) json_decode($result["extraparams"], true);
639
640
				$this->db->free($resql);
641
642
643
				// Retreive all extrafield
644
				// fetch optionals attributes and labels
645
				$this->fetch_optionals();
646
647
648
				/*
649
				 * Lines
650
				 */
651
652
				$this->lines  = array();
653
654
				$result=$this->fetch_lines();
655
				if ($result < 0)
656
				{
657
					$this->error=$this->db->lasterror();
658
					return -3;
659
				}
660
661
				return $this->id;
662
			}
663
			else
664
			{
665
				dol_syslog(get_class($this)."::Fetch Erreur contrat non trouve");
666
				$this->error="Contract not found";
667
				return 0;
668
			}
669
		}
670
		else
671
		{
672
			dol_syslog(get_class($this)."::Fetch Erreur lecture contrat");
673
			$this->error=$this->db->error();
674
			return -1;
675
		}
676
677
	}
678
679
	/**
680
	 *  Load lines array into this->lines.
681
	 *  This set also nbofserviceswait, nbofservicesopened, nbofservicesexpired and nbofservicesclosed
682
	 *
683
	 *  @return ContratLigne[]   Return array of contract lines
684
	 */
685
	function fetch_lines()
686
	{
687
		$this->nbofserviceswait=0;
688
		$this->nbofservicesopened=0;
689
		$this->nbofservicesexpired=0;
690
		$this->nbofservicesclosed=0;
691
692
		$total_ttc=0;
693
		$total_vat=0;
694
		$total_ht=0;
695
696
		$now=dol_now();
697
698
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
699
		$extrafieldsline=new ExtraFields($this->db);
700
		$line = new ContratLigne($this->db);
701
		$extralabelsline=$extrafieldsline->fetch_name_optionals_label($line->table_element,true);
702
703
		$this->lines=array();
704
        $pos = 0;
705
706
		// Selectionne les lignes contrats liees a un produit
707
		$sql = "SELECT p.label as product_label, p.description as product_desc, p.ref as product_ref,";
708
		$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,";
709
		$sql.= " d.total_ht,";
710
		$sql.= " d.total_tva,";
711
		$sql.= " d.total_localtax1,";
712
		$sql.= " d.total_localtax2,";
713
		$sql.= " d.total_ttc,";
714
		$sql.= " d.info_bits, d.fk_product,";
715
		$sql.= " d.date_ouverture_prevue, d.date_ouverture,";
716
		$sql.= " d.date_fin_validite, d.date_cloture,";
717
		$sql.= " d.fk_user_author,";
718
		$sql.= " d.fk_user_ouverture,";
719
		$sql.= " d.fk_user_cloture,";
720
		$sql.= " d.fk_unit";
721
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as d LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
722
		$sql.= " WHERE d.fk_contrat = ".$this->id;
723
		$sql.= " ORDER by d.rowid ASC";
724
725
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
726
		$result = $this->db->query($sql);
727
		if ($result)
728
		{
729
			$num = $this->db->num_rows($result);
730
			$i = 0;
731
732
			while ($i < $num)
733
			{
734
				$objp					= $this->db->fetch_object($result);
735
736
				$line					= new ContratLigne($this->db);
737
				$line->id				= $objp->rowid;
738
				$line->ref				= $objp->rowid;
739
				$line->fk_contrat		= $objp->fk_contrat;
740
				$line->desc				= $objp->description;  // Description ligne
741
				$line->qty				= $objp->qty;
742
				$line->vat_src_code 	= $objp->vat_src_code ;
743
				$line->tva_tx			= $objp->tva_tx;
744
				$line->localtax1_tx		= $objp->localtax1_tx;
745
				$line->localtax2_tx		= $objp->localtax2_tx;
746
				$line->localtax1_type	= $objp->localtax1_type;
747
				$line->localtax2_type	= $objp->localtax2_type;
748
				$line->subprice			= $objp->subprice;
749
				$line->statut			= $objp->statut;
750
				$line->remise_percent	= $objp->remise_percent;
751
				$line->price_ht			= $objp->price_ht;
752
				$line->price			= $objp->price_ht;	// For backward compatibility
753
				$line->total_ht			= $objp->total_ht;
754
				$line->total_tva		= $objp->total_tva;
755
				$line->total_localtax1	= $objp->total_localtax1;
756
				$line->total_localtax2	= $objp->total_localtax2;
757
				$line->total_ttc		= $objp->total_ttc;
758
				$line->fk_product		= (($objp->fk_product > 0)?$objp->fk_product:0);
759
				$line->info_bits		= $objp->info_bits;
760
761
				$line->fk_fournprice 	= $objp->fk_fournprice;
762
				$marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
763
				$line->pa_ht 			= $marginInfos[0];
764
765
				$line->fk_user_author	= $objp->fk_user_author;
766
				$line->fk_user_ouverture= $objp->fk_user_ouverture;
767
				$line->fk_user_cloture  = $objp->fk_user_cloture;
768
				$line->fk_unit           = $objp->fk_unit;
769
770
				$line->ref				= $objp->product_ref;	// deprecated
771
				$line->product_ref		= $objp->product_ref;   // Ref product
772
				$line->product_desc		= $objp->product_desc;  // Description product
773
				$line->product_label	= $objp->product_label; // Label product
774
775
				$line->description		= $objp->description;
776
777
				$line->date_start            = $this->db->jdate($objp->date_ouverture_prevue);
778
				$line->date_start_real       = $this->db->jdate($objp->date_ouverture);
779
				$line->date_end              = $this->db->jdate($objp->date_fin_validite);
780
				$line->date_end_real         = $this->db->jdate($objp->date_cloture);
781
				// For backward compatibility
782
				$line->date_ouverture_prevue = $this->db->jdate($objp->date_ouverture_prevue);
783
				$line->date_ouverture        = $this->db->jdate($objp->date_ouverture);
784
				$line->date_fin_validite     = $this->db->jdate($objp->date_fin_validite);
785
				$line->date_cloture          = $this->db->jdate($objp->date_cloture);
786
				$line->date_debut_prevue = $this->db->jdate($objp->date_ouverture_prevue);
787
				$line->date_debut_reel   = $this->db->jdate($objp->date_ouverture);
788
				$line->date_fin_prevue   = $this->db->jdate($objp->date_fin_validite);
789
				$line->date_fin_reel     = $this->db->jdate($objp->date_cloture);
790
791
				// Retreive all extrafield for contract
792
				// fetch optionals attributes and labels
793
				$line->fetch_optionals();
794
795
				$this->lines[$pos]			= $line;
796
				$this->lines_id_index_mapper[$line->id] = $pos;
797
798
				//dol_syslog("1 ".$line->desc);
799
				//dol_syslog("2 ".$line->product_desc);
800
801
				if ($line->statut == ContratLigne::STATUS_INITIAL) $this->nbofserviceswait++;
802
				if ($line->statut == ContratLigne::STATUS_OPEN && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) $this->nbofservicesopened++;
803
				if ($line->statut == ContratLigne::STATUS_OPEN && (! empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) $this->nbofservicesexpired++;
804
				if ($line->statut == ContratLigne::STATUS_CLOSED) $this->nbofservicesclosed++;
805
806
				$total_ttc+=$objp->total_ttc;   // TODO Not saved into database
807
                $total_vat+=$objp->total_tva;
808
                $total_ht+=$objp->total_ht;
809
810
				$i++;
811
				$pos++;
812
			}
813
			$this->db->free($result);
814
		}
815
		else
816
		{
817
			dol_syslog(get_class($this)."::Fetch Erreur lecture des lignes de contrats liees aux produits");
818
			return -3;
819
		}
820
821
		$this->nbofservices=count($this->lines);
822
        $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
823
        $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
824
        $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
825
826
		return $this->lines;
827
	}
828
829
	/**
830
	 *  Create a contract into database
831
	 *
832
	 *  @param	User	$user       User that create
833
	 *  @return int  				<0 if KO, id of contract if OK
834
	 */
835
	function create($user)
836
	{
837
		global $conf,$langs,$mysoc;
838
839
		// Check parameters
840
		$paramsok=1;
841
		if ($this->commercial_signature_id <= 0)
842
		{
843
			$langs->load("commercial");
844
			$this->error.=$langs->trans("ErrorFieldRequired",$langs->trans("SalesRepresentativeSignature"));
845
			$paramsok=0;
846
		}
847
		if ($this->commercial_suivi_id <= 0)
848
		{
849
			$langs->load("commercial");
850
			$this->error.=($this->error?"<br>":'');
851
			$this->error.=$langs->trans("ErrorFieldRequired",$langs->trans("SalesRepresentativeFollowUp"));
852
			$paramsok=0;
853
		}
854
		if (! $paramsok) return -1;
855
856
857
		$this->db->begin();
858
859
		$now=dol_now();
860
861
		// Insert contract
862
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."contrat (datec, fk_soc, fk_user_author, date_contrat,";
863
		$sql.= " fk_commercial_signature, fk_commercial_suivi, fk_projet,";
864
		$sql.= " ref, entity, note_private, note_public, ref_customer, ref_supplier, ref_ext)";
865
		$sql.= " VALUES ('".$this->db->idate($now)."',".$this->socid.",".$user->id;
866
		$sql.= ", ".(dol_strlen($this->date_contrat)!=0 ? "'".$this->db->idate($this->date_contrat)."'" : "NULL");
867
		$sql.= ",".($this->commercial_signature_id>0?$this->commercial_signature_id:"NULL");
868
		$sql.= ",".($this->commercial_suivi_id>0?$this->commercial_suivi_id:"NULL");
869
		$sql.= ",".($this->fk_project>0?$this->fk_project:"NULL");
870
		$sql.= ", ".(dol_strlen($this->ref)<=0 ? "null" : "'".$this->db->escape($this->ref)."'");
871
		$sql.= ", ".$conf->entity;
872
		$sql.= ", ".(!empty($this->note_private)?("'".$this->db->escape($this->note_private)."'"):"NULL");
873
		$sql.= ", ".(!empty($this->note_public)?("'".$this->db->escape($this->note_public)."'"):"NULL");
874
		$sql.= ", ".(!empty($this->ref_customer)?("'".$this->db->escape($this->ref_customer)."'"):"NULL");
875
		$sql.= ", ".(!empty($this->ref_supplier)?("'".$this->db->escape($this->ref_supplier)."'"):"NULL");
876
		$sql.= ", ".(!empty($this->ref_ext)?("'".$this->db->escape($this->ref_ext)."'"):"NULL");
877
		$sql.= ")";
878
		$resql=$this->db->query($sql);
879
		if ($resql)
880
		{
881
			$error=0;
882
883
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."contrat");
884
885
			// Load object modContract
886
			$module=(! empty($conf->global->CONTRACT_ADDON)?$conf->global->CONTRACT_ADDON:'mod_contract_serpis');
887
			if (substr($module, 0, 13) == 'mod_contract_' && substr($module, -3) == 'php')
888
			{
889
				$module = substr($module, 0, dol_strlen($module)-4);
890
			}
891
			$result=dol_include_once('/core/modules/contract/'.$module.'.php');
892
			if ($result > 0)
893
			{
894
				$modCodeContract = new $module();
895
896
				if (!empty($modCodeContract->code_auto)) {
897
					// Mise a jour ref
898
					$sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".$this->id;
899
					if ($this->db->query($sql))
900
					{
901
						if ($this->id)
902
						{
903
							$this->ref="(PROV".$this->id.")";
904
						}
905
					}
906
				} else {
907
					$error++;
908
					$this->error='Failed to get PROV number';
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
		$error=0;
1336
1337
		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");
1338
1339
		// Check parameters
1340
		if ($fk_product <= 0 && empty($desc))
1341
		{
1342
			$this->error="ErrorDescRequiredForFreeProductLines";
1343
			return -1;
1344
		}
1345
1346
		if ($this->statut >= 0)
1347
		{
1348
			$this->db->begin();
1349
1350
			// Clean parameters
1351
			$pu_ht=price2num($pu_ht);
1352
			$pu_ttc=price2num($pu_ttc);
1353
			$pa_ht=price2num($pa_ht);
1354
			$txtva=price2num($txtva);
1355
			$txlocaltax1=price2num($txlocaltax1);
1356
			$txlocaltax2=price2num($txlocaltax2);
1357
			$remise_percent=price2num($remise_percent);
1358
			$qty=price2num($qty);
1359
			if (empty($qty)) $qty=1;
1360
			if (empty($info_bits)) $info_bits=0;
1361
			if (empty($pu_ht) || ! is_numeric($pu_ht))  $pu_ht=0;
1362
			if (empty($pu_ttc)) $pu_ttc=0;
1363
			if (empty($txtva) || ! is_numeric($txtva)) $txtva=0;
1364
			if (empty($txlocaltax1) || ! is_numeric($txlocaltax1)) $txlocaltax1=0;
1365
            if (empty($txlocaltax2) || ! is_numeric($txlocaltax2)) $txlocaltax2=0;
1366
1367
			if ($price_base_type=='HT')
1368
			{
1369
				$pu=$pu_ht;
1370
			}
1371
			else
1372
			{
1373
				$pu=$pu_ttc;
1374
			}
1375
1376
			// Check parameters
1377
			if (empty($remise_percent)) $remise_percent=0;
1378
1379
			// Calcul du total TTC et de la TVA pour la ligne a partir de
1380
			// qty, pu, remise_percent et txtva
1381
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1382
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1383
1384
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->societe, $mysoc);
1385
1386
		    // Clean vat code
1387
    		$vat_src_code='';
1388
    		if (preg_match('/\((.*)\)/', $txtva, $reg))
1389
    		{
1390
    		    $vat_src_code = $reg[1];
1391
    		    $txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
1392
    		}
1393
1394
			$tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, 1,$mysoc, $localtaxes_type);
1395
			$total_ht  = $tabprice[0];
1396
			$total_tva = $tabprice[1];
1397
			$total_ttc = $tabprice[2];
1398
			$total_localtax1= $tabprice[9];
1399
			$total_localtax2= $tabprice[10];
1400
1401
			$localtax1_type=$localtaxes_type[0];
1402
			$localtax2_type=$localtaxes_type[2];
1403
1404
			// TODO A virer
1405
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1406
			$remise = 0;
1407
			$price = price2num(round($pu_ht, 2));
1408
			if (dol_strlen($remise_percent) > 0)
1409
			{
1410
				$remise = round(($pu_ht * $remise_percent / 100), 2);
1411
				$price = $pu_ht - $remise;
1412
			}
1413
1414
		    if (empty($pa_ht)) $pa_ht=0;
1415
1416
1417
			// if buy price not defined, define buyprice as configured in margin admin
1418
			if ($this->pa_ht == 0)
1419
			{
1420
				if (($result = $this->defineBuyPrice($pu_ht, $remise_percent, $fk_product)) < 0)
1421
				{
1422
					return $result;
1423
				}
1424
				else
1425
				{
1426
					$pa_ht = $result;
1427
				}
1428
			}
1429
1430
			// Insertion dans la base
1431
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
1432
			$sql.= " (fk_contrat, label, description, fk_product, qty, tva_tx, vat_src_code,";
1433
			$sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
1434
			$sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
1435
			$sql.= " info_bits,";
1436
			$sql.= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
1437
			if ($date_start > 0) { $sql.= ",date_ouverture_prevue"; }
1438
			if ($date_end > 0)   { $sql.= ",date_fin_validite"; }
1439
			$sql.= ", fk_unit";
1440
			$sql.= ") VALUES (";
1441
			$sql.= $this->id.", '', '" . $this->db->escape($desc) . "',";
1442
			$sql.= ($fk_product>0 ? $fk_product : "null").",";
1443
			$sql.= " ".$qty.",";
1444
			$sql.= " ".$txtva.",";
1445
			$sql.= " ".($vat_src_code?"'".$vat_src_code."'":"null").",";
1446
			$sql.= " ".$txlocaltax1.",";
1447
			$sql.= " ".$txlocaltax2.",";
1448
			$sql.= " '".$localtax1_type."',";
1449
			$sql.= " '".$localtax2_type."',";
1450
			$sql.= " ".price2num($remise_percent).",";
1451
			$sql.= " ".price2num($pu_ht).",";
1452
			$sql.= " ".price2num($total_ht).",".price2num($total_tva).",".price2num($total_localtax1).",".price2num($total_localtax2).",".price2num($total_ttc).",";
1453
			$sql.= " '".$info_bits."',";
1454
			$sql.= " ".price2num($price).",".price2num($remise).",";
1455
			if (isset($fk_fournprice)) $sql.= ' '.$fk_fournprice.',';
1456
			else $sql.= ' null,';
1457
			if (isset($pa_ht)) $sql.= ' '.price2num($pa_ht);
1458
			else $sql.= ' null';
1459
			if ($date_start > 0) { $sql.= ",'".$this->db->idate($date_start)."'"; }
1460
			if ($date_end > 0) { $sql.= ",'".$this->db->idate($date_end)."'"; }
1461
			$sql.= ", ".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
1462
			$sql.= ")";
1463
1464
			$resql=$this->db->query($sql);
1465
			if ($resql)
1466
			{
1467
				$contractlineid = $this->db->last_insert_id(MAIN_DB_PREFIX."contratdet");
1468
1469
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1470
				{
1471
					$contractline = new ContratLigne($this->db);
1472
					$contractline->array_options=$array_options;
1473
					$contractline->id=$contractlineid;
1474
					$result=$contractline->insertExtraFields();
1475
					if ($result < 0)
1476
					{
1477
						$this->error[]=$contractline->error;
1478
						$error++;
1479
					}
1480
				}
1481
1482
				if (empty($error)) {
1483
				    // Call trigger
1484
				    $result=$this->call_trigger('LINECONTRACT_INSERT',$user);
1485
				    if ($result < 0)
1486
				    {
1487
				    	$error++;
1488
				    }
1489
				    // End call triggers
1490
				}
1491
1492
				if ($error)
1493
				{
1494
					$this->db->rollback();
1495
					return -1;
1496
				}
1497
				else
1498
				{
1499
					$this->db->commit();
1500
					return $contractlineid;
1501
				}
1502
			}
1503
			else
1504
			{
1505
				$this->db->rollback();
1506
				$this->error=$this->db->error()." sql=".$sql;
1507
				return -1;
1508
			}
1509
		}
1510
		else
1511
		{
1512
			dol_syslog(get_class($this)."::addline ErrorTryToAddLineOnValidatedContract", LOG_ERR);
1513
			return -2;
1514
		}
1515
	}
1516
1517
	/**
1518
	 *  Mets a jour une ligne de contrat
1519
	 *
1520
	 *  @param	int			$rowid            	Id de la ligne de facture
1521
	 *  @param  string		$desc             	Description de la ligne
1522
	 *  @param  float		$pu               	Prix unitaire
1523
	 *  @param  int			$qty              	Quantite
1524
	 *  @param  float		$remise_percent   	Pourcentage de remise de la ligne
1525
	 *  @param  int			$date_start       	Date de debut prevue
1526
	 *  @param  int			$date_end         	Date de fin prevue
1527
	 *  @param  float		$tvatx            	Taux TVA
1528
	 *  @param  float		$localtax1tx      	Local tax 1 rate
1529
	 *  @param  float		$localtax2tx      	Local tax 2 rate
1530
	 *  @param  int|string	$date_debut_reel  	Date de debut reelle
1531
	 *  @param  int|string	$date_fin_reel    	Date de fin reelle
1532
	 *	@param	string		$price_base_type	HT or TTC
1533
	 * 	@param  int			$info_bits			Bits de type de lignes
1534
	 * 	@param  int			$fk_fournprice		Fourn price id
1535
	 *  @param  int			$pa_ht				Buying price HT
1536
	 *  @param	array		$array_options		extrafields array
1537
	 * 	@param 	string		$fk_unit 			Code of the unit to use. Null to use the default one
1538
	 *  @return int              				< 0 si erreur, > 0 si ok
1539
	 */
1540
	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)
1541
	{
1542
		global $user, $conf, $langs, $mysoc;
1543
1544
		$error=0;
1545
1546
		// Clean parameters
1547
		$qty=trim($qty);
1548
		$desc=trim($desc);
1549
		$desc=trim($desc);
1550
		$price = price2num($pu);
1551
		$tvatx = price2num($tvatx);
1552
		$localtax1tx = price2num($localtax1tx);
1553
		$localtax2tx = price2num($localtax2tx);
1554
		$pa_ht=price2num($pa_ht);
1555
		if (empty($fk_fournprice)) $fk_fournprice=0;
1556
1557
		$subprice = $price;
1558
		$remise = 0;
1559
		if (dol_strlen($remise_percent) > 0)
1560
		{
1561
			$remise = round(($pu * $remise_percent / 100), 2);
1562
			$price = $pu - $remise;
1563
		}
1564
		else
1565
		{
1566
			$remise_percent=0;
1567
		}
1568
1569
		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");
1570
1571
		$this->db->begin();
1572
1573
		// Calcul du total TTC et de la TVA pour la ligne a partir de
1574
		// qty, pu, remise_percent et tvatx
1575
		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1576
		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1577
1578
		$localtaxes_type=getLocalTaxesFromRate($tvatx, 0, $this->societe, $mysoc);
1579
		$tvatx = preg_replace('/\s*\(.*\)/','',$tvatx);  // Remove code into vatrate.
1580
1581
		$tabprice=calcul_price_total($qty, $pu, $remise_percent, $tvatx, $localtax1tx, $localtax2tx, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1582
		$total_ht  = $tabprice[0];
1583
		$total_tva = $tabprice[1];
1584
		$total_ttc = $tabprice[2];
1585
		$total_localtax1= $tabprice[9];
1586
		$total_localtax2= $tabprice[10];
1587
1588
		$localtax1_type=$localtaxes_type[0];
1589
		$localtax2_type=$localtaxes_type[2];
1590
1591
		// TODO A virer
1592
		// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1593
		$remise = 0;
1594
		$price = price2num(round($pu, 2));
1595
		if (dol_strlen($remise_percent) > 0)
1596
		{
1597
		    $remise = round(($pu * $remise_percent / 100), 2);
1598
		    $price = $pu - $remise;
1599
		}
1600
1601
	    if (empty($pa_ht)) $pa_ht=0;
1602
1603
		// if buy price not defined, define buyprice as configured in margin admin
1604
		if ($this->pa_ht == 0)
1605
		{
1606
			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...
1607
			{
1608
				return $result;
1609
			}
1610
			else
1611
			{
1612
				$pa_ht = $result;
1613
			}
1614
		}
1615
1616
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet set description='".$this->db->escape($desc)."'";
1617
		$sql.= ",price_ht='" .     price2num($price)."'";
1618
		$sql.= ",subprice='" .     price2num($subprice)."'";
1619
		$sql.= ",remise='" .       price2num($remise)."'";
1620
		$sql.= ",remise_percent='".price2num($remise_percent)."'";
1621
		$sql.= ",qty='".$qty."'";
1622
		$sql.= ",tva_tx='".        price2num($tvatx)."'";
1623
		$sql.= ",localtax1_tx='".  price2num($localtax1tx)."'";
1624
		$sql.= ",localtax2_tx='".  price2num($localtax2tx)."'";
1625
		$sql.= ",localtax1_type='".$localtax1_type."'";
1626
		$sql.= ",localtax2_type='".$localtax2_type."'";
1627
		$sql.= ", total_ht='".     price2num($total_ht)."'";
1628
		$sql.= ", total_tva='".    price2num($total_tva)."'";
1629
		$sql.= ", total_localtax1='".price2num($total_localtax1)."'";
1630
		$sql.= ", total_localtax2='".price2num($total_localtax2)."'";
1631
		$sql.= ", total_ttc='".      price2num($total_ttc)."'";
1632
		$sql.= ", fk_product_fournisseur_price=".($fk_fournprice > 0 ? $fk_fournprice : "null");
1633
		$sql.= ", buy_price_ht='".price2num($pa_ht)."'";
1634
		if ($date_start > 0) { $sql.= ",date_ouverture_prevue='".$this->db->idate($date_start)."'"; }
1635
		else { $sql.=",date_ouverture_prevue=null"; }
1636
		if ($date_end > 0) { $sql.= ",date_fin_validite='".$this->db->idate($date_end)."'"; }
1637
		else { $sql.=",date_fin_validite=null"; }
1638
		if ($date_debut_reel > 0) { $sql.= ",date_ouverture='".$this->db->idate($date_debut_reel)."'"; }
1639
		else { $sql.=",date_ouverture=null"; }
1640
		if ($date_fin_reel > 0) { $sql.= ",date_cloture='".$this->db->idate($date_fin_reel)."'"; }
1641
		else { $sql.=",date_cloture=null"; }
1642
		$sql .= ", fk_unit=".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
1643
		$sql .= " WHERE rowid = ".$rowid;
1644
1645
		dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
1646
		$result = $this->db->query($sql);
1647
		if ($result)
1648
		{
1649
			$result=$this->update_statut($user);
1650
			if ($result >= 0)
1651
			{
1652
1653
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1654
				{
1655
					$contractline = new ContratLigne($this->db);
1656
					$contractline->array_options=$array_options;
1657
					$contractline->id= $rowid;
1658
					$result=$contractline->insertExtraFields();
1659
					if ($result < 0)
1660
					{
1661
						$this->error[]=$contractline->error;
1662
						$error++;
1663
					}
1664
				}
1665
1666
				if (empty($error)) {
1667
			        // Call trigger
1668
			        $result=$this->call_trigger('LINECONTRACT_UPDATE',$user);
1669
			        if ($result < 0)
1670
			        {
1671
			            $this->db->rollback();
1672
			            return -3;
1673
			        }
1674
			        // End call triggers
1675
1676
					$this->db->commit();
1677
					return 1;
1678
				}
1679
			}
1680
			else
1681
			{
1682
				$this->db->rollback();
1683
				dol_syslog(get_class($this)."::updateligne Erreur -2");
1684
				return -2;
1685
			}
1686
		}
1687
		else
1688
		{
1689
			$this->db->rollback();
1690
			$this->error=$this->db->error();
1691
			dol_syslog(get_class($this)."::updateligne Erreur -1");
1692
			return -1;
1693
		}
1694
	}
1695
1696
	/**
1697
	 *  Delete a contract line
1698
	 *
1699
	 *  @param	int		$idline		Id of line to delete
1700
	 *	@param  User	$user       User that delete
1701
	 *  @return int         		>0 if OK, <0 if KO
1702
	 */
1703
	function deleteline($idline, User $user)
1704
	{
1705
		global $conf, $langs;
1706
1707
		$error=0;
1708
1709
		if ($this->statut >= 0)
1710
		{
1711
1712
		    // Call trigger
1713
		    $result=$this->call_trigger('LINECONTRACT_DELETE',$user);
1714
		    if ($result < 0) return -1;
1715
		    // End call triggers
1716
1717
		    $this->db->begin();
1718
1719
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet";
1720
			$sql.= " WHERE rowid=".$idline;
1721
1722
			dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1723
			$resql = $this->db->query($sql);
1724
			if (! $resql)
1725
			{
1726
				$this->error="Error ".$this->db->lasterror();
1727
				$error++;
1728
			}
1729
1730
			if (empty($error)) {
1731
				// Remove extrafields
1732
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1733
				{
1734
					$contractline = new ContratLigne($this->db);
1735
					$contractline->id= $idline;
1736
					$result=$contractline->deleteExtraFields();
1737
					if ($result < 0)
1738
					{
1739
						$error++;
1740
						$this->error="Error ".get_class($this)."::delete deleteExtraFields error -4 ".$contractline->error;
1741
					}
1742
				}
1743
			}
1744
1745
			if (empty($error)) {
1746
				$this->db->commit();
1747
				return 1;
1748
			} else {
1749
				dol_syslog(get_class($this)."::delete ERROR:".$this->error, LOG_ERR);
1750
				$this->db->rollback();
1751
				return -1;
1752
			}
1753
		}
1754
		else
1755
		{
1756
			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1757
			return -2;
1758
		}
1759
	}
1760
1761
1762
	/**
1763
	 *  Update statut of contract according to services
1764
	 *
1765
	 *	@param	User	$user		Object user
1766
	 *	@return int     			<0 if KO, >0 if OK
1767
	 *  @deprecated					This function will never be used. Status of a contract is status of its lines.
1768
	 */
1769
	function update_statut($user)
1770
	{
1771
		dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING);
1772
1773
		// If draft, we keep it (should not happen)
1774
		if ($this->statut == 0) return 1;
1775
1776
		// Load $this->lines array
1777
		//		$this->fetch_lines();
1778
1779
//		$newstatut=1;
1780
//		foreach($this->lines as $key => $contractline)
1781
//		{
1782
//			//			if ($contractline)         // Loop on each service
1783
//		}
1784
1785
		return 1;
1786
	}
1787
1788
1789
	/**
1790
	 *  Return label of a contract status
1791
	 *
1792
	 *  @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
1793
	 *  @return string      		Label
1794
	 */
1795
	function getLibStatut($mode)
1796
	{
1797
		return $this->LibStatut($this->statut,$mode);
1798
	}
1799
1800
	/**
1801
	 *  Renvoi label of a given contrat status
1802
	 *
1803
	 *  @param	int		$statut      	Status id
1804
	 *  @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
1805
	 *	@return string      			Label
1806
	 */
1807
	function LibStatut($statut,$mode)
1808
	{
1809
		global $langs;
1810
		$langs->load("contracts");
1811
		if ($mode == 0)
1812
		{
1813
			if ($statut == 0) { return $langs->trans("ContractStatusDraft"); }
1814
			if ($statut == 1) { return $langs->trans("ContractStatusValidated"); }
1815
			if ($statut == 2) { return $langs->trans("ContractStatusClosed"); }
1816
		}
1817
		if ($mode == 1)
1818
		{
1819
			if ($statut == 0) { return $langs->trans("ContractStatusDraft"); }
1820
			if ($statut == 1) { return $langs->trans("ContractStatusValidated"); }
1821
			if ($statut == 2) { return $langs->trans("ContractStatusClosed"); }
1822
		}
1823
		if ($mode == 2)
1824
		{
1825
			if ($statut == 0) { return img_picto($langs->trans('ContractStatusDraft'),'statut0').' '.$langs->trans("ContractStatusDraft"); }
1826
			if ($statut == 1) { return img_picto($langs->trans('ContractStatusValidated'),'statut4').' '.$langs->trans("ContractStatusValidated"); }
1827
			if ($statut == 2) { return img_picto($langs->trans('ContractStatusClosed'),'statut6').' '.$langs->trans("ContractStatusClosed"); }
1828
		}
1829
		if ($mode == 3)
1830
		{
1831
			if ($statut == 0) { return img_picto($langs->trans('ContractStatusDraft'),'statut0'); }
1832
			if ($statut == 1) { return img_picto($langs->trans('ContractStatusValidated'),'statut4'); }
1833
			if ($statut == 2) { return img_picto($langs->trans('ContractStatusClosed'),'statut6'); }
1834
		}
1835
		if ($mode == 4 || $mode == 6 || $mode == 7)
1836
		{
1837
			$text='';
1838
			if ($mode == 4)
1839
			{
1840
				$text ='<span class="hideonsmartphone">';
1841
				$text.=($this->nbofserviceswait+$this->nbofservicesopened+$this->nbofservicesexpired+$this->nbofservicesclosed);
1842
				$text.=' '.$langs->trans("Services");
1843
				$text.=': &nbsp; &nbsp; ';
1844
				$text.='</span>';
1845
			}
1846
			$text.=($mode == 7?'<div class="inline-block">':'');
1847
			$text.=($mode != 7 || $this->nbofserviceswait > 0) ? ($this->nbofserviceswait.ContratLigne::LibStatut(0,3,-1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesopened || $this->nbofservicesexpired || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1848
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1849
			$text.=($mode != 7 || $this->nbofservicesopened > 0) ? ($this->nbofservicesopened.ContratLigne::LibStatut(4,3,0,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesexpired || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1850
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1851
			$text.=($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.ContratLigne::LibStatut(4,3,1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1852
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1853
			$text.=($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.ContratLigne::LibStatut(5,3,-1,'class="paddingleft2 inline-block valigntextbottom"')) : '';
1854
			$text.=($mode == 7?'</div>':'');
1855
			return $text;
1856
		}
1857
		if ($mode == 5)
1858
		{
1859
			if ($statut == 0) { return $langs->trans("ContractStatusDraft").' '.img_picto($langs->trans('ContractStatusDraft'),'statut0'); }
1860
			if ($statut == 1) { return $langs->trans("ContractStatusValidated").' '.img_picto($langs->trans('ContractStatusValidated'),'statut4'); }
1861
			if ($statut == 2) { return $langs->trans("ContractStatusClosed").' '.img_picto($langs->trans('ContractStatusClosed'),'statut6'); }
1862
		}
1863
	}
1864
1865
1866
	/**
1867
	 *	Return clicable name (with picto eventually)
1868
	 *
1869
	 *	@param	int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
1870
	 *	@param	int		$maxlength					Max length of ref
1871
     *  @param	int     $notooltip					1=Disable tooltip
1872
     *  @param  int     $save_lastsearch_value		-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1873
	 *	@return	string								Chaine avec URL
1874
	 */
1875
	function getNomUrl($withpicto=0, $maxlength=0, $notooltip=0, $save_lastsearch_value=-1)
1876
	{
1877
		global $conf, $langs, $user;
1878
1879
		$result='';
1880
1881
		$url = DOL_URL_ROOT.'/contrat/card.php?id='.$this->id;
1882
1883
		//if ($option !== 'nolink')
1884
		//{
1885
			// Add param to save lastsearch_values or not
1886
			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1887
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1888
			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1889
		//}
1890
1891
        $label = '';
1892
1893
        if ($user->rights->contrat->lire) {
1894
            $label = '<u>'.$langs->trans("ShowContract").'</u>';
1895
            $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1896
            $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
1897
            $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
1898
            if (!empty($this->total_ht)) {
1899
                $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1900
            }
1901
            if (!empty($this->total_tva)) {
1902
                $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1,	$conf->currency);
1903
            }
1904
            if (!empty($this->total_ttc)) {
1905
                $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1906
            }
1907
        }
1908
1909
        $linkclose='';
1910
        if (empty($notooltip) && $user->rights->contrat->lire)
1911
        {
1912
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1913
            {
1914
                $label=$langs->trans("ShowOrder");
1915
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1916
            }
1917
            $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1918
            $linkclose.=' class="classfortooltip"';
1919
        }
1920
1921
		$linkstart = '<a href="'.$url.'"';
1922
		$linkstart.=$linkclose.'>';
1923
		$linkend='</a>';
1924
1925
		$result .= $linkstart;
1926
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1927
		if ($withpicto != 2) $result.= $this->ref;
1928
		$result .= $linkend;
1929
1930
		return $result;
1931
	}
1932
1933
	/**
1934
	 *  Charge les informations d'ordre info dans l'objet contrat
1935
	 *
1936
	 *  @param  int		$id     id du contrat a charger
1937
	 *  @return	void
1938
	 */
1939
	function info($id)
1940
	{
1941
		$sql = "SELECT c.rowid, c.ref, c.datec, c.date_cloture,";
1942
		$sql.= " c.tms as date_modification,";
1943
		$sql.= " fk_user_author, fk_user_cloture";
1944
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
1945
		$sql.= " WHERE c.rowid = ".$id;
1946
1947
		$result=$this->db->query($sql);
1948
		if ($result)
1949
		{
1950
			if ($this->db->num_rows($result))
1951
			{
1952
				$obj = $this->db->fetch_object($result);
1953
1954
				$this->id = $obj->rowid;
1955
1956
				if ($obj->fk_user_author) {
1957
					$cuser = new User($this->db);
1958
					$cuser->fetch($obj->fk_user_author);
1959
					$this->user_creation     = $cuser;
1960
				}
1961
1962
				if ($obj->fk_user_cloture) {
1963
					$cuser = new User($this->db);
1964
					$cuser->fetch($obj->fk_user_cloture);
1965
					$this->user_cloture = $cuser;
1966
				}
1967
				$this->ref			     = (! $obj->ref) ? $obj->rowid : $obj->ref;
1968
				$this->date_creation     = $this->db->jdate($obj->datec);
1969
				$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...
1970
				$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...
1971
			}
1972
1973
			$this->db->free($result);
1974
1975
		}
1976
		else
1977
		{
1978
			dol_print_error($this->db);
1979
		}
1980
	}
1981
1982
	/**
1983
	 *  Return list of line rowid
1984
	 *
1985
	 *  @param	int		$statut     Status of lines to get
1986
	 *  @return array       		Array of line's rowid
1987
	 */
1988
	function array_detail($statut=-1)
1989
	{
1990
		$tab=array();
1991
1992
		$sql = "SELECT cd.rowid";
1993
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as cd";
1994
		$sql.= " WHERE fk_contrat =".$this->id;
1995
		if ($statut >= 0) $sql.= " AND statut = '$statut'";
1996
1997
		dol_syslog(get_class($this)."::array_detail()", LOG_DEBUG);
1998
		$resql=$this->db->query($sql);
1999
		if ($resql)
2000
		{
2001
			$num=$this->db->num_rows($resql);
2002
			$i=0;
2003
			while ($i < $num)
2004
			{
2005
				$obj = $this->db->fetch_object($resql);
2006
				$tab[$i]=$obj->rowid;
2007
				$i++;
2008
			}
2009
			return $tab;
2010
		}
2011
		else
2012
		{
2013
			$this->error=$this->db->error();
2014
			return -1;
2015
		}
2016
	}
2017
2018
	/**
2019
	 *  Return list of other contracts for same company than current contract
2020
	 *
2021
	 *	@param	string		$option		'all' or 'others'
2022
	 *  @return array   				Array of contracts id
2023
	 */
2024
	function getListOfContracts($option='all')
2025
	{
2026
		$tab=array();
2027
2028
		$sql = "SELECT c.rowid, c.ref";
2029
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2030
		$sql.= " WHERE fk_soc =".$this->socid;
2031
		if ($option == 'others') $sql.= " AND c.rowid != ".$this->id;
2032
2033
		dol_syslog(get_class($this)."::getOtherContracts()", LOG_DEBUG);
2034
		$resql=$this->db->query($sql);
2035
		if ($resql)
2036
		{
2037
			$num=$this->db->num_rows($resql);
2038
			$i=0;
2039
			while ($i < $num)
2040
			{
2041
				$obj = $this->db->fetch_object($resql);
2042
				$contrat=new Contrat($this->db);
2043
				$contrat->fetch($obj->rowid);
2044
				$tab[]=$contrat;
2045
				$i++;
2046
			}
2047
			return $tab;
2048
		}
2049
		else
2050
		{
2051
			$this->error=$this->db->error();
2052
			return -1;
2053
		}
2054
	}
2055
2056
2057
	/**
2058
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2059
     *
2060
     *      @param	User	$user           Objet user
2061
     *      @param  string	$mode           "inactive" pour services a activer, "expired" pour services expires
2062
     *      @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
2063
	 */
2064
	function load_board($user,$mode)
2065
	{
2066
		global $conf, $langs;
2067
2068
		$this->from = " FROM ".MAIN_DB_PREFIX."contrat as c";
2069
		$this->from.= ", ".MAIN_DB_PREFIX."contratdet as cd";
2070
		$this->from.= ", ".MAIN_DB_PREFIX."societe as s";
2071
		if (!$user->rights->societe->client->voir && !$user->societe_id) $this->from.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2072
2073
		if ($mode == 'inactives')
2074
		{
2075
			$sql = "SELECT cd.rowid, cd.date_ouverture_prevue as datefin";
2076
			$sql.= $this->from;
2077
			$sql.= " WHERE c.statut = 1";
2078
			$sql.= " AND c.rowid = cd.fk_contrat";
2079
			$sql.= " AND cd.statut = 0";
2080
		}
2081
		if ($mode == 'expired')
2082
		{
2083
			$sql = "SELECT cd.rowid, cd.date_fin_validite 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 = 4";
2088
			$sql.= " AND cd.date_fin_validite < '".$this->db->idate(time())."'";
2089
		}
2090
		$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...
2091
		$sql.= " AND c.entity = ".$conf->entity;
2092
		if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
2093
		if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id;
2094
		$resql=$this->db->query($sql);
2095
		if ($resql)
2096
		{
2097
			$langs->load("contracts");
2098
			$now=dol_now();
2099
2100
			if ($mode == 'inactives') {
2101
				$warning_delay = $conf->contrat->services->inactifs->warning_delay;
2102
				$label = $langs->trans("BoardNotActivatedServices");
2103
				$url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&amp;leftmenu=contracts&amp;mode=0';
2104
			} else {
2105
				$warning_delay = $conf->contrat->services->expires->warning_delay;
2106
				$url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&amp;leftmenu=contracts&amp;mode=4&amp;filter=expired';
2107
				$label = $langs->trans("BoardRunningServices");
2108
			}
2109
2110
			$response = new WorkboardResponse();
2111
			$response->warning_delay = $warning_delay/60/60/24;
2112
			$response->label = $label;
2113
			$response->url = $url;
2114
			$response->img = img_object('',"contract");
2115
2116
			while ($obj=$this->db->fetch_object($resql))
2117
			{
2118
				$response->nbtodo++;
2119
2120
				if ($obj->datefin && $this->db->jdate($obj->datefin) < ($now - $warning_delay)) {
2121
					$response->nbtodolate++;
2122
				}
2123
			}
2124
2125
			return $response;
2126
		}
2127
		else
2128
		{
2129
			dol_print_error($this->db);
2130
			$this->error=$this->db->error();
2131
			return -1;
2132
		}
2133
	}
2134
2135
	/**
2136
	 *   Charge indicateurs this->nb de tableau de bord
2137
	 *
2138
	 *   @return     int         <0 si ko, >0 si ok
2139
	 */
2140
	function load_state_board()
2141
	{
2142
		global $conf, $user;
2143
2144
		$this->nb=array();
2145
		$clause = "WHERE";
2146
2147
		$sql = "SELECT count(c.rowid) as nb";
2148
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2149
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
2150
		if (!$user->rights->societe->client->voir && !$user->societe_id)
2151
		{
2152
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2153
			$sql.= " WHERE sc.fk_user = " .$user->id;
2154
			$clause = "AND";
2155
		}
2156
		$sql.= " ".$clause." c.entity = ".$conf->entity;
2157
2158
		$resql=$this->db->query($sql);
2159
		if ($resql)
2160
		{
2161
			while ($obj=$this->db->fetch_object($resql))
2162
			{
2163
				$this->nb["Contracts"]=$obj->nb;
2164
			}
2165
            $this->db->free($resql);
2166
			return 1;
2167
		}
2168
		else
2169
		{
2170
			dol_print_error($this->db);
2171
			$this->error=$this->db->error();
2172
			return -1;
2173
		}
2174
	}
2175
2176
2177
	/* gestion des contacts d'un contrat */
2178
2179
	/**
2180
	 *  Return id des contacts clients de facturation
2181
	 *
2182
	 *  @return     array       Liste des id contacts facturation
2183
	 */
2184
	function getIdBillingContact()
2185
	{
2186
		return $this->getIdContact('external','BILLING');
2187
	}
2188
2189
	/**
2190
	 *  Return id des contacts clients de prestation
2191
	 *
2192
	 *  @return     array       Liste des id contacts prestation
2193
	 */
2194
	function getIdServiceContact()
2195
	{
2196
		return $this->getIdContact('external','SERVICE');
2197
	}
2198
2199
2200
	/**
2201
     *  Initialise an instance with random values.
2202
     *  Used to build previews or test instances.
2203
     *	id must be 0 if object instance is a specimen.
2204
     *
2205
     *  @return	void
2206
	 */
2207
	function initAsSpecimen()
2208
	{
2209
		global $user,$langs,$conf;
2210
2211
        // Load array of products prodids
2212
		$num_prods = 0;
2213
		$prodids = array();
2214
		$sql = "SELECT rowid";
2215
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
2216
		$sql.= " WHERE entity IN (".getEntity('product').")";
2217
		$sql.= " AND tosell = 1";
2218
		$resql = $this->db->query($sql);
2219
		if ($resql)
2220
		{
2221
			$num_prods = $this->db->num_rows($resql);
2222
			$i = 0;
2223
			while ($i < $num_prods)
2224
			{
2225
				$i++;
2226
				$row = $this->db->fetch_row($resql);
2227
				$prodids[$i] = $row[0];
2228
			}
2229
		}
2230
2231
		// Initialise parametres
2232
		$this->id=0;
2233
		$this->specimen=1;
2234
2235
		$this->ref = 'SPECIMEN';
2236
		$this->ref_customer = 'SPECIMENCUST';
2237
		$this->ref_supplier = 'SPECIMENSUPP';
2238
		$this->socid = 1;
2239
		$this->statut= 0;
2240
		$this->date_creation = (dol_now() - 3600 * 24 * 7);
2241
		$this->date_contrat = dol_now();
2242
		$this->commercial_signature_id = 1;
2243
		$this->commercial_suivi_id = 1;
2244
		$this->note_private='This is a comment (private)';
2245
		$this->note_public='This is a comment (public)';
2246
		$this->fk_projet = 0;
2247
		// Lines
2248
		$nbp = 5;
2249
		$xnbp = 0;
2250
		while ($xnbp < $nbp)
2251
		{
2252
			$line=new ContratLigne($this->db);
2253
			$line->qty=1;
2254
			$line->subprice=100;
2255
			$line->price=100;
2256
			$line->tva_tx=19.6;
2257
			$line->remise_percent=10;
2258
			$line->total_ht=90;
2259
			$line->total_ttc=107.64;	// 90 * 1.196
2260
			$line->total_tva=17.64;
2261
			$line->date_start = dol_now() - 500000;
2262
			$line->date_start_real = dol_now() - 200000;
2263
			$line->date_end = dol_now() + 500000;
2264
			$line->date_end_real = dol_now() - 100000;
2265
			if ($num_prods > 0)
2266
            {
2267
				$prodid = mt_rand(1, $num_prods);
2268
				$line->fk_product=$prodids[$prodid];
2269
            }
2270
			$this->lines[$xnbp]=$line;
2271
			$xnbp++;
2272
		}
2273
	}
2274
2275
	/**
2276
	 * 	Create an array of order lines
2277
	 *
2278
	 * 	@return int		>0 if OK, <0 if KO
2279
	 */
2280
	function getLinesArray()
2281
	{
2282
		return $this->fetch_lines();
2283
	}
2284
2285
2286
	/**
2287
	 *  Create a document onto disk according to template module.
2288
	 *
2289
	 * 	@param	    string		$modele			Force model to use ('' to not force)
2290
	 * 	@param		Translate	$outputlangs	Object langs to use for output
2291
	 *  @param      int			$hidedetails    Hide details of lines
2292
	 *  @param      int			$hidedesc       Hide description
2293
	 *  @param      int			$hideref        Hide ref
2294
	 * 	@return     int         				0 if KO, 1 if OK
2295
	 */
2296
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
2297
	{
2298
		global $conf,$langs;
2299
2300
		$langs->load("contracts");
2301
2302
		if (! dol_strlen($modele)) {
2303
2304
			$modele = 'strato';
2305
2306
			if ($this->modelpdf) {
2307
				$modele = $this->modelpdf;
2308
			} elseif (! empty($conf->global->CONTRACT_ADDON_PDF)) {
2309
				$modele = $conf->global->CONTRACT_ADDON_PDF;
2310
			}
2311
		}
2312
2313
		$modelpath = "core/modules/contract/doc/";
2314
2315
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2316
	}
2317
2318
	/**
2319
	 * Function used to replace a thirdparty id with another one.
2320
	 *
2321
	 * @param DoliDB $db Database handler
2322
	 * @param int $origin_id Old thirdparty id
2323
	 * @param int $dest_id New thirdparty id
2324
	 * @return bool
2325
	 */
2326
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2327
	{
2328
		$tables = array(
2329
			'contrat'
2330
		);
2331
2332
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2333
	}
2334
2335
	/**
2336
	 * Load an object from its id and create a new one in database
2337
	 *
2338
	 * @param int $socid Id of thirdparty
2339
	 * @param int $notrigger	1=Does not execute triggers, 0= execute triggers
2340
	 * @return int New id of clone
2341
	 */
2342
	function createFromClone($socid = 0, $notrigger=0) {
2343
		global $db, $user, $langs, $conf, $hookmanager;
2344
2345
		dol_include_once('/projet/class/project.class.php');
2346
2347
		$this->context['createfromclone'] = 'createfromclone';
2348
2349
		$error = 0;
2350
2351
		$this->fetch($this->id);
2352
		// Load dest object
2353
		$clonedObj = clone $this;
2354
        $clonedObj->socid = $socid;
2355
2356
		$this->db->begin();
2357
2358
		$objsoc = new Societe($this->db);
2359
2360
		$objsoc->fetch($clonedObj->socid);
2361
2362
		// $clonedObj->id=0;
2363
		$clonedObj->statut = 0;
2364
2365
		if (empty($conf->global->CONTRACT_ADDON) || ! is_readable(DOL_DOCUMENT_ROOT . "/core/modules/contract/" . $conf->global->CONTRACT_ADDON . ".php")) {
2366
			$this->error = 'ErrorSetupNotComplete';
2367
			dol_syslog($this->error);
2368
			return - 1;
2369
		}
2370
2371
		// Set ref
2372
		require_once DOL_DOCUMENT_ROOT . "/core/modules/contract/" . $conf->global->CONTRACT_ADDON . '.php';
2373
		$obj = $conf->global->CONTRACT_ADDON;
2374
		$modContract = new $obj();
2375
		$clonedObj->ref = $modContract->getNextValue($objsoc, $clonedObj);
2376
2377
		// get extrafields so they will be clone
2378
		foreach ( $this->lines as $line ) {
2379
			$line->fetch_optionals($line->rowid);
2380
		}
2381
2382
		// Create clone
2383
		$result = $clonedObj->create($user);
2384
		if ($result < 0) {
2385
			$error ++;
2386
			$this->error = $clonedObj->error;
2387
			$this->errors[] = $clonedObj->error;
2388
		} else {
2389
            // copy external contacts if same company
2390
            if ($this->socid == $clonedObj->socid) {
2391
                if ($clonedObj->copy_linked_contact($this, 'external') < 0) {
2392
                    $error++;
2393
                }
2394
            }
2395
        }
2396
2397
		if (! $error) {
2398
			foreach ( $this->lines as $line ) {
2399
				$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);
2400
				if ($result < 0) {
2401
					$error ++;
2402
					$this->error = $clonedObj->error;
2403
					$this->errors[] = $clonedObj->error;
2404
				}
2405
			}
2406
		}
2407
2408
		if (! $error) {
2409
			// Hook of thirdparty module
2410
			if (is_object($hookmanager)) {
2411
				$parameters = array (
2412
						'objFrom' => $this,
2413
						'clonedObj' => $clonedObj
2414
				);
2415
				$action = '';
2416
				$reshook = $hookmanager->executeHooks('createFrom', $parameters, $clonedObj, $action); // Note that $action and $object may have been modified by some hooks
2417
				if ($reshook < 0)
2418
					$error ++;
2419
			}
2420
2421
		}
2422
2423
		unset($this->context['createfromclone']);
2424
2425
		// End
2426
		if (! $error) {
2427
			$this->db->commit();
2428
			return $clonedObj->id;
2429
		} else {
2430
			$this->db->rollback();
2431
			return - 1;
2432
		}
2433
	}
2434
}
2435
2436
2437
/**
2438
 *	Classe permettant la gestion des lignes de contrats
2439
 */
2440
class ContratLigne extends CommonObjectLine
2441
{
2442
    public $element='contratdet';
2443
    public $table_element='contratdet';
2444
2445
	var $id;
2446
	var $ref;
2447
	var $tms;
2448
2449
	var $fk_contrat;
2450
	var $fk_product;
2451
	var $statut;					// 0 inactive, 4 active, 5 closed
2452
	var $type;						// 0 for product, 1 for service
2453
	/**
2454
	 * @var string
2455
	 * @deprecated
2456
	 */
2457
	var $label;
2458
	/**
2459
	 * @var string
2460
	 * @deprecated
2461
	 */
2462
	public $libelle;
2463
2464
	var $description;
2465
2466
	var $product_ref;
2467
	var $product_label;
2468
2469
	var $date_commande;
2470
2471
	var $date_start;				// date start planned
2472
	var $date_start_real;			// date start real
2473
	var $date_end;					// date end planned
2474
	var $date_end_real;				// date end real
2475
	// For backward compatibility
2476
	var $date_ouverture_prevue;		// date start planned
2477
	var $date_ouverture;			// date start real
2478
	var $date_fin_validite;			// date end planned
2479
	var $date_cloture;				// date end real
2480
	var $tva_tx;
2481
	var $localtax1_tx;
2482
	var $localtax2_tx;
2483
	var $localtax1_type;	// Local tax 1 type
2484
	var $localtax2_type;	// Local tax 2 type
2485
	var $qty;
2486
	var $remise_percent;
2487
	var $remise;
2488
	var $fk_remise_except;
2489
2490
	var $subprice;					// Unit price HT
2491
2492
	/**
2493
	 * @var float
2494
	 * @deprecated Use $price_ht instead
2495
	 * @see price_ht
2496
	 */
2497
	public $price;
2498
2499
	var $price_ht;
2500
2501
	var $total_ht;
2502
	var $total_tva;
2503
	var $total_localtax1;
2504
	var $total_localtax2;
2505
	var $total_ttc;
2506
2507
	var $fk_fournprice;
2508
	var $pa_ht;
2509
2510
	var $info_bits;
2511
	var $fk_user_author;
2512
	var $fk_user_ouverture;
2513
	var $fk_user_cloture;
2514
	var $commentaire;
2515
2516
	const STATUS_INITIAL = 0;
2517
	const STATUS_OPEN = 4;
2518
	const STATUS_CLOSED = 5;
2519
2520
2521
2522
	/**
2523
     *  Constructor
2524
     *
2525
     *  @param      DoliDb		$db      Database handler
2526
	 */
2527
	function __construct($db)
2528
	{
2529
		$this->db = $db;
2530
	}
2531
2532
2533
	/**
2534
	 *  Return label of this contract line status
2535
	 *
2536
	 *	@param	int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
2537
	 *  @return string      		Libelle
2538
	 */
2539
	function getLibStatut($mode)
2540
	{
2541
		return $this->LibStatut($this->statut,$mode,((! empty($this->date_fin_validite))?($this->date_fin_validite < dol_now()?1:0):-1));
2542
	}
2543
2544
	/**
2545
	 *  Return label of a contract line status
2546
	 *
2547
	 *  @param	int		$statut     Id statut
2548
	 *	@param  int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
2549
	 *	@param	int		$expired	0=Not expired, 1=Expired, -1=Both or unknown
2550
	 *  @param	string	$moreatt	More attribute
2551
	 *  @return string      		Libelle
2552
	 */
2553
	static function LibStatut($statut,$mode,$expired=-1,$moreatt='')
2554
	{
2555
		global $langs;
2556
		$langs->load("contracts");
2557
		if ($mode == 0)
2558
		{
2559
			if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial"); }
2560
			if ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning"); }
2561
			if ($statut == self::STATUS_OPEN && $expired == 0)  { return $langs->trans("ServiceStatusNotLate"); }
2562
			if ($statut == self::STATUS_OPEN && $expired == 1)  { return $langs->trans("ServiceStatusLate"); }
2563
			if ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed");  }
2564
		}
2565
		if ($mode == 1)
2566
		{
2567
			if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial"); }
2568
			if ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning"); }
2569
			if ($statut == self::STATUS_OPEN && $expired == 0)  { return $langs->trans("ServiceStatusNotLateShort"); }
2570
			if ($statut == self::STATUS_OPEN && $expired == 1)  { return $langs->trans("ServiceStatusLateShort"); }
2571
			if ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed");  }
2572
		}
2573
		if ($mode == 2)
2574
		{
2575
			if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); }
2576
			if ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); }
2577
			if ($statut == self::STATUS_OPEN && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLateShort"); }
2578
			if ($statut == self::STATUS_OPEN && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLateShort"); }
2579
			if ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); }
2580
		}
2581
		if ($mode == 3)
2582
		{
2583
			if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0',$moreatt); }
2584
			if ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4',$moreatt); }
2585
			if ($statut == self::STATUS_OPEN && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4',$moreatt); }
2586
			if ($statut == self::STATUS_OPEN && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'),'statut3',$moreatt); }
2587
			if ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6',$moreatt); }
2588
		}
2589
		if ($mode == 4)
2590
		{
2591
			if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); }
2592
			if ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); }
2593
			if ($statut == self::STATUS_OPEN && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLate"); }
2594
			if ($statut == self::STATUS_OPEN && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLate"); }
2595
			if ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); }
2596
		}
2597
		if ($mode == 5)
2598
		{
2599
			if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial").' '.img_picto($langs->trans('ServiceStatusInitial'),'statut0'); }
2600
			if ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning").' '.img_picto($langs->trans('ServiceStatusRunning'),'statut4'); }
2601
			if ($statut == self::STATUS_OPEN && $expired == 0)  { return $langs->trans("ServiceStatusNotLateShort").' '.img_picto($langs->trans('ServiceStatusNotLateShort'),'statut4'); }
2602
			if ($statut == self::STATUS_OPEN && $expired == 1)  { return $langs->trans("ServiceStatusLateShort").' '.img_picto($langs->trans('ServiceStatusLate'),'statut3'); }
2603
			if ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed").' '.img_picto($langs->trans('ServiceStatusClosed'),'statut6'); }
2604
		}
2605
	}
2606
2607
	/**
2608
	 *	Return clicable name (with picto eventually)
2609
	 *
2610
	 *  @param	int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
2611
	 *  @param	int		$maxlength		Max length
2612
	 *  @return	string					Chaine avec URL
2613
 	 */
2614
	function getNomUrl($withpicto=0,$maxlength=0)
2615
	{
2616
		global $langs;
2617
2618
		$result='';
2619
        $label=$langs->trans("ShowContractOfService").': '.$this->label;
2620
        if (empty($label)) $label=$this->description;
2621
2622
        $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
2623
		$linkend='</a>';
2624
2625
		$picto='service';
2626
		if ($this->type == 0) $picto='product';
2627
2628
        if ($withpicto) $result.=($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
2629
		if ($withpicto && $withpicto != 2) $result.=' ';
2630
		if ($withpicto != 2) $result.=$link.($this->product_ref?$this->product_ref.' ':'').($this->label?$this->label:$this->description).$linkend;
2631
		return $result;
2632
	}
2633
2634
	/**
2635
	 *    	Load object in memory from database
2636
	 *
2637
	 *    	@param	int		$id         Id object
2638
	 * 		@param	string	$ref		Ref of contract
2639
	 *    	@return int         		<0 if KO, >0 if OK
2640
	 */
2641
	function fetch($id, $ref='')
2642
	{
2643
2644
		// Check parameters
2645
		if (empty($id) && empty($ref)) return -1;
2646
2647
		$sql = "SELECT";
2648
		$sql.= " t.rowid,";
2649
2650
		$sql.= " t.tms,";
2651
		$sql.= " t.fk_contrat,";
2652
		$sql.= " t.fk_product,";
2653
		$sql.= " t.statut,";
2654
		$sql.= " t.label,";			// This field is not used. Only label of product
2655
		$sql.= " p.ref as product_ref,";
2656
		$sql.= " p.label as product_label,";
2657
		$sql.= " p.description as product_desc,";
2658
		$sql.= " p.fk_product_type as product_type,";
2659
		$sql.= " t.description,";
2660
		$sql.= " t.date_commande,";
2661
		$sql.= " t.date_ouverture_prevue as date_ouverture_prevue,";
2662
		$sql.= " t.date_ouverture as date_ouverture,";
2663
		$sql.= " t.date_fin_validite as date_fin_validite,";
2664
		$sql.= " t.date_cloture as date_cloture,";
2665
		$sql.= " t.tva_tx,";
2666
		$sql.= " t.vat_src_code,";
2667
		$sql.= " t.localtax1_tx,";
2668
		$sql.= " t.localtax2_tx,";
2669
		$sql.= " t.localtax1_type,";
2670
		$sql.= " t.localtax2_type,";
2671
		$sql.= " t.qty,";
2672
		$sql.= " t.remise_percent,";
2673
		$sql.= " t.remise,";
2674
		$sql.= " t.fk_remise_except,";
2675
		$sql.= " t.subprice,";
2676
		$sql.= " t.price_ht,";
2677
		$sql.= " t.total_ht,";
2678
		$sql.= " t.total_tva,";
2679
		$sql.= " t.total_localtax1,";
2680
		$sql.= " t.total_localtax2,";
2681
		$sql.= " t.total_ttc,";
2682
		$sql.= " t.fk_product_fournisseur_price as fk_fournprice,";
2683
		$sql.= " t.buy_price_ht as pa_ht,";
2684
		$sql.= " t.info_bits,";
2685
		$sql.= " t.fk_user_author,";
2686
		$sql.= " t.fk_user_ouverture,";
2687
		$sql.= " t.fk_user_cloture,";
2688
		$sql.= " t.commentaire,";
2689
		$sql.= " t.fk_unit";
2690
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
2691
		if ($id)  $sql.= " WHERE t.rowid = ".$id;
2692
		if ($ref) $sql.= " WHERE t.rowid = '".$this->db->escape($ref)."'";
2693
2694
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2695
		$resql=$this->db->query($sql);
2696
		if ($resql)
2697
		{
2698
			if ($this->db->num_rows($resql))
2699
			{
2700
				$obj = $this->db->fetch_object($resql);
2701
2702
				$this->id    = $obj->rowid;
2703
				$this->ref   = $obj->rowid;
2704
2705
				$this->tms = $this->db->jdate($obj->tms);
2706
				$this->fk_contrat = $obj->fk_contrat;
2707
				$this->fk_product = $obj->fk_product;
2708
				$this->statut = $obj->statut;
2709
				$this->product_ref = $obj->product_ref;
2710
				$this->product_label = $obj->product_label;
2711
				$this->product_description = $obj->product_description;
2712
				$this->product_type = $obj->product_type;
2713
				$this->label = $obj->label;					// deprecated. We do not use this field. Only ref and label of product, and description of contract line
2714
				$this->description = $obj->description;
2715
				$this->date_commande = $this->db->jdate($obj->date_commande);
2716
2717
				$this->date_start = $this->db->jdate($obj->date_ouverture_prevue);
2718
				$this->date_start_real = $this->db->jdate($obj->date_ouverture);
2719
				$this->date_end = $this->db->jdate($obj->date_fin_validite);
2720
				$this->date_end_real = $this->db->jdate($obj->date_cloture);
2721
				// For backward compatibility
2722
				$this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
2723
				$this->date_ouverture = $this->db->jdate($obj->date_ouverture);
2724
				$this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
2725
				$this->date_cloture = $this->db->jdate($obj->date_cloture);
2726
2727
				$this->tva_tx = $obj->tva_tx;
2728
				$this->vat_src_code = $obj->vat_src_code;
2729
				$this->localtax1_tx = $obj->localtax1_tx;
2730
				$this->localtax2_tx = $obj->localtax2_tx;
2731
				$this->localtax1_type = $obj->localtax1_type;
2732
				$this->localtax2_type = $obj->localtax2_type;
2733
				$this->qty = $obj->qty;
2734
				$this->remise_percent = $obj->remise_percent;
2735
				$this->remise = $obj->remise;
2736
				$this->fk_remise_except = $obj->fk_remise_except;
2737
				$this->subprice = $obj->subprice;
2738
				$this->price_ht = $obj->price_ht;
2739
				$this->total_ht = $obj->total_ht;
2740
				$this->total_tva = $obj->total_tva;
2741
				$this->total_localtax1 = $obj->total_localtax1;
2742
				$this->total_localtax2 = $obj->total_localtax2;
2743
				$this->total_ttc = $obj->total_ttc;
2744
				$this->info_bits = $obj->info_bits;
2745
				$this->fk_user_author = $obj->fk_user_author;
2746
				$this->fk_user_ouverture = $obj->fk_user_ouverture;
2747
				$this->fk_user_cloture = $obj->fk_user_cloture;
2748
				$this->commentaire = $obj->commentaire;
2749
				$this->fk_fournprice = $obj->fk_fournprice;
2750
				$marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
2751
				$this->pa_ht = $marginInfos[0];
2752
				$this->fk_unit     = $obj->fk_unit;
2753
2754
			}
2755
			$this->db->free($resql);
2756
2757
			return 1;
2758
		}
2759
		else
2760
		{
2761
			$this->error="Error ".$this->db->lasterror();
2762
			return -1;
2763
		}
2764
	}
2765
2766
2767
	/**
2768
	 *      Update database for contract line
2769
	 *
2770
	 *      @param	User	$user        	User that modify
2771
	 *      @param  int		$notrigger	    0=no, 1=yes (no update trigger)
2772
	 *      @return int         			<0 if KO, >0 if OK
2773
	 */
2774
	function update($user, $notrigger=0)
2775
	{
2776
		global $conf, $langs, $mysoc;
2777
2778
		$error=0;
2779
2780
		// Clean parameters
2781
		$this->fk_contrat=trim($this->fk_contrat);
2782
		$this->fk_product=trim($this->fk_product);
2783
		$this->statut=(int) $this->statut;
2784
		$this->label=trim($this->label);
2785
		$this->description=trim($this->description);
2786
		$this->vat_src_code=trim($this->vat_src_code);
2787
		$this->tva_tx=trim($this->tva_tx);
2788
		$this->localtax1_tx=trim($this->localtax1_tx);
2789
		$this->localtax2_tx=trim($this->localtax2_tx);
2790
		$this->qty=trim($this->qty);
2791
		$this->remise_percent=trim($this->remise_percent);
2792
		$this->remise=trim($this->remise);
2793
		$this->fk_remise_except=trim($this->fk_remise_except);
2794
		$this->subprice=price2num($this->subprice);
2795
		$this->price_ht=price2num($this->price_ht);
2796
		$this->total_ht=trim($this->total_ht);
2797
		$this->total_tva=trim($this->total_tva);
2798
		$this->total_localtax1=trim($this->total_localtax1);
2799
		$this->total_localtax2=trim($this->total_localtax2);
2800
		$this->total_ttc=trim($this->total_ttc);
2801
		$this->info_bits=trim($this->info_bits);
2802
		$this->fk_user_author=trim($this->fk_user_author);
2803
		$this->fk_user_ouverture=trim($this->fk_user_ouverture);
2804
		$this->fk_user_cloture=trim($this->fk_user_cloture);
2805
		$this->commentaire=trim($this->commentaire);
2806
		//if (empty($this->subprice)) $this->subprice = 0;
2807
		if (empty($this->price_ht)) $this->price_ht = 0;
2808
		if (empty($this->total_ht)) $this->total_ht = 0;
2809
		if (empty($this->total_tva)) $this->total_tva = 0;
2810
		if (empty($this->total_ttc)) $this->total_ttc = 0;
2811
		if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
2812
		if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
2813
		if (empty($this->remise_percent)) $this->remise_percent = 0;
2814
		// For backward compatibility
2815
		if (empty($this->date_start))      $this->date_start=$this->date_ouverture_prevue;
2816
		if (empty($this->date_start_real)) $this->date_start=$this->date_ouverture;
2817
		if (empty($this->date_end))        $this->date_start=$this->date_fin_validite;
2818
		if (empty($this->date_end_real))   $this->date_start=$this->date_cloture;
2819
2820
2821
		// Check parameters
2822
		// Put here code to add control on parameters values
2823
2824
		// Calcul du total TTC et de la TVA pour la ligne a partir de
2825
		// qty, pu, remise_percent et txtva
2826
		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2827
		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2828
		$localtaxes_type = getLocalTaxesFromRate($this->txtva, 0, $this->societe, $mysoc);
2829
2830
		$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);
2831
		$this->total_ht  = $tabprice[0];
2832
		$this->total_tva = $tabprice[1];
2833
		$this->total_ttc = $tabprice[2];
2834
		$this->total_localtax1= $tabprice[9];
2835
		$this->total_localtax2= $tabprice[10];
2836
2837
	    if (empty($this->pa_ht)) $this->pa_ht=0;
2838
2839
		// if buy price not defined, define buyprice as configured in margin admin
2840
		if ($this->pa_ht == 0)
2841
		{
2842
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
2843
			{
2844
				return $result;
2845
			}
2846
			else
2847
			{
2848
				$this->pa_ht = $result;
2849
			}
2850
		}
2851
2852
2853
		$this->db->begin();
2854
2855
		$this->oldcopy = new ContratLigne($this->db);
2856
		$this->oldcopy->fetch($this->id);
2857
2858
		// Update request
2859
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
2860
		$sql.= " fk_contrat=".$this->fk_contrat.",";
2861
		$sql.= " fk_product=".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":'null').",";
2862
		$sql.= " statut=".$this->statut.",";
2863
		$sql.= " label='".$this->db->escape($this->label)."',";
2864
		$sql.= " description='".$this->db->escape($this->description)."',";
2865
		$sql.= " date_commande=".($this->date_commande!=''?"'".$this->db->idate($this->date_commande)."'":"null").",";
2866
		$sql.= " date_ouverture_prevue=".($this->date_ouverture_prevue!=''?"'".$this->db->idate($this->date_ouverture_prevue)."'":"null").",";
2867
		$sql.= " date_ouverture=".($this->date_ouverture!=''?"'".$this->db->idate($this->date_ouverture)."'":"null").",";
2868
		$sql.= " date_fin_validite=".($this->date_fin_validite!=''?"'".$this->db->idate($this->date_fin_validite)."'":"null").",";
2869
		$sql.= " date_cloture=".($this->date_cloture!=''?"'".$this->db->idate($this->date_cloture)."'":"null").",";
2870
		$sql.= " vat_src_code='".$this->db->escape($this->vat_src_code)."',";
2871
		$sql.= " tva_tx=".price2num($this->tva_tx).",";
2872
		$sql.= " localtax1_tx=".price2num($this->localtax1_tx).",";
2873
		$sql.= " localtax2_tx=".price2num($this->localtax2_tx).",";
2874
		$sql.= " qty=".price2num($this->qty).",";
2875
		$sql.= " remise_percent=".price2num($this->remise_percent).",";
2876
		$sql.= " remise=".($this->remise?price2num($this->remise):"null").",";
2877
		$sql.= " fk_remise_except=".($this->fk_remise_except > 0?$this->fk_remise_except:"null").",";
2878
		$sql.= " subprice=".($this->subprice != '' ? $this->subprice : "null").",";
2879
		$sql.= " price_ht=".($this->price_ht != '' ? $this->price_ht : "null").",";
2880
		$sql.= " total_ht=".$this->total_ht.",";
2881
		$sql.= " total_tva=".$this->total_tva.",";
2882
		$sql.= " total_localtax1=".$this->total_localtax1.",";
2883
		$sql.= " total_localtax2=".$this->total_localtax2.",";
2884
		$sql.= " total_ttc=".$this->total_ttc.",";
2885
		$sql.= " fk_product_fournisseur_price=".(!empty($this->fk_fournprice)?$this->fk_fournprice:"NULL").",";
2886
		$sql.= " buy_price_ht='".price2num($this->pa_ht)."',";
2887
		$sql.= " info_bits='".$this->db->escape($this->info_bits)."',";
2888
		$sql.= " fk_user_author=".($this->fk_user_author >= 0?$this->fk_user_author:"NULL").",";
2889
		$sql.= " fk_user_ouverture=".($this->fk_user_ouverture > 0?$this->fk_user_ouverture:"NULL").",";
2890
		$sql.= " fk_user_cloture=".($this->fk_user_cloture > 0?$this->fk_user_cloture:"NULL").",";
2891
		$sql.= " commentaire='".$this->db->escape($this->commentaire)."',";
2892
		$sql.= " fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
2893
		$sql.= " WHERE rowid=".$this->id;
2894
2895
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
2896
		$resql = $this->db->query($sql);
2897
		if (! $resql)
2898
		{
2899
			$this->error="Error ".$this->db->lasterror();
2900
			$error++;
2901
		}
2902
2903
		if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
2904
		{
2905
			$result=$this->insertExtraFields();
2906
			if ($result < 0)
2907
			{
2908
				$error++;
2909
			}
2910
		}
2911
2912
		// If we change a planned date (start or end), sync dates for all services
2913
		if (! $error && ! empty($conf->global->CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES))
2914
		{
2915
			if ($this->date_ouverture_prevue != $this->oldcopy->date_ouverture_prevue)
2916
			{
2917
				$sql ='UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
2918
				$sql.= " date_ouverture_prevue = ".($this->date_ouverture_prevue!=''?"'".$this->db->idate($this->date_ouverture_prevue)."'":"null");
2919
				$sql.= " WHERE fk_contrat = ".$this->fk_contrat;
2920
2921
				$resql = $this->db->query($sql);
2922
				if (! $resql)
2923
				{
2924
					$error++;
2925
					$this->error="Error ".$this->db->lasterror();
2926
				}
2927
			}
2928
			if ($this->date_fin_validite != $this->oldcopy->date_fin_validite)
2929
			{
2930
				$sql ='UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
2931
				$sql.= " date_fin_validite = ".($this->date_fin_validite!=''?"'".$this->db->idate($this->date_fin_validite)."'":"null");
2932
				$sql.= " WHERE fk_contrat = ".$this->fk_contrat;
2933
2934
				$resql = $this->db->query($sql);
2935
				if (! $resql)
2936
				{
2937
					$error++;
2938
					$this->error="Error ".$this->db->lasterror();
2939
				}
2940
			}
2941
		}
2942
2943
		if (! $error)
2944
		{
2945
			if (! $notrigger)
2946
			{
2947
	            // Call trigger
2948
	            $result=$this->call_trigger('LINECONTRACT_UPDATE', $user);
2949
	            if ($result < 0) { $error++; $this->db->rollback(); }
2950
	            // End call triggers
2951
			}
2952
		}
2953
2954
		if (! $error)
2955
		{
2956
        	$this->db->commit();
2957
			return 1;
2958
		} else {
2959
			$this->db->rollback();
2960
			$this->errors[]=$this->error;
2961
			return -1;
2962
		}
2963
	}
2964
2965
2966
	/**
2967
	 *      Mise a jour en base des champs total_xxx de ligne
2968
	 *		Used by migration process
2969
	 *
2970
	 *		@return		int		<0 if KO, >0 if OK
2971
	 */
2972
	function update_total()
2973
	{
2974
		$this->db->begin();
2975
2976
		// Mise a jour ligne en base
2977
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
2978
		$sql.= " total_ht=".price2num($this->total_ht,'MT')."";
2979
		$sql.= ",total_tva=".price2num($this->total_tva,'MT')."";
2980
		$sql.= ",total_localtax1=".price2num($this->total_localtax1,'MT')."";
2981
		$sql.= ",total_localtax2=".price2num($this->total_localtax2,'MT')."";
2982
		$sql.= ",total_ttc=".price2num($this->total_ttc,'MT')."";
2983
		$sql.= " WHERE rowid = ".$this->id;
2984
2985
		dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
2986
2987
		$resql=$this->db->query($sql);
2988
		if ($resql)
2989
		{
2990
			$this->db->commit();
2991
			return 1;
2992
		}
2993
		else
2994
		{
2995
			$this->error=$this->db->error();
2996
			$this->db->rollback();
2997
			return -2;
2998
		}
2999
	}
3000
3001
3002
	/**
3003
	 * Inserts a contrat line into database
3004
	 *
3005
	 * @param int $notrigger Set to 1 if you don't want triggers to be fired
3006
	 * @return int <0 if KO, >0 if OK
3007
	 */
3008
	public function insert($notrigger = 0)
3009
	{
3010
		global $conf, $user;
3011
3012
		// Insertion dans la base
3013
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
3014
		$sql.= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
3015
		$sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
3016
		$sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
3017
		$sql.= " info_bits,";
3018
		$sql.= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
3019
		if ($this->date_ouverture_prevue > 0) { $sql.= ",date_ouverture_prevue"; }
3020
		if ($this->date_fin_validite > 0)     { $sql.= ",date_fin_validite"; }
3021
		$sql.= ") VALUES ($this->fk_contrat, '', '" . $this->db->escape($this->description) . "',";
3022
		$sql.= ($this->fk_product>0 ? $this->fk_product : "null").",";
3023
		$sql.= " '".$this->db->escape($this->qty)."',";
3024
		$sql.= " '".$this->db->escape($this->vat_src_code)."',";
3025
		$sql.= " '".$this->db->escape($this->tva_tx)."',";
3026
		$sql.= " '".$this->db->escape($this->localtax1_tx)."',";
3027
		$sql.= " '".$this->db->escape($this->localtax2_tx)."',";
3028
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
3029
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
3030
		$sql.= " ".price2num($this->remise_percent).",".price2num($this->subprice).",";
3031
		$sql.= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
3032
		$sql.= " '".$this->db->escape($this->info_bits)."',";
3033
		$sql.= " ".price2num($this->price_ht).",".price2num($this->remise).",";
3034
		if ($this->fk_fournprice > 0) $sql.= ' '.$this->fk_fournprice.',';
3035
		else $sql.= ' null,';
3036
		if ($this->pa_ht > 0) $sql.= ' '.price2num($this->pa_ht);
3037
		else $sql.= ' null';
3038
		if ($this->date_ouverture > 0) { $sql.= ",'".$this->db->idate($this->date_ouverture)."'"; }
3039
		if ($this->date_cloture > 0)   { $sql.= ",'".$this->db->idate($this->date_cloture)."'"; }
3040
		$sql.= ")";
3041
3042
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3043
3044
		$resql=$this->db->query($sql);
3045
		if ($resql)
3046
		{
3047
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
3048
3049
			// Insert of extrafields
3050
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
3051
			{
3052
				$result = $this->insertExtraFields();
3053
				if ($result < 0)
3054
				{
3055
					$this->db->rollback();
3056
					return -1;
3057
				}
3058
			}
3059
3060
			if (!$notrigger)
3061
			{
3062
				// Call trigger
3063
				$result = $this->call_trigger('LINECONTRACT_INSERT', $user);
3064
				if ($result < 0) {
3065
					$this->db->rollback();
3066
					return -1;
3067
				}
3068
				// End call triggers
3069
			}
3070
3071
			$this->db->commit();
3072
			return 1;
3073
		}
3074
		else
3075
		{
3076
			$this->db->rollback();
3077
			$this->error=$this->db->error()." sql=".$sql;
3078
			return -1;
3079
		}
3080
	}
3081
3082
	/**
3083
	 *  Activate a contract line
3084
	 *
3085
	 * @param   User 		$user 		Objet User who activate contract
3086
	 * @param  	int 		$date 		Date activation
3087
	 * @param  	int|string 	$date_end 	Date planned end. Use '-1' to keep it unchanged.
3088
	 * @param   string 		$comment 	A comment typed by user
3089
	 * @return 	int                    	<0 if KO, >0 if OK
3090
	 */
3091
	function active_line($user, $date, $date_end = '', $comment = '')
3092
	{
3093
		global $langs, $conf;
3094
3095
		$error = 0;
3096
3097
		$this->db->begin();
3098
3099
		$sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = ".ContratLigne::STATUS_OPEN.",";
3100
		$sql .= " date_ouverture = " . (dol_strlen($date) != 0 ? "'" . $this->db->idate($date) . "'" : "null") . ",";
3101
		if ($date_end >= 0) $sql .= " date_fin_validite = " . (dol_strlen($date_end) != 0 ? "'" . $this->db->idate($date_end) . "'" : "null") . ",";
3102
		$sql .= " fk_user_ouverture = " . $user->id . ",";
3103
		$sql .= " date_cloture = null,";
3104
		$sql .= " commentaire = '" . $this->db->escape($comment) . "'";
3105
		$sql .= " WHERE rowid = " . $this->id . " AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")";
3106
3107
		dol_syslog(get_class($this) . "::active_line", LOG_DEBUG);
3108
		$resql = $this->db->query($sql);
3109
		if ($resql) {
3110
			// Call trigger
3111
			$result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
3112
			if ($result < 0) $error++;
3113
			// End call triggers
3114
3115
			if (! $error)
3116
			{
3117
				$this->statut = ContratLigne::STATUS_OPEN;
3118
				$this->date_ouverture = $date;
3119
				$this->date_fin_validite = $date_end;
3120
				$this->fk_user_ouverture = $user->id;
3121
				$this->date_cloture = null;
3122
				$this->commentaire = $comment;
3123
3124
				$this->db->commit();
3125
				return 1;
3126
			}
3127
			else
3128
			{
3129
				$this->db->rollback();
3130
				return -1;
3131
			}
3132
		} else {
3133
			$this->error = $this->db->lasterror();
3134
			$this->db->rollback();
3135
			return -1;
3136
		}
3137
	}
3138
3139
	/**
3140
	 *  Close a contract line
3141
	 *
3142
	 * @param    User 	$user 			Objet User who close contract
3143
	 * @param  	 int 	$date_end 		Date end
3144
	 * @param    string $comment 		A comment typed by user
3145
     * @param    int	$notrigger		1=Does not execute triggers, 0=Execute triggers
3146
	 * @return int                    	<0 if KO, >0 if OK
3147
	 */
3148
	function close_line($user, $date_end, $comment = '', $notrigger=0)
3149
	{
3150
		global $langs, $conf;
3151
3152
		// Update object
3153
		$this->date_cloture = $date_end;
3154
		$this->fk_user_cloture = $user->id;
3155
		$this->commentaire = $comment;
3156
3157
		$error = 0;
3158
3159
		// statut actif : 4
3160
3161
		$this->db->begin();
3162
3163
		$sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = ".ContratLigne::STATUS_CLOSED.",";
3164
		$sql .= " date_cloture = '" . $this->db->idate($date_end) . "',";
3165
		$sql .= " fk_user_cloture = " . $user->id . ",";
3166
		$sql .= " commentaire = '" . $this->db->escape($comment) . "'";
3167
		$sql .= " WHERE rowid = " . $this->id . " AND statut = ".ContratLigne::STATUS_OPEN;
3168
3169
		$resql = $this->db->query($sql);
3170
		if ($resql)
3171
		{
3172
			if (! $notrigger)
3173
			{
3174
				// Call trigger
3175
				$result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
3176
				if ($result < 0) {
3177
					$error++;
3178
					$this->db->rollback();
3179
					return -1;
3180
				}
3181
				// End call triggers
3182
			}
3183
3184
			$this->db->commit();
3185
			return 1;
3186
		} else {
3187
			$this->error = $this->db->lasterror();
3188
			$this->db->rollback();
3189
			return -1;
3190
		}
3191
	}
3192
}
3193