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

Contrat::close_line()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 4
dl 0
loc 10
rs 10
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) 2018   	Nicolas ZABOURI			<[email protected]>
12
 * Copyright (C) 2018       Frédéric France         <[email protected]>
13
 * Copyright (C) 2015-2018	Ferran Marcet			<[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 *	\file       htdocs/contrat/class/contrat.class.php
31
 *	\ingroup    contrat
32
 *	\brief      File of class to manage contracts
33
 */
34
35
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
36
require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
37
require_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
38
require_once DOL_DOCUMENT_ROOT . '/margin/lib/margins.lib.php';
39
40
/**
41
 *	Class to manage contracts
42
 */
43
class Contrat extends CommonObject
44
{
45
	/**
46
	 * @var string ID to identify managed object
47
	 */
48
	public $element='contrat';
49
50
	/**
51
	 * @var string Name of table without prefix where object is stored
52
	 */
53
	public $table_element='contrat';
54
55
	/**
56
	 * @var int    Name of subtable line
57
	 */
58
	public $table_element_line='contratdet';
59
60
	/**
61
	 * @var int Field with ID of parent key if this field has a parent
62
	 */
63
	public $fk_element='fk_contrat';
64
65
    /**
66
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
67
	 */
68
	public $picto='contract';
69
70
    /**
71
     * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
72
     * @var int
73
     */
74
    public $ismultientitymanaged = 1;
75
76
    /**
77
     * 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
78
     * @var integer
79
     */
80
    public $restrictiononfksoc = 1;
81
82
	/**
83
	 * {@inheritdoc}
84
	 */
85
	protected $table_ref_field = 'ref';
86
87
	/**
88
	 * Customer reference of the contract
89
	 * @var string
90
	 */
91
	public $ref_customer;
92
93
	/**
94
	 * Supplier reference of the contract
95
	 * @var string
96
	 */
97
	public $ref_supplier;
98
99
	/**
100
	 * Client id linked to the contract
101
	 * @var int
102
	 */
103
	public $socid;
104
105
	public $societe;		// Objet societe
106
107
	/**
108
	 * Status of the contract
109
	 * @var int
110
	 */
111
	public $statut=0;		// 0=Draft,
112
113
	public $product;
114
115
	/**
116
	 * @var int		Id of user author of the contract
117
	 */
118
	public $fk_user_author;
119
120
	/**
121
	 * TODO: Which is the correct one?
122
	 * Author of the contract
123
	 * @var int
124
	 */
125
	public $user_author_id;
126
127
	/**
128
	 * @var User 	Object user that create the contract. Set by the info method.
129
	 */
130
	public $user_creation;
131
132
	/**
133
	 * @var User 	Object user that close the contract. Set by the info method.
134
	 */
135
	public $user_cloture;
136
137
	/**
138
	 * @var int		Date of creation
139
	 */
140
	public $date_creation;
141
142
	/**
143
	 * @var int		Date of last modification. Not filled until you call ->info()
144
	 */
145
	public $date_modification;
146
147
	/**
148
	 * @var int		Date of validation
149
	 */
150
	public $date_validation;
151
152
	/**
153
	 * @var int		Date when contract was signed
154
	 */
155
	public $date_contrat;
156
157
	/**
158
	 * @var int		Date of contract closure
159
	 * @deprecated we close contract lines, not a contract
160
	 */
161
	public $date_cloture;
162
163
	public $commercial_signature_id;
164
	public $commercial_suivi_id;
165
166
	/**
167
	 * @deprecated Use fk_project instead
168
	 * @see $fk_project
169
	 */
170
	public $fk_projet;
171
172
	public $extraparams=array();
173
174
	/**
175
	 * @var ContratLigne[]		Contract lines
176
	 */
177
	public $lines=array();
178
179
	/**
180
	 * Maps ContratLigne IDs to $this->lines indexes
181
	 * @var int[]
182
	 */
183
	protected $lines_id_index_mapper=array();
184
185
186
	/**
187
	 *	Constructor
188
	 *
189
	 *  @param		DoliDB		$db      Database handler
190
	 */
191
	public function __construct($db)
192
	{
193
		$this->db = $db;
194
	}
195
196
	/**
197
	 *	Return next contract ref
198
	 *
199
	 *	@param	Societe		$soc		Thirdparty object
200
	 *	@return string					free reference for contract
201
	 */
202
	public function getNextNumRef($soc)
203
	{
204
		global $db, $langs, $conf;
205
		$langs->load("contracts");
206
207
		if (!empty($conf->global->CONTRACT_ADDON))
208
		{
209
			$mybool = false;
210
211
			$file = $conf->global->CONTRACT_ADDON.".php";
212
			$classname = $conf->global->CONTRACT_ADDON;
213
214
			// Include file with class
215
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
216
217
			foreach ($dirmodels as $reldir) {
218
219
				$dir = dol_buildpath($reldir."core/modules/contract/");
220
221
				// Load file with numbering class (if found)
222
				$mybool|=@include_once $dir.$file;
223
			}
224
225
			if (! $mybool)
1 ignored issue
show
Bug Best Practice introduced by
The expression $mybool of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
226
			{
227
				dol_print_error('', "Failed to include file ".$file);
228
				return '';
229
			}
230
231
			$obj = new $classname();
232
			$numref = $obj->getNextValue($soc, $this);
233
234
			if ( $numref != "")
235
			{
236
				return $numref;
237
			}
238
			else
239
			{
240
				$this->error = $obj->error;
241
				dol_print_error($db, get_class($this)."::getNextValue ".$obj->error);
242
				return "";
243
			}
244
		}
245
		else
246
		{
247
			$langs->load("errors");
248
			print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
249
			return "";
250
		}
251
	}
252
253
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
254
	/**
255
	 *  Activate a contract line
256
	 *
257
	 *  @param	User		$user       Objet User who activate contract
258
	 *  @param  int			$line_id    Id of line to activate
259
	 *  @param  int			$date       Opening date
260
	 *  @param  int|string	$date_end   Expected end date
261
	 * 	@param	string		$comment	A comment typed by user
262
	 *  @return int         			<0 if KO, >0 if OK
263
	 */
264
	public function active_line($user, $line_id, $date, $date_end = '', $comment = '')
265
	{
266
        // phpcs:enable
267
		$result = $this->lines[$this->lines_id_index_mapper[$line_id]]->active_line($user, $date, $date_end, $comment);
268
		if ($result < 0)
269
		{
270
			$this->error = $this->lines[$this->lines_id_index_mapper[$line_id]]->error;
271
			$this->errors = $this->lines[$this->lines_id_index_mapper[$line_id]]->errors;
272
		}
273
		return $result;
274
	}
275
276
277
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
278
	/**
279
	 *  Close a contract line
280
	 *
281
	 *  @param	User		$user       Objet User who close contract
282
	 *  @param  int			$line_id    Id of line to close
283
	 *  @param  int			$date_end	End date
284
	 * 	@param	string		$comment	A comment typed by user
285
	 *  @return int         			<0 if KO, >0 if OK
286
	 */
287
	public function close_line($user, $line_id, $date_end, $comment = '')
288
	{
289
        // phpcs:enable
290
		$result=$this->lines[$this->lines_id_index_mapper[$line_id]]->close_line($user, $date_end, $comment);
291
		if ($result < 0)
292
		{
293
			$this->error = $this->lines[$this->lines_id_index_mapper[$line_id]]->error;
294
			$this->errors = $this->lines[$this->lines_id_index_mapper[$line_id]]->errors;
295
		}
296
		return $result;
297
	}
298
299
300
	/**
301
	 *  Open all lines of a contract
302
	 *
303
	 *  @param	User		$user      		Object User making action
304
	 *  @param	int|string	$date_start		Date start (now if empty)
305
     *  @param	int			$notrigger		1=Does not execute triggers, 0=Execute triggers
306
     *  @param	string		$comment		Comment
307
	 *	@return	int							<0 if KO, >0 if OK
308
	 *  @see ()
309
	 */
310
	public function activateAll($user, $date_start = '', $notrigger = 0, $comment = '')
311
	{
312
		if (empty($date_start)) $date_start = dol_now();
313
314
		$this->db->begin();
315
316
		$error=0;
317
318
		// Load lines
319
		$this->fetch_lines();
320
321
		foreach($this->lines as $contratline)
322
		{
323
			// Open lines not already open
324
			if ($contratline->statut != ContratLigne::STATUS_OPEN)
325
			{
326
				$contratline->context = $this->context;
327
328
				$result = $contratline->active_line($user, $date_start, -1, $comment);
329
				if ($result < 0)
330
				{
331
					$error++;
332
					$this->error = $contratline->error;
333
					$this->errors = $contratline->errors;
334
					break;
335
				}
336
			}
337
		}
338
339
		if (! $error && $this->statut == 0)
340
		{
341
			$result=$this->validate($user, '', $notrigger);
342
			if ($result < 0) $error++;
343
		}
344
345
		if (! $error)
346
		{
347
			$this->db->commit();
348
			return 1;
349
		}
350
		else
351
		{
352
			$this->db->rollback();
353
			return -1;
354
		}
355
	}
356
357
	/**
358
	 * Close all lines of a contract
359
	 *
360
	 * @param	User		$user      		Object User making action
361
     * @param	int			$notrigger		1=Does not execute triggers, 0=Execute triggers
362
     * @param	string		$comment		Comment
363
	 * @return	int							<0 if KO, >0 if OK
364
	 * @see activateAll()
365
	 */
366
	public function closeAll(User $user, $notrigger = 0, $comment = '')
367
	{
368
		$this->db->begin();
369
370
		// Load lines
371
		$this->fetch_lines();
372
373
		$now = dol_now();
374
375
		$error = 0;
376
377
		foreach($this->lines as $contratline)
378
		{
379
			// Close lines not already closed
380
			if ($contratline->statut != ContratLigne::STATUS_CLOSED)
381
	        {
382
				$contratline->date_cloture=$now;
383
				$contratline->fk_user_cloture=$user->id;
384
				$contratline->statut=ContratLigne::STATUS_CLOSED;
385
				$result=$contratline->close_line($user, $now, $comment, $notrigger);
386
				if ($result < 0)
387
				{
388
					$error++;
389
					$this->error = $contratline->error;
390
					$this->errors = $contratline->errors;
391
					break;
392
				}
393
	        }
394
		}
395
396
		if (! $error && $this->statut == 0)
397
		{
398
			$result=$this->validate($user, '', $notrigger);
399
			if ($result < 0) $error++;
400
		}
401
402
        if (! $error)
403
        {
404
            $this->db->commit();
405
            return 1;
406
        }
407
        else
408
        {
409
            $this->db->rollback();
410
            return -1;
411
        }
412
	}
413
414
	/**
415
	 * Validate a contract
416
	 *
417
	 * @param	User	$user      		Objet User
418
	 * @param   string	$force_number	Reference to force on contract (not implemented yet)
419
     * @param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
420
	 * @return	int						<0 if KO, >0 if OK
421
	 */
422
	public function validate(User $user, $force_number = '', $notrigger = 0)
423
	{
424
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
425
		global $langs, $conf;
426
427
		$now=dol_now();
428
429
		$error=0;
430
		dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number);
431
432
433
		$this->db->begin();
434
435
		$this->fetch_thirdparty();
436
437
		// A contract is validated so we can move thirdparty to status customer
438
		if (empty($conf->global->CONTRACT_DISABLE_AUTOSET_AS_CLIENT_ON_CONTRACT_VALIDATION))
439
		{
440
			$result=$this->thirdparty->set_as_client();
441
		}
442
443
		// Define new ref
444
		if ($force_number)
445
		{
446
			$num = $force_number;
447
		}
448
		elseif (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
449
		{
450
			$num = $this->getNextNumRef($this->thirdparty);
451
		}
452
		else
453
		{
454
			$num = $this->ref;
455
		}
456
        $this->newref = $num;
457
458
		if ($num)
459
		{
460
			$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET ref = '".$num."', statut = 1";
461
			//$sql.= ", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
462
			$sql .= " WHERE rowid = ".$this->id . " AND statut = 0";
463
464
			dol_syslog(get_class($this)."::validate", LOG_DEBUG);
465
			$resql = $this->db->query($sql);
466
			if (! $resql)
467
			{
468
				dol_print_error($this->db);
469
				$error++;
470
				$this->error=$this->db->lasterror();
471
			}
472
473
			// Trigger calls
474
			if (! $error && ! $notrigger)
475
			{
476
                // Call trigger
477
                $result=$this->call_trigger('CONTRACT_VALIDATE', $user);
478
                if ($result < 0) { $error++; }
479
                // End call triggers
480
			}
481
482
			if (! $error)
483
			{
484
            	$this->oldref = $this->ref;
485
486
				// Rename directory if dir was a temporary ref
487
				if (preg_match('/^[\(]?PROV/i', $this->ref))
488
				{
489
					// Now we rename also files into index
490
					$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref)+1).")), filepath = 'contract/".$this->db->escape($this->newref)."'";
491
					$sql.= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'contract/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
492
					$resql = $this->db->query($sql);
493
					if (! $resql) { $error++; $this->error = $this->db->lasterror(); }
494
495
					// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
496
					$oldref = dol_sanitizeFileName($this->ref);
497
					$newref = dol_sanitizeFileName($num);
498
					$dirsource = $conf->contract->dir_output.'/'.$oldref;
499
					$dirdest = $conf->contract->dir_output.'/'.$newref;
500
					if (! $error && file_exists($dirsource))
501
					{
502
						dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
503
504
						if (@rename($dirsource, $dirdest))
505
						{
506
							dol_syslog("Rename ok");
507
						    // Rename docs starting with $oldref with $newref
508
            				$listoffiles=dol_dir_list($conf->contract->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
509
            				foreach($listoffiles as $fileentry)
510
            				{
511
            					$dirsource=$fileentry['name'];
512
            					$dirdest=preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
513
            					$dirsource=$fileentry['path'].'/'.$dirsource;
514
            					$dirdest=$fileentry['path'].'/'.$dirdest;
515
            					@rename($dirsource, $dirdest);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
516
            				}
517
						}
518
					}
519
				}
520
			}
521
522
			// Set new ref and define current statut
523
			if (! $error)
524
			{
525
				$this->ref = $num;
526
				$this->statut = 1;
527
				$this->brouillon = 0;
528
				$this->date_validation = $now;
529
			}
530
		}
531
		else
532
		{
533
			$error++;
534
		}
535
536
		if (! $error)
537
		{
538
			$this->db->commit();
539
			return 1;
540
		}
541
		else
542
		{
543
			$this->db->rollback();
544
			return -1;
545
		}
546
	}
547
548
	/**
549
	 * Unvalidate a contract
550
	 *
551
	 * @param	User	$user      		Object User
552
     * @param	int		$notrigger		1=Does not execute triggers, 0=execute triggers
553
	 * @return	int						<0 if KO, >0 if OK
554
	 */
555
	public function reopen($user, $notrigger = 0)
556
	{
557
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
558
		global $langs, $conf;
559
560
		$now=dol_now();
561
562
		$error=0;
563
		dol_syslog(get_class($this).'::reopen user='.$user->id);
564
565
		$this->db->begin();
566
567
		$this->fetch_thirdparty();
568
569
		$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET statut = 0";
570
		//$sql.= ", fk_user_valid = null, date_valid = null";
571
		$sql .= " WHERE rowid = ".$this->id . " AND statut = 1";
572
573
		dol_syslog(get_class($this)."::validate", LOG_DEBUG);
574
		$resql = $this->db->query($sql);
575
		if (! $resql)
576
		{
577
			dol_print_error($this->db);
578
			$error++;
579
			$this->error=$this->db->lasterror();
580
		}
581
582
		// Trigger calls
583
		if (! $error && ! $notrigger)
584
		{
585
			// Call trigger
586
			$result=$this->call_trigger('CONTRACT_REOPEN', $user);
587
			if ($result < 0) {
588
				$error++;
589
			}
590
			// End call triggers
591
		}
592
593
		// Set new ref and define current status
594
		if (! $error)
595
		{
596
			$this->statut=0;
597
			$this->brouillon=1;
598
			$this->date_validation=$now;
599
		}
600
601
		if (! $error)
602
		{
603
			$this->db->commit();
604
			return 1;
605
		}
606
		else
607
		{
608
			$this->db->rollback();
609
			return -1;
610
		}
611
	}
612
613
	/**
614
	 *    Load a contract from database
615
	 *
616
	 *    @param	int		$id     		Id of contract to load
617
	 *    @param	string	$ref			Ref
618
	 *    @param	string	$ref_customer	Customer ref
619
	 *    @param	string	$ref_supplier	Supplier ref
620
	 *    @return   int     				<0 if KO, 0 if not found, Id of contract if OK
621
	 */
622
	public function fetch($id, $ref = '', $ref_customer = '', $ref_supplier = '')
623
	{
624
		$sql = "SELECT rowid, statut, ref, fk_soc, mise_en_service as datemise,";
625
		$sql.= " ref_supplier, ref_customer,";
626
		$sql.= " ref_ext,";
627
		$sql.= " fk_user_mise_en_service, date_contrat as datecontrat,";
628
		$sql.= " fk_user_author, fin_validite, date_cloture,";
629
		$sql.= " fk_projet as fk_project,";
630
		$sql.= " fk_commercial_signature, fk_commercial_suivi,";
631
		$sql.= " note_private, note_public, model_pdf, extraparams";
632
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat";
633
		if (! $id) $sql.=" WHERE entity IN (".getEntity('contract').")";
634
		else $sql.= " WHERE rowid=".$id;
635
		if ($ref_customer)
636
		{
637
			$sql.= " AND ref_customer = '".$this->db->escape($ref_customer)."'";
638
		}
639
		if ($ref_supplier)
640
		{
641
			$sql.= " AND ref_supplier = '".$this->db->escape($ref_supplier)."'";
642
		}
643
		if ($ref)
644
		{
645
			$sql.= " AND ref='".$this->db->escape($ref)."'";
646
		}
647
648
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
649
		$resql = $this->db->query($sql);
650
		if ($resql)
651
		{
652
			$obj = $this->db->fetch_object($resql);
653
654
			if ($obj)
655
			{
656
				$this->id						= $obj->rowid;
657
				$this->ref						= (!isset($obj->ref) || !$obj->ref) ? $obj->rowid : $obj->ref;
658
				$this->ref_customer				= $obj->ref_customer;
659
				$this->ref_supplier				= $obj->ref_supplier;
660
				$this->ref_ext					= $obj->ref_ext;
661
				$this->statut					= $obj->statut;
662
				$this->mise_en_service			= $this->db->jdate($obj->datemise);
663
664
				$this->date_contrat				= $this->db->jdate($obj->datecontrat);
665
				$this->date_creation			= $this->db->jdate($obj->datecontrat);
666
667
				$this->fin_validite				= $this->db->jdate($obj->fin_validite);
668
				$this->date_cloture				= $this->db->jdate($obj->date_cloture);
669
670
671
				$this->user_author_id			= $obj->fk_user_author;
672
673
				$this->commercial_signature_id = $obj->fk_commercial_signature;
674
				$this->commercial_suivi_id		= $obj->fk_commercial_suivi;
675
676
				$this->note_private				= $obj->note_private;
677
				$this->note_public				= $obj->note_public;
678
				$this->modelpdf					= $obj->model_pdf;
679
680
				$this->fk_projet				= $obj->fk_project; // deprecated
681
				$this->fk_project				= $obj->fk_project;
682
683
				$this->socid					= $obj->fk_soc;
684
				$this->fk_soc					= $obj->fk_soc;
685
686
				$this->extraparams = (array) json_decode($obj->extraparams, true);
687
688
				$this->db->free($resql);
689
690
				// Retreive all extrafields
691
				// fetch optionals attributes and labels
692
				$this->fetch_optionals();
693
694
				// Lines
695
				$result=$this->fetch_lines();
696
				if ($result < 0)
697
				{
698
					$this->error=$this->db->lasterror();
699
					return -3;
700
				}
701
702
				return $this->id;
703
			}
704
			else
705
			{
706
				dol_syslog(get_class($this)."::fetch Contract not found");
707
				$this->error="Contract not found";
708
				return 0;
709
			}
710
		}
711
		else
712
		{
713
			dol_syslog(get_class($this)."::fetch Error searching contract");
714
			$this->error=$this->db->error();
715
			return -1;
716
		}
717
	}
718
719
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
720
	/**
721
	 *  Load lines array into this->lines.
722
	 *  This set also nbofserviceswait, nbofservicesopened, nbofservicesexpired and nbofservicesclosed
723
	 *
724
	 *	@param		int		$only_product	Return only physical products
725
	 *	@param		int		$loadalsotranslation	Return translation for products
726
	 *
727
	 *  @return ContratLigne[]   Return array of contract lines
728
	 */
729
	public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
730
	{
731
		global $langs, $conf;
732
        // phpcs:enable
733
		$this->nbofserviceswait=0;
734
		$this->nbofservicesopened=0;
735
		$this->nbofservicesexpired=0;
736
		$this->nbofservicesclosed=0;
737
738
		$total_ttc=0;
739
		$total_vat=0;
740
		$total_ht=0;
741
742
		$now=dol_now();
743
744
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
745
		$extrafieldsline=new ExtraFields($this->db);
746
		$line = new ContratLigne($this->db);
747
		$extralabelsline=$extrafieldsline->fetch_name_optionals_label($line->table_element, true);
748
749
		$this->lines=array();
750
        $pos = 0;
751
752
		// Selects contract lines related to a product
753
		$sql = "SELECT p.label as product_label, p.description as product_desc, p.ref as product_ref,";
754
		$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,";
755
		$sql.= " d.total_ht,";
756
		$sql.= " d.total_tva,";
757
		$sql.= " d.total_localtax1,";
758
		$sql.= " d.total_localtax2,";
759
		$sql.= " d.total_ttc,";
760
		$sql.= " d.info_bits, d.fk_product,";
761
		$sql.= " d.date_ouverture_prevue, d.date_ouverture,";
762
		$sql.= " d.date_fin_validite, d.date_cloture,";
763
		$sql.= " d.fk_user_author,";
764
		$sql.= " d.fk_user_ouverture,";
765
		$sql.= " d.fk_user_cloture,";
766
		$sql.= " d.fk_unit";
767
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as d LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
768
		$sql.= " WHERE d.fk_contrat = ".$this->id;
769
		$sql.= " ORDER by d.rowid ASC";
770
771
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
772
		$result = $this->db->query($sql);
773
		if ($result)
774
		{
775
			$num = $this->db->num_rows($result);
776
			$i = 0;
777
778
			while ($i < $num)
779
			{
780
				$objp = $this->db->fetch_object($result);
781
782
				$line					= new ContratLigne($this->db);
783
				$line->id				= $objp->rowid;
784
				$line->ref				= $objp->rowid;
785
				$line->fk_contrat		= $objp->fk_contrat;
786
				$line->desc				= $objp->description;  // Description line
787
				$line->qty				= $objp->qty;
788
				$line->vat_src_code 	= $objp->vat_src_code ;
789
				$line->tva_tx			= $objp->tva_tx;
790
				$line->localtax1_tx		= $objp->localtax1_tx;
791
				$line->localtax2_tx		= $objp->localtax2_tx;
792
				$line->localtax1_type	= $objp->localtax1_type;
793
				$line->localtax2_type	= $objp->localtax2_type;
794
				$line->subprice			= $objp->subprice;
795
				$line->statut			= $objp->statut;
796
				$line->remise_percent	= $objp->remise_percent;
797
				$line->price_ht			= $objp->price_ht;
798
				$line->price			= $objp->price_ht;	// For backward compatibility
799
				$line->total_ht			= $objp->total_ht;
800
				$line->total_tva		= $objp->total_tva;
801
				$line->total_localtax1	= $objp->total_localtax1;
802
				$line->total_localtax2	= $objp->total_localtax2;
803
				$line->total_ttc		= $objp->total_ttc;
804
				$line->fk_product		= (($objp->fk_product > 0)?$objp->fk_product:0);
805
				$line->info_bits		= $objp->info_bits;
806
807
				$line->fk_fournprice 	= $objp->fk_fournprice;
808
				$marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
809
				$line->pa_ht 			= $marginInfos[0];
810
811
				$line->fk_user_author	= $objp->fk_user_author;
812
				$line->fk_user_ouverture= $objp->fk_user_ouverture;
813
				$line->fk_user_cloture  = $objp->fk_user_cloture;
814
				$line->fk_unit           = $objp->fk_unit;
815
816
				$line->ref				= $objp->product_ref;	// deprecated
817
				$line->product_ref		= $objp->product_ref;   // Product Ref
818
				$line->product_desc		= $objp->product_desc;  // Product Description
819
				$line->product_label	= $objp->product_label; // Product Label
820
821
				$line->description		= $objp->description;
822
823
				$line->date_start            = $this->db->jdate($objp->date_ouverture_prevue);
824
				$line->date_start_real       = $this->db->jdate($objp->date_ouverture);
825
				$line->date_end              = $this->db->jdate($objp->date_fin_validite);
826
				$line->date_end_real         = $this->db->jdate($objp->date_cloture);
827
				// For backward compatibility
828
				$line->date_ouverture_prevue = $this->db->jdate($objp->date_ouverture_prevue);
829
				$line->date_ouverture        = $this->db->jdate($objp->date_ouverture);
830
				$line->date_fin_validite     = $this->db->jdate($objp->date_fin_validite);
831
				$line->date_cloture          = $this->db->jdate($objp->date_cloture);
832
				$line->date_debut_prevue = $this->db->jdate($objp->date_ouverture_prevue);
833
				$line->date_debut_reel   = $this->db->jdate($objp->date_ouverture);
834
				$line->date_fin_prevue   = $this->db->jdate($objp->date_fin_validite);
835
				$line->date_fin_reel     = $this->db->jdate($objp->date_cloture);
836
837
				// Retreive all extrafields for contract
838
				// fetch optionals attributes and labels
839
				$line->fetch_optionals();
840
841
				// multilangs
842
        		if (! empty($conf->global->MAIN_MULTILANGS) && ! empty($objp->fk_product) && ! empty($loadalsotranslation)) {
843
        		$line = new Product($this->db);
844
        		$line->fetch($objp->fk_product);
845
        		$line->getMultiLangs();
846
        		}
847
848
				$this->lines[$pos] = $line;
849
				$this->lines_id_index_mapper[$line->id] = $pos;
850
851
				//dol_syslog("1 ".$line->desc);
852
				//dol_syslog("2 ".$line->product_desc);
853
854
				if ($line->statut == ContratLigne::STATUS_INITIAL) $this->nbofserviceswait++;
855
				if ($line->statut == ContratLigne::STATUS_OPEN && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) $this->nbofservicesopened++;
856
				if ($line->statut == ContratLigne::STATUS_OPEN && (! empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) $this->nbofservicesexpired++;
857
				if ($line->statut == ContratLigne::STATUS_CLOSED) $this->nbofservicesclosed++;
858
859
				$total_ttc+=$objp->total_ttc;   // TODO Not saved into database
860
                $total_vat+=$objp->total_tva;
861
                $total_ht+=$objp->total_ht;
862
863
				$i++;
864
				$pos++;
865
			}
866
			$this->db->free($result);
867
		}
868
		else
869
		{
870
			dol_syslog(get_class($this)."::Fetch Error when reading lines of contracts linked to products");
871
			return -3;
872
		}
873
874
		$this->nbofservices=count($this->lines);
875
        $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
876
        $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
877
        $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
878
879
		return $this->lines;
880
	}
881
882
	/**
883
	 *  Create a contract into database
884
	 *
885
	 *  @param	User	$user       User that create
886
	 *  @return int  				<0 if KO, id of contract if OK
887
	 */
888
	public function create($user)
889
	{
890
		global $conf,$langs,$mysoc;
891
892
		// Check parameters
893
		$paramsok=1;
894
		if ($this->commercial_signature_id <= 0)
895
		{
896
			$langs->load("commercial");
897
			$this->error.=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("SalesRepresentativeSignature"));
898
			$paramsok=0;
899
		}
900
		if ($this->commercial_suivi_id <= 0)
901
		{
902
			$langs->load("commercial");
903
			$this->error.=($this->error?"<br>":'');
904
			$this->error.=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("SalesRepresentativeFollowUp"));
905
			$paramsok=0;
906
		}
907
		if (! $paramsok) return -1;
908
909
910
		$this->db->begin();
911
912
		$now=dol_now();
913
914
		// Insert contract
915
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."contrat (datec, fk_soc, fk_user_author, date_contrat,";
916
		$sql.= " fk_commercial_signature, fk_commercial_suivi, fk_projet,";
917
		$sql.= " ref, entity, note_private, note_public, ref_customer, ref_supplier, ref_ext)";
918
		$sql.= " VALUES ('".$this->db->idate($now)."',".$this->socid.",".$user->id;
919
		$sql.= ", ".(dol_strlen($this->date_contrat)!=0 ? "'".$this->db->idate($this->date_contrat)."'" : "NULL");
920
		$sql.= ",".($this->commercial_signature_id>0?$this->commercial_signature_id:"NULL");
921
		$sql.= ",".($this->commercial_suivi_id>0?$this->commercial_suivi_id:"NULL");
922
		$sql.= ",".($this->fk_project>0?$this->fk_project:"NULL");
923
		$sql.= ", ".(dol_strlen($this->ref)<=0 ? "null" : "'".$this->db->escape($this->ref)."'");
924
		$sql.= ", ".$conf->entity;
925
		$sql.= ", ".(!empty($this->note_private)?("'".$this->db->escape($this->note_private)."'"):"NULL");
926
		$sql.= ", ".(!empty($this->note_public)?("'".$this->db->escape($this->note_public)."'"):"NULL");
927
		$sql.= ", ".(!empty($this->ref_customer)?("'".$this->db->escape($this->ref_customer)."'"):"NULL");
928
		$sql.= ", ".(!empty($this->ref_supplier)?("'".$this->db->escape($this->ref_supplier)."'"):"NULL");
929
		$sql.= ", ".(!empty($this->ref_ext)?("'".$this->db->escape($this->ref_ext)."'"):"NULL");
930
		$sql.= ")";
931
		$resql=$this->db->query($sql);
932
933
		if ($resql)
934
		{
935
			$error=0;
936
937
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."contrat");
938
939
			// Load object modContract
940
			$module=(! empty($conf->global->CONTRACT_ADDON)?$conf->global->CONTRACT_ADDON:'mod_contract_serpis');
941
			if (substr($module, 0, 13) == 'mod_contract_' && substr($module, -3) == 'php')
942
			{
943
				$module = substr($module, 0, dol_strlen($module)-4);
944
			}
945
			$result=dol_include_once('/core/modules/contract/'.$module.'.php');
946
			if ($result > 0)
947
			{
948
				$modCodeContract = new $module();
949
950
				if (! empty($modCodeContract->code_auto)) {
951
					// Force the ref to a draft value if numbering module is an automatic numbering
952
					$sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".$this->id;
953
					if ($this->db->query($sql))
954
					{
955
						if ($this->id)
956
						{
957
							$this->ref="(PROV".$this->id.")";
958
						}
959
					}
960
				}
961
			}
962
963
			if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))
964
			{
965
				$result=$this->insertExtraFields();
966
				if ($result < 0)
967
				{
968
					$error++;
969
				}
970
			}
971
972
			// Insert business contacts ('SALESREPSIGN','contrat')
973
			if (! $error)
974
			{
975
    			$result=$this->add_contact($this->commercial_signature_id, 'SALESREPSIGN', 'internal');
976
    			if ($result < 0) $error++;
977
			}
978
979
			// Insert business contacts ('SALESREPFOLL','contrat')
980
			if (! $error)
981
			{
982
                $result=$this->add_contact($this->commercial_suivi_id, 'SALESREPFOLL', 'internal');
983
			    if ($result < 0) $error++;
984
			}
985
986
			if (! $error)
987
			{
988
				if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
989
				{
990
					$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
991
				}
992
993
				// Add object linked
994
    			if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
995
    			{
996
    			    foreach($this->linked_objects as $origin => $tmp_origin_id)
997
    			    {
998
    			        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, ...))
999
    			        {
1000
    			            foreach($tmp_origin_id as $origin_id)
1001
    			            {
1002
    			                $ret = $this->add_object_linked($origin, $origin_id);
1003
    			                if (! $ret)
1004
    			                {
1005
    			                    $this->error=$this->db->lasterror();
1006
    			                    $error++;
1007
    			                }
1008
    			            }
1009
    			        }
1010
    			        else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1011
    			        {
1012
    			            $origin_id = $tmp_origin_id;
1013
    			            $ret = $this->add_object_linked($origin, $origin_id);
1014
    			            if (! $ret)
1015
    			            {
1016
    			                $this->error=$this->db->lasterror();
1017
    			                $error++;
1018
    			            }
1019
    			        }
1020
    			    }
1021
    			}
1022
1023
    			if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id))   // Get contact from origin object
1024
    			{
1025
    			    $originforcontact = $this->origin;
1026
    			    $originidforcontact = $this->origin_id;
1027
    			    if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
1028
    			    {
1029
    			        require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
1030
    			        $exp = new Expedition($db);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $db seems to be never defined.
Loading history...
1031
    			        $exp->fetch($this->origin_id);
1032
    			        $exp->fetchObjectLinked();
1033
    			        if (count($exp->linkedObjectsIds['commande']) > 0)
1034
    			        {
1035
    			            foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
1036
    			            {
1037
    			                $originforcontact = 'commande';
1038
    			                $originidforcontact = $value->id;
1039
    			                break; // We take first one
1040
    			            }
1041
    			        }
1042
    			    }
1043
1044
    			    $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";
1045
    			    $sqlcontact.= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
1046
1047
    			    $resqlcontact = $this->db->query($sqlcontact);
1048
    			    if ($resqlcontact)
1049
    			    {
1050
    			        while($objcontact = $this->db->fetch_object($resqlcontact))
1051
    			        {
1052
    			            if ($objcontact->source == 'internal' && in_array($objcontact->code, array('SALESREPSIGN', 'SALESREPFOLL'))) continue;    // ignore this, already forced previously
1053
1054
    			            //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1055
    			            $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
1056
    			        }
1057
    			    }
1058
    			    else dol_print_error($resqlcontact);
1059
    			}
1060
			}
1061
1062
			if (! $error)
1063
			{
1064
                // Call trigger
1065
                $result=$this->call_trigger('CONTRACT_CREATE', $user);
1066
                if ($result < 0) { $error++; }
1067
                // End call triggers
1068
1069
				if (! $error)
1070
				{
1071
					$this->db->commit();
1072
					return $this->id;
1073
				}
1074
				else
1075
				{
1076
					dol_syslog(get_class($this)."::create - 30 - ".$this->error, LOG_ERR);
1077
					$this->db->rollback();
1078
					return -3;
1079
				}
1080
			}
1081
			else
1082
			{
1083
				$this->error="Failed to add contract";
1084
				dol_syslog(get_class($this)."::create - 20 - ".$this->error, LOG_ERR);
1085
				$this->db->rollback();
1086
				return -2;
1087
			}
1088
		}
1089
		else
1090
		{
1091
			$this->error=$langs->trans("UnknownError: ".$this->db->error()." -", LOG_DEBUG);
1092
1093
			$this->db->rollback();
1094
			return -1;
1095
		}
1096
	}
1097
1098
1099
	/**
1100
	 *  Supprime l'objet de la base
1101
	 *
1102
	 *  @param	User		$user       Utilisateur qui supprime
1103
	 *  @return int         			< 0 si erreur, > 0 si ok
1104
	 */
1105
	public function delete($user)
1106
	{
1107
		global $conf, $langs;
1108
		require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1109
1110
		$error=0;
1111
1112
		$this->db->begin();
1113
1114
	    // Call trigger
1115
	    $result=$this->call_trigger('CONTRACT_DELETE', $user);
1116
	    if ($result < 0) { $error++; }
1117
	    // End call triggers
1118
1119
		if (! $error)
1120
		{
1121
			// Delete linked contacts
1122
			$res = $this->delete_linked_contact();
1123
			if ($res < 0)
1124
			{
1125
				dol_syslog(get_class($this)."::delete error", LOG_ERR);
1126
				$error++;
1127
			}
1128
		}
1129
1130
		if (! $error)
1131
		{
1132
			// Delete linked object
1133
			$res = $this->deleteObjectLinked();
1134
			if ($res < 0) $error++;
1135
		}
1136
1137
		if (! $error)
1138
		{
1139
			// Delete contratdet_log
1140
			/*
1141
			$sql = "DELETE cdl";
1142
			$sql.= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1143
			$sql.= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".$this->id;
1144
			*/
1145
			$sql = "SELECT cdl.rowid as cdlrowid ";
1146
			$sql.= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1147
			$sql.= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".$this->id;
1148
1149
			dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1150
			$resql=$this->db->query($sql);
1151
			if (! $resql)
1152
			{
1153
				$this->error=$this->db->error();
1154
				$error++;
1155
			}
1156
			$numressql=$this->db->num_rows($resql);
1157
			if (! $error && $numressql )
1158
			{
1159
				$tab_resql=array();
1160
				for($i=0;$i<$numressql;$i++)
1161
				{
1162
					$objresql=$this->db->fetch_object($resql);
1163
					$tab_resql[]= $objresql->cdlrowid;
1164
				}
1165
				$this->db->free($resql);
1166
1167
				$sql= "DELETE FROM ".MAIN_DB_PREFIX."contratdet_log ";
1168
				$sql.= " WHERE ".MAIN_DB_PREFIX."contratdet_log.rowid IN (".implode(",", $tab_resql).")";
1169
1170
				dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1171
				$resql=$this->db->query($sql);
1172
				if (! $resql)
1173
				{
1174
					$this->error=$this->db->error();
1175
					$error++;
1176
				}
1177
			}
1178
		}
1179
1180
		if (! $error)
1181
		{
1182
			// Delete contratdet
1183
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet";
1184
			$sql.= " WHERE fk_contrat=".$this->id;
1185
1186
			dol_syslog(get_class($this)."::delete contratdet", LOG_DEBUG);
1187
			$resql=$this->db->query($sql);
1188
			if (! $resql)
1189
			{
1190
				$this->error=$this->db->error();
1191
				$error++;
1192
			}
1193
		}
1194
1195
		if (! $error)
1196
		{
1197
			// Delete contrat
1198
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."contrat";
1199
			$sql.= " WHERE rowid=".$this->id;
1200
1201
			dol_syslog(get_class($this)."::delete contrat", LOG_DEBUG);
1202
			$resql=$this->db->query($sql);
1203
			if (! $resql)
1204
			{
1205
				$this->error=$this->db->error();
1206
				$error++;
1207
			}
1208
		}
1209
1210
		// Removed extrafields
1211
		if (! $error) {
1212
			$result=$this->deleteExtraFields();
1213
			if ($result < 0)
1214
			{
1215
				$error++;
1216
				dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1217
			}
1218
		}
1219
1220
		if (! $error)
1221
		{
1222
			// We remove directory
1223
			$ref = dol_sanitizeFileName($this->ref);
1224
			if ($conf->contrat->dir_output)
1225
			{
1226
				$dir = $conf->contrat->dir_output . "/" . $ref;
1227
				if (file_exists($dir))
1228
				{
1229
					$res=@dol_delete_dir_recursive($dir);
1230
					if (! $res)
1231
					{
1232
						$this->error='ErrorFailToDeleteDir';
1233
						$error++;
1234
					}
1235
				}
1236
			}
1237
		}
1238
1239
		if (! $error)
1240
		{
1241
			$this->db->commit();
1242
			return 1;
1243
		}
1244
		else
1245
		{
1246
			$this->error=$this->db->lasterror();
1247
			$this->db->rollback();
1248
			return -1;
1249
		}
1250
	}
1251
1252
	/**
1253
	 *  Update object into database
1254
	 *
1255
	 *  @param	User	$user        User that modifies
1256
	 *  @param  int		$notrigger	 0=launch triggers after, 1=disable triggers
1257
	 *  @return int     		   	 <0 if KO, >0 if OK
1258
	 */
1259
	public function update($user, $notrigger = 0)
1260
	{
1261
		global $conf, $langs;
1262
		$error=0;
1263
1264
		// Clean parameters
1265
		if (empty($this->fk_commercial_signature) && $this->commercial_signature_id > 0) $this->fk_commercial_signature = $this->commercial_signature_id;
1266
		if (empty($this->fk_commercial_suivi) && $this->commercial_suivi_id > 0) $this->fk_commercial_suivi = $this->commercial_suivi_id;
1267
		if (empty($this->fk_soc) && $this->socid > 0) $this->fk_soc = $this->socid;
1268
		if (empty($this->fk_project) && $this->projet > 0) $this->fk_project = $this->projet;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$projet has been deprecated. ( Ignorable by Annotation )

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

1268
		if (empty($this->fk_project) && /** @scrutinizer ignore-deprecated */ $this->projet > 0) $this->fk_project = $this->projet;
Loading history...
1269
1270
		if (isset($this->ref)) $this->ref=trim($this->ref);
1271
		if (isset($this->ref_customer)) $this->ref_customer=trim($this->ref_customer);
1272
		if (isset($this->ref_supplier)) $this->ref_supplier=trim($this->ref_supplier);
1273
		if (isset($this->ref_ext)) $this->ref_ext=trim($this->ref_ext);
1274
		if (isset($this->entity)) $this->entity=trim($this->entity);
1275
		if (isset($this->statut)) $this->statut=(int) $this->statut;
1276
		if (isset($this->fk_soc)) $this->fk_soc=trim($this->fk_soc);
1277
		if (isset($this->fk_commercial_signature)) $this->fk_commercial_signature=trim($this->fk_commercial_signature);
1278
		if (isset($this->fk_commercial_suivi)) $this->fk_commercial_suivi=trim($this->fk_commercial_suivi);
1279
		if (isset($this->fk_user_mise_en_service)) $this->fk_user_mise_en_service=trim($this->fk_user_mise_en_service);
1280
		if (isset($this->fk_user_cloture)) $this->fk_user_cloture=trim($this->fk_user_cloture);
1281
		if (isset($this->note_private)) $this->note_private=trim($this->note_private);
1282
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1283
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1284
		//if (isset($this->extraparams)) $this->extraparams=trim($this->extraparams);
1285
1286
		// Check parameters
1287
		// Put here code to add a control on parameters values
1288
1289
		// Update request
1290
    	$sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET";
1291
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1292
		$sql.= " ref_customer=".(isset($this->ref_customer)?"'".$this->db->escape($this->ref_customer)."'":"null").",";
1293
		$sql.= " ref_supplier=".(isset($this->ref_supplier)?"'".$this->db->escape($this->ref_supplier)."'":"null").",";
1294
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
1295
		$sql.= " entity=".$conf->entity.",";
1296
		$sql.= " date_contrat=".(dol_strlen($this->date_contrat)!=0 ? "'".$this->db->idate($this->date_contrat)."'" : 'null').",";
1297
		$sql.= " statut=".(isset($this->statut)?$this->statut:"null").",";
1298
		$sql.= " mise_en_service=".(dol_strlen($this->mise_en_service)!=0 ? "'".$this->db->idate($this->mise_en_service)."'" : 'null').",";
1299
		$sql.= " fin_validite=".(dol_strlen($this->fin_validite)!=0 ? "'".$this->db->idate($this->fin_validite)."'" : 'null').",";
1300
		$sql.= " date_cloture=".(dol_strlen($this->date_cloture)!=0 ? "'".$this->db->idate($this->date_cloture)."'" : 'null').",";
1301
		$sql.= " fk_soc=".($this->fk_soc > 0 ? $this->fk_soc:"null").",";
1302
		$sql.= " fk_projet=".($this->fk_project > 0 ? $this->fk_project:"null").",";
1303
		$sql.= " fk_commercial_signature=".(isset($this->fk_commercial_signature)?$this->fk_commercial_signature:"null").",";
1304
		$sql.= " fk_commercial_suivi=".(isset($this->fk_commercial_suivi)?$this->fk_commercial_suivi:"null").",";
1305
		$sql.= " fk_user_mise_en_service=".(isset($this->fk_user_mise_en_service)?$this->fk_user_mise_en_service:"null").",";
1306
		$sql.= " fk_user_cloture=".(isset($this->fk_user_cloture)?$this->fk_user_cloture:"null").",";
1307
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1308
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1309
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
1310
		//$sql.= " extraparams=".(isset($this->extraparams)?"'".$this->db->escape($this->extraparams)."'":"null")."";
1311
		$sql.= " WHERE rowid=".$this->id;
1312
1313
		$this->db->begin();
1314
1315
		$resql = $this->db->query($sql);
1316
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
1317
1318
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
1319
		{
1320
			$result=$this->insertExtraFields();
1321
			if ($result < 0)
1322
			{
1323
				$error++;
1324
			}
1325
		}
1326
1327
		if (! $error && ! $notrigger)
1328
		{
1329
			// Call triggers
1330
			$result=$this->call_trigger('CONTRACT_MODIFY', $user);
1331
			if ($result < 0) { $error++; }
1332
			// End call triggers
1333
		}
1334
1335
		// Commit or rollback
1336
		if ($error)
1337
		{
1338
			foreach($this->errors as $errmsg)
1339
			{
1340
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1341
				$this->error.=($this->error?', '.$errmsg:$errmsg);
1342
			}
1343
			$this->db->rollback();
1344
			return -1*$error;
1345
		}
1346
		else
1347
		{
1348
			$this->db->commit();
1349
			return 1;
1350
		}
1351
	}
1352
1353
1354
	/**
1355
	 *  Ajoute une ligne de contrat en base
1356
	 *
1357
	 *  @param	string		$desc            	Description de la ligne
1358
	 *  @param  float		$pu_ht              Prix unitaire HT
1359
	 *  @param  int			$qty             	Quantite
1360
	 *  @param  float		$txtva           	Taux tva
1361
	 *  @param  float		$txlocaltax1        Local tax 1 rate
1362
	 *  @param  float		$txlocaltax2        Local tax 2 rate
1363
	 *  @param  int			$fk_product      	Id produit
1364
	 *  @param  float		$remise_percent  	Percentage discount of the line
1365
	 *  @param  int			$date_start      	Date de debut prevue
1366
	 *  @param  int			$date_end        	Date de fin prevue
1367
	 *	@param	string		$price_base_type	HT or TTC
1368
	 * 	@param  float		$pu_ttc             Prix unitaire TTC
1369
	 * 	@param  int			$info_bits			Bits of type of lines
1370
	 * 	@param  int			$fk_fournprice		Fourn price id
1371
	 *  @param  int			$pa_ht				Buying price HT
1372
	 *  @param	array		$array_options		extrafields array
1373
	 * 	@param 	string		$fk_unit 			Code of the unit to use. Null to use the default one
1374
	 * 	@param 	string		$rang 				Position
1375
	 *  @return int             				<0 if KO, >0 if OK
1376
	 */
1377
	public 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)
1378
	{
1379
		global $user, $langs, $conf, $mysoc;
1380
		$error=0;
1381
1382
		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");
1383
1384
		// Check parameters
1385
		if ($fk_product <= 0 && empty($desc))
1386
		{
1387
			$this->error="ErrorDescRequiredForFreeProductLines";
1388
			return -1;
1389
		}
1390
1391
		if ($this->statut >= 0)
1392
		{
1393
1394
			// Clean parameters
1395
			$pu_ht=price2num($pu_ht);
1396
			$pu_ttc=price2num($pu_ttc);
1397
			$pa_ht=price2num($pa_ht);
1398
			if (!preg_match('/\((.*)\)/', $txtva)) {
1399
				$txtva = price2num($txtva);               // $txtva can have format '5.0(XXX)' or '5'
1400
			}
1401
			$txlocaltax1=price2num($txlocaltax1);
1402
			$txlocaltax2=price2num($txlocaltax2);
1403
			$remise_percent=price2num($remise_percent);
1404
			$qty=price2num($qty);
1405
			if (empty($qty)) $qty=1;
1406
			if (empty($info_bits)) $info_bits=0;
1407
			if (empty($pu_ht) || ! is_numeric($pu_ht))  $pu_ht=0;
1408
			if (empty($pu_ttc)) $pu_ttc=0;
1409
			if (empty($txtva) || ! is_numeric($txtva)) $txtva=0;
1410
			if (empty($txlocaltax1) || ! is_numeric($txlocaltax1)) $txlocaltax1=0;
1411
            if (empty($txlocaltax2) || ! is_numeric($txlocaltax2)) $txlocaltax2=0;
1412
1413
			if ($price_base_type=='HT')
1414
			{
1415
				$pu=$pu_ht;
1416
			}
1417
			else
1418
			{
1419
				$pu=$pu_ttc;
1420
			}
1421
1422
			// Check parameters
1423
			if (empty($remise_percent)) $remise_percent=0;
1424
1425
			if ($date_start && $date_end && $date_start > $date_end) {
1426
				$langs->load("errors");
1427
				$this->error=$langs->trans('ErrorStartDateGreaterEnd');
1428
				return -1;
1429
			}
1430
1431
			$this->db->begin();
1432
1433
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->societe, $mysoc);
1434
1435
			// Clean vat code
1436
			$vat_src_code='';
1437
			if (preg_match('/\((.*)\)/', $txtva, $reg))
1438
			{
1439
				$vat_src_code = $reg[1];
1440
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
1441
			}
1442
1443
			// Calcul du total TTC et de la TVA pour la ligne a partir de
1444
			// qty, pu, remise_percent et txtva
1445
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1446
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1447
1448
			$tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1449
			$total_ht  = $tabprice[0];
1450
			$total_tva = $tabprice[1];
1451
			$total_ttc = $tabprice[2];
1452
			$total_localtax1= $tabprice[9];
1453
			$total_localtax2= $tabprice[10];
1454
1455
			$localtax1_type=$localtaxes_type[0];
1456
			$localtax2_type=$localtaxes_type[2];
1457
1458
			// TODO A virer
1459
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1460
			$remise = 0;
1461
			$price = price2num(round($pu_ht, 2));
1462
			if (dol_strlen($remise_percent) > 0)
1463
			{
1464
				$remise = round(($pu_ht * $remise_percent / 100), 2);
1465
				$price = $pu_ht - $remise;
1466
			}
1467
1468
		    if (empty($pa_ht)) $pa_ht=0;
1469
1470
1471
			// if buy price not defined, define buyprice as configured in margin admin
1472
			if ($this->pa_ht == 0)
1473
			{
1474
				if (($result = $this->defineBuyPrice($pu_ht, $remise_percent, $fk_product)) < 0)
1475
				{
1476
					return $result;
1477
				}
1478
				else
1479
				{
1480
					$pa_ht = $result;
1481
				}
1482
			}
1483
1484
			// Insertion dans la base
1485
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
1486
			$sql.= " (fk_contrat, label, description, fk_product, qty, tva_tx, vat_src_code,";
1487
			$sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
1488
			$sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
1489
			$sql.= " info_bits,";
1490
			$sql.= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
1491
			if ($date_start > 0) { $sql.= ",date_ouverture_prevue"; }
1492
			if ($date_end > 0)   { $sql.= ",date_fin_validite"; }
1493
			$sql.= ", fk_unit";
1494
			$sql.= ") VALUES (";
1495
			$sql.= $this->id.", '', '" . $this->db->escape($desc) . "',";
1496
			$sql.= ($fk_product>0 ? $fk_product : "null").",";
1497
			$sql.= " ".$qty.",";
1498
			$sql.= " ".$txtva.",";
1499
			$sql.= " ".($vat_src_code?"'".$vat_src_code."'":"null").",";
1500
			$sql.= " ".$txlocaltax1.",";
1501
			$sql.= " ".$txlocaltax2.",";
1502
			$sql.= " '".$localtax1_type."',";
1503
			$sql.= " '".$localtax2_type."',";
1504
			$sql.= " ".price2num($remise_percent).",";
1505
			$sql.= " ".price2num($pu_ht).",";
1506
			$sql.= " ".price2num($total_ht).",".price2num($total_tva).",".price2num($total_localtax1).",".price2num($total_localtax2).",".price2num($total_ttc).",";
1507
			$sql.= " '".$info_bits."',";
1508
			$sql.= " ".price2num($price).",".price2num($remise).",";
1509
			if (isset($fk_fournprice)) $sql.= ' '.$fk_fournprice.',';
1510
			else $sql.= ' null,';
1511
			if (isset($pa_ht)) $sql.= ' '.price2num($pa_ht);
1512
			else $sql.= ' null';
1513
			if ($date_start > 0) { $sql.= ",'".$this->db->idate($date_start)."'"; }
1514
			if ($date_end > 0) { $sql.= ",'".$this->db->idate($date_end)."'"; }
1515
			$sql.= ", ".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
1516
			$sql.= ")";
1517
1518
			$resql=$this->db->query($sql);
1519
			if ($resql)
1520
			{
1521
				$contractlineid = $this->db->last_insert_id(MAIN_DB_PREFIX."contratdet");
1522
1523
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1524
				{
1525
					$contractline = new ContratLigne($this->db);
1526
					$contractline->array_options=$array_options;
1527
					$contractline->id=$contractlineid;
1528
					$result=$contractline->insertExtraFields();
1529
					if ($result < 0)
1530
					{
1531
						$this->error[]=$contractline->error;
1532
						$error++;
1533
					}
1534
				}
1535
1536
				if (empty($error)) {
1537
				    // Call trigger
1538
				    $result=$this->call_trigger('LINECONTRACT_INSERT', $user);
1539
				    if ($result < 0)
1540
				    {
1541
				    	$error++;
1542
				    }
1543
				    // End call triggers
1544
				}
1545
1546
				if ($error)
1547
				{
1548
					$this->db->rollback();
1549
					return -1;
1550
				}
1551
				else
1552
				{
1553
					$this->db->commit();
1554
					return $contractlineid;
1555
				}
1556
			}
1557
			else
1558
			{
1559
				$this->db->rollback();
1560
				$this->error=$this->db->error()." sql=".$sql;
1561
				return -1;
1562
			}
1563
		}
1564
		else
1565
		{
1566
			dol_syslog(get_class($this)."::addline ErrorTryToAddLineOnValidatedContract", LOG_ERR);
1567
			return -2;
1568
		}
1569
	}
1570
1571
	/**
1572
	 *  Mets a jour une ligne de contrat
1573
	 *
1574
	 *  @param	int			$rowid            	Id de la ligne de facture
1575
	 *  @param  string		$desc             	Description de la ligne
1576
	 *  @param  float		$pu               	Prix unitaire
1577
	 *  @param  int			$qty              	Quantite
1578
	 *  @param  float		$remise_percent   	Percentage discount of the line
1579
	 *  @param  int			$date_start       	Date de debut prevue
1580
	 *  @param  int			$date_end         	Date de fin prevue
1581
	 *  @param  float		$tvatx            	Taux TVA
1582
	 *  @param  float		$localtax1tx      	Local tax 1 rate
1583
	 *  @param  float		$localtax2tx      	Local tax 2 rate
1584
	 *  @param  int|string	$date_debut_reel  	Date de debut reelle
1585
	 *  @param  int|string	$date_fin_reel    	Date de fin reelle
1586
	 *	@param	string		$price_base_type	HT or TTC
1587
	 * 	@param  int			$info_bits			Bits of type of lines
1588
	 * 	@param  int			$fk_fournprice		Fourn price id
1589
	 *  @param  int			$pa_ht				Buying price HT
1590
	 *  @param	array		$array_options		extrafields array
1591
	 * 	@param 	string		$fk_unit 			Code of the unit to use. Null to use the default one
1592
	 *  @return int              				< 0 si erreur, > 0 si ok
1593
	 */
1594
	public 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)
1595
	{
1596
		global $user, $conf, $langs, $mysoc;
1597
1598
		$error=0;
1599
1600
		// Clean parameters
1601
		$qty=trim($qty);
1602
		$desc=trim($desc);
1603
		$desc=trim($desc);
1604
		$price = price2num($pu);
1605
		$tvatx = price2num($tvatx);
1606
		$localtax1tx = price2num($localtax1tx);
1607
		$localtax2tx = price2num($localtax2tx);
1608
		$pa_ht=price2num($pa_ht);
1609
		if (empty($fk_fournprice)) $fk_fournprice=0;
1610
1611
		$subprice = $price;
1612
		$remise = 0;
1613
		if (dol_strlen($remise_percent) > 0)
1614
		{
1615
			$remise = round(($pu * $remise_percent / 100), 2);
1616
			$price = $pu - $remise;
1617
		}
1618
		else
1619
		{
1620
			$remise_percent=0;
1621
		}
1622
1623
		if ($date_start && $date_end && $date_start > $date_end) {
1624
			$langs->load("errors");
1625
			$this->error=$langs->trans('ErrorStartDateGreaterEnd');
1626
			return -1;
1627
		}
1628
1629
		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");
1630
1631
		$this->db->begin();
1632
1633
		// Calcul du total TTC et de la TVA pour la ligne a partir de
1634
		// qty, pu, remise_percent et tvatx
1635
		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1636
		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1637
1638
		$localtaxes_type=getLocalTaxesFromRate($tvatx, 0, $this->societe, $mysoc);
1639
		$tvatx = preg_replace('/\s*\(.*\)/', '', $tvatx);  // Remove code into vatrate.
1640
1641
		$tabprice=calcul_price_total($qty, $pu, $remise_percent, $tvatx, $localtax1tx, $localtax2tx, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1642
		$total_ht  = $tabprice[0];
1643
		$total_tva = $tabprice[1];
1644
		$total_ttc = $tabprice[2];
1645
		$total_localtax1= $tabprice[9];
1646
		$total_localtax2= $tabprice[10];
1647
1648
		$localtax1_type=$localtaxes_type[0];
1649
		$localtax2_type=$localtaxes_type[2];
1650
1651
		// TODO A virer
1652
		// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1653
		$remise = 0;
1654
		$price = price2num(round($pu, 2));
1655
		if (dol_strlen($remise_percent) > 0)
1656
		{
1657
		    $remise = round(($pu * $remise_percent / 100), 2);
1658
		    $price = $pu - $remise;
1659
		}
1660
1661
	    if (empty($pa_ht)) $pa_ht=0;
1662
1663
		// if buy price not defined, define buyprice as configured in margin admin
1664
		if ($this->pa_ht == 0)
1665
		{
1666
			if (($result = $this->defineBuyPrice($pu, $remise_percent)) < 0)
1667
			{
1668
				return $result;
1669
			}
1670
			else
1671
			{
1672
				$pa_ht = $result;
1673
			}
1674
		}
1675
1676
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet set description='".$this->db->escape($desc)."'";
1677
		$sql.= ",price_ht='".price2num($price)."'";
1678
		$sql.= ",subprice='".price2num($subprice)."'";
1679
		$sql.= ",remise='".price2num($remise)."'";
1680
		$sql.= ",remise_percent='".price2num($remise_percent)."'";
1681
		$sql.= ",qty='".$qty."'";
1682
		$sql.= ",tva_tx='".price2num($tvatx)."'";
1683
		$sql.= ",localtax1_tx='".price2num($localtax1tx)."'";
1684
		$sql.= ",localtax2_tx='".price2num($localtax2tx)."'";
1685
		$sql.= ",localtax1_type='".$localtax1_type."'";
1686
		$sql.= ",localtax2_type='".$localtax2_type."'";
1687
		$sql.= ", total_ht='".price2num($total_ht)."'";
1688
		$sql.= ", total_tva='".price2num($total_tva)."'";
1689
		$sql.= ", total_localtax1='".price2num($total_localtax1)."'";
1690
		$sql.= ", total_localtax2='".price2num($total_localtax2)."'";
1691
		$sql.= ", total_ttc='".price2num($total_ttc)."'";
1692
		$sql.= ", fk_product_fournisseur_price=".($fk_fournprice > 0 ? $fk_fournprice : "null");
1693
		$sql.= ", buy_price_ht='".price2num($pa_ht)."'";
1694
		if ($date_start > 0) { $sql.= ",date_ouverture_prevue='".$this->db->idate($date_start)."'"; }
1695
		else { $sql.=",date_ouverture_prevue=null"; }
1696
		if ($date_end > 0) { $sql.= ",date_fin_validite='".$this->db->idate($date_end)."'"; }
1697
		else { $sql.=",date_fin_validite=null"; }
1698
		if ($date_debut_reel > 0) { $sql.= ",date_ouverture='".$this->db->idate($date_debut_reel)."'"; }
1699
		else { $sql.=",date_ouverture=null"; }
1700
		if ($date_fin_reel > 0) { $sql.= ",date_cloture='".$this->db->idate($date_fin_reel)."'"; }
1701
		else { $sql.=",date_cloture=null"; }
1702
		$sql .= ", fk_unit=".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
1703
		$sql .= " WHERE rowid = ".$rowid;
1704
1705
		dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
1706
		$result = $this->db->query($sql);
1707
		if ($result)
1708
		{
1709
			$result=$this->update_statut($user);
1710
			if ($result >= 0)
1711
			{
1712
1713
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used
1714
				{
1715
					$contractline = new ContratLigne($this->db);
1716
					$contractline->array_options=$array_options;
1717
					$contractline->id= $rowid;
1718
					$result=$contractline->insertExtraFields();
1719
					if ($result < 0)
1720
					{
1721
						$this->error[]=$contractline->error;
1722
						$error++;
1723
					}
1724
				}
1725
1726
				if (empty($error)) {
1727
			        // Call trigger
1728
			        $result=$this->call_trigger('LINECONTRACT_UPDATE', $user);
1729
			        if ($result < 0)
1730
			        {
1731
			            $this->db->rollback();
1732
			            return -3;
1733
			        }
1734
			        // End call triggers
1735
1736
					$this->db->commit();
1737
					return 1;
1738
				}
1739
			}
1740
			else
1741
			{
1742
				$this->db->rollback();
1743
				dol_syslog(get_class($this)."::updateline Erreur -2");
1744
				return -2;
1745
			}
1746
		}
1747
		else
1748
		{
1749
			$this->db->rollback();
1750
			$this->error=$this->db->error();
1751
			dol_syslog(get_class($this)."::updateline Erreur -1");
1752
			return -1;
1753
		}
1754
	}
1755
1756
	/**
1757
	 *  Delete a contract line
1758
	 *
1759
	 *  @param	int		$idline		Id of line to delete
1760
	 *	@param  User	$user       User that delete
1761
	 *  @return int         		>0 if OK, <0 if KO
1762
	 */
1763
	public function deleteline($idline, User $user)
1764
	{
1765
		global $conf, $langs;
1766
1767
		$error=0;
1768
1769
		if ($this->statut >= 0)
1770
		{
1771
		    // Call trigger
1772
		    $result=$this->call_trigger('LINECONTRACT_DELETE', $user);
1773
		    if ($result < 0) return -1;
1774
		    // End call triggers
1775
1776
		    $this->db->begin();
1777
1778
		    $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
1779
			$sql.= " WHERE rowid=".$idline;
1780
1781
			dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
1782
			$resql = $this->db->query($sql);
1783
			if (! $resql)
1784
			{
1785
				$this->error="Error ".$this->db->lasterror();
1786
				$error++;
1787
			}
1788
1789
			if (empty($error)) {
1790
				// Remove extrafields
1791
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1792
				{
1793
					$contractline = new ContratLigne($this->db);
1794
					$contractline->id= $idline;
1795
					$result=$contractline->deleteExtraFields();
1796
					if ($result < 0)
1797
					{
1798
						$error++;
1799
						$this->error="Error ".get_class($this)."::deleteline deleteExtraFields error -4 ".$contractline->error;
1800
					}
1801
				}
1802
			}
1803
1804
			if (empty($error)) {
1805
				$this->db->commit();
1806
				return 1;
1807
			} else {
1808
				dol_syslog(get_class($this)."::deleteline ERROR:".$this->error, LOG_ERR);
1809
				$this->db->rollback();
1810
				return -1;
1811
			}
1812
		}
1813
		else
1814
		{
1815
			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1816
			return -2;
1817
		}
1818
	}
1819
1820
1821
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1822
	/**
1823
	 *  Update statut of contract according to services
1824
	 *
1825
	 *	@param	User	$user		Object user
1826
	 *	@return int     			<0 if KO, >0 if OK
1827
	 *  @deprecated					This function will never be used. Status of a contract is status of its lines.
1828
	 */
1829
	public function update_statut($user)
1830
	{
1831
        // phpcs:enable
1832
		dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING);
1833
1834
		// If draft, we keep it (should not happen)
1835
		if ($this->statut == 0) return 1;
1836
1837
		// Load $this->lines array
1838
		//		$this->fetch_lines();
1839
1840
        //		$newstatut=1;
1841
        //		foreach($this->lines as $key => $contractline)
1842
        //		{
1843
        //			//			if ($contractline)         // Loop on each service
1844
        //		}
1845
1846
		return 1;
1847
	}
1848
1849
1850
	/**
1851
	 *  Return label of a contract status
1852
	 *
1853
	 *  @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
1854
	 *  @return string      		Label
1855
	 */
1856
	public function getLibStatut($mode)
1857
	{
1858
		return $this->LibStatut($this->statut, $mode);
1859
	}
1860
1861
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1862
	/**
1863
	 *  Renvoi label of a given contrat status
1864
	 *
1865
	 *  @param	int		$statut      	Status id
1866
	 *  @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
1867
	 *	@return string      			Label
1868
	 */
1869
	public function LibStatut($statut, $mode)
1870
	{
1871
        // phpcs:enable
1872
		global $langs;
1873
		$langs->load("contracts");
1874
		if ($mode == 0)
1875
		{
1876
			if ($statut == 0) { return $langs->trans("ContractStatusDraft"); }
1877
			elseif ($statut == 1) { return $langs->trans("ContractStatusValidated"); }
1878
			elseif ($statut == 2) { return $langs->trans("ContractStatusClosed"); }
1879
		}
1880
		elseif ($mode == 1)
1881
		{
1882
			if ($statut == 0) { return $langs->trans("ContractStatusDraft"); }
1883
			elseif ($statut == 1) { return $langs->trans("ContractStatusValidated"); }
1884
			elseif ($statut == 2) { return $langs->trans("ContractStatusClosed"); }
1885
		}
1886
		elseif ($mode == 2)
1887
		{
1888
			if ($statut == 0) { return img_picto($langs->trans('ContractStatusDraft'), 'statut0').' '.$langs->trans("ContractStatusDraft"); }
1889
			elseif ($statut == 1) { return img_picto($langs->trans('ContractStatusValidated'), 'statut4').' '.$langs->trans("ContractStatusValidated"); }
1890
			elseif ($statut == 2) { return img_picto($langs->trans('ContractStatusClosed'), 'statut6').' '.$langs->trans("ContractStatusClosed"); }
1891
		}
1892
		elseif ($mode == 3)
1893
		{
1894
			if ($statut == 0) { return img_picto($langs->trans('ContractStatusDraft'), 'statut0'); }
1895
			elseif ($statut == 1) { return img_picto($langs->trans('ContractStatusValidated'), 'statut4'); }
1896
			elseif ($statut == 2) { return img_picto($langs->trans('ContractStatusClosed'), 'statut6'); }
1897
		}
1898
		elseif ($mode == 4 || $mode == 6 || $mode == 7)
1899
		{
1900
			$text='';
1901
			if ($mode == 4) {
1902
				$text ='<span class="hideonsmartphone">';
1903
				$text.=($this->nbofserviceswait+$this->nbofservicesopened+$this->nbofservicesexpired+$this->nbofservicesclosed);
1904
				$text.=' '.$langs->trans("Services");
1905
				$text.=': &nbsp; &nbsp; ';
1906
				$text.='</span>';
1907
			}
1908
			$text.=($mode == 7?'<div class="inline-block">':'');
1909
			$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; ':'') : '';
1910
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1911
			$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; ':'') : '';
1912
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1913
			$text.=($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.ContratLigne::LibStatut(4, 3, 1, 'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesclosed)?' &nbsp; ':'') : '';
1914
			$text.=($mode == 7?'</div><div class="inline-block">':'');
1915
			$text.=($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.ContratLigne::LibStatut(5, 3, -1, 'class="paddingleft2 inline-block valigntextbottom"')) : '';
1916
			$text.=($mode == 7?'</div>':'');
1917
			return $text;
1918
		}
1919
		elseif ($mode == 5)
1920
		{
1921
			if ($statut == 0) { return $langs->trans("ContractStatusDraft").' '.img_picto($langs->trans('ContractStatusDraft'), 'statut0'); }
1922
			elseif ($statut == 1) { return $langs->trans("ContractStatusValidated").' '.img_picto($langs->trans('ContractStatusValidated'), 'statut4'); }
1923
			elseif ($statut == 2) { return $langs->trans("ContractStatusClosed").' '.img_picto($langs->trans('ContractStatusClosed'), 'statut6'); }
1924
		}
1925
	}
1926
1927
1928
	/**
1929
	 *	Return clicable name (with picto eventually)
1930
	 *
1931
	 *	@param	int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
1932
	 *	@param	int		$maxlength					Max length of ref
1933
     *  @param	int     $notooltip					1=Disable tooltip
1934
     *  @param  int     $save_lastsearch_value		-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1935
	 *	@return	string								Chaine avec URL
1936
	 */
1937
    public function getNomUrl($withpicto = 0, $maxlength = 0, $notooltip = 0, $save_lastsearch_value = -1)
1938
	{
1939
		global $conf, $langs, $user, $hookmanager;
1940
1941
		$result='';
1942
1943
		$url = DOL_URL_ROOT.'/contrat/card.php?id='.$this->id;
1944
1945
		//if ($option !== 'nolink')
1946
		//{
1947
			// Add param to save lastsearch_values or not
1948
			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1949
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1950
			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1951
		//}
1952
1953
        $label = '';
1954
1955
        if ($user->rights->contrat->lire) {
1956
            $label = '<u>'.$langs->trans("ShowContract").'</u>';
1957
            $label .= '<br><b>'.$langs->trans('Ref').':</b> '.($this->ref?$this->ref:$this->id);
1958
            $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
0 ignored issues
show
Bug Best Practice introduced by
The property ref_client does not exist on Contrat. Did you maybe forget to declare it?
Loading history...
1959
            $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
1960
            if (!empty($this->total_ht)) {
1961
                $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1962
            }
1963
            if (!empty($this->total_tva)) {
1964
                $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1,	$conf->currency);
1965
            }
1966
            if (!empty($this->total_ttc)) {
1967
                $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1968
            }
1969
        }
1970
1971
        $linkclose='';
1972
        if (empty($notooltip) && $user->rights->contrat->lire)
1973
        {
1974
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1975
            {
1976
                $label=$langs->trans("ShowOrder");
1977
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1978
            }
1979
            $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1980
            $linkclose.=' class="classfortooltip"';
1981
        }
1982
1983
		$linkstart = '<a href="'.$url.'"';
1984
		$linkstart.=$linkclose.'>';
1985
		$linkend='</a>';
1986
1987
		$result .= $linkstart;
1988
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1989
		if ($withpicto != 2) $result.= ($this->ref?$this->ref:$this->id);
1990
		$result .= $linkend;
1991
1992
		global $action;
1993
		$hookmanager->initHooks(array('contractdao'));
1994
		$parameters=array('id'=>$this->id, 'getnomurl'=>$result);
1995
		$reshook=$hookmanager->executeHooks('getNomUrl', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
1996
		if ($reshook > 0) {
1997
			$result = $hookmanager->resPrint;
1998
		} else {
1999
			$result .= $hookmanager->resPrint;
2000
		}
2001
2002
		return $result;
2003
	}
2004
2005
	/**
2006
	 *  Charge les informations d'ordre info dans l'objet contrat
2007
	 *
2008
	 *  @param  int		$id     id du contrat a charger
2009
	 *  @return	void
2010
	 */
2011
	public function info($id)
2012
	{
2013
		$sql = "SELECT c.rowid, c.ref, c.datec, c.date_cloture,";
2014
		$sql.= " c.tms as date_modification,";
2015
		$sql.= " fk_user_author, fk_user_cloture";
2016
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2017
		$sql.= " WHERE c.rowid = ".$id;
2018
2019
		$result=$this->db->query($sql);
2020
		if ($result)
2021
		{
2022
			if ($this->db->num_rows($result))
2023
			{
2024
				$obj = $this->db->fetch_object($result);
2025
2026
				$this->id = $obj->rowid;
2027
2028
				if ($obj->fk_user_author) {
2029
					$cuser = new User($this->db);
2030
					$cuser->fetch($obj->fk_user_author);
2031
					$this->user_creation     = $cuser;
2032
				}
2033
2034
				if ($obj->fk_user_cloture) {
2035
					$cuser = new User($this->db);
2036
					$cuser->fetch($obj->fk_user_cloture);
2037
					$this->user_cloture = $cuser;
2038
				}
2039
				$this->ref			     = (! $obj->ref) ? $obj->rowid : $obj->ref;
2040
				$this->date_creation     = $this->db->jdate($obj->datec);
2041
				$this->date_modification = $this->db->jdate($obj->date_modification);
2042
				$this->date_cloture      = $this->db->jdate($obj->date_cloture);
2043
			}
2044
2045
			$this->db->free($result);
2046
		}
2047
		else
2048
		{
2049
			dol_print_error($this->db);
2050
		}
2051
	}
2052
2053
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2054
	/**
2055
	 *  Return list of line rowid
2056
	 *
2057
	 *  @param	int		$statut     Status of lines to get
2058
	 *  @return array       		Array of line's rowid
2059
	 */
2060
	public function array_detail($statut = -1)
2061
	{
2062
        // phpcs:enable
2063
		$tab=array();
2064
2065
		$sql = "SELECT cd.rowid";
2066
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as cd";
2067
		$sql.= " WHERE fk_contrat =".$this->id;
2068
		if ($statut >= 0) $sql.= " AND statut = '$statut'";
2069
2070
		dol_syslog(get_class($this)."::array_detail()", LOG_DEBUG);
2071
		$resql=$this->db->query($sql);
2072
		if ($resql)
2073
		{
2074
			$num=$this->db->num_rows($resql);
2075
			$i=0;
2076
			while ($i < $num)
2077
			{
2078
				$obj = $this->db->fetch_object($resql);
2079
				$tab[$i]=$obj->rowid;
2080
				$i++;
2081
			}
2082
			return $tab;
2083
		}
2084
		else
2085
		{
2086
			$this->error=$this->db->error();
2087
			return -1;
2088
		}
2089
	}
2090
2091
	/**
2092
	 *  Return list of other contracts for same company than current contract
2093
	 *
2094
	 *	@param	string		$option		'all' or 'others'
2095
	 *  @return array   				Array of contracts id
2096
	 */
2097
	public function getListOfContracts($option = 'all')
2098
	{
2099
		$tab=array();
2100
2101
		$sql = "SELECT c.rowid, c.ref";
2102
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2103
		$sql.= " WHERE fk_soc =".$this->socid;
2104
		if ($option == 'others') $sql.= " AND c.rowid != ".$this->id;
2105
2106
		dol_syslog(get_class($this)."::getOtherContracts()", LOG_DEBUG);
2107
		$resql=$this->db->query($sql);
2108
		if ($resql)
2109
		{
2110
			$num=$this->db->num_rows($resql);
2111
			$i=0;
2112
			while ($i < $num)
2113
			{
2114
				$obj = $this->db->fetch_object($resql);
2115
				$contrat=new Contrat($this->db);
2116
				$contrat->fetch($obj->rowid);
2117
				$tab[]=$contrat;
2118
				$i++;
2119
			}
2120
			return $tab;
2121
		}
2122
		else
2123
		{
2124
			$this->error=$this->db->error();
2125
			return -1;
2126
		}
2127
	}
2128
2129
2130
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2131
	/**
2132
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2133
     *
2134
     *      @param	User	$user           Objet user
2135
     *      @param  string	$mode           "inactive" pour services a activer, "expired" pour services expires
2136
     *      @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
2137
	 */
2138
    public function load_board($user, $mode)
2139
	{
2140
        // phpcs:enable
2141
		global $conf, $langs;
2142
2143
		$this->from = " FROM ".MAIN_DB_PREFIX."contrat as c";
2144
		$this->from.= ", ".MAIN_DB_PREFIX."contratdet as cd";
2145
		$this->from.= ", ".MAIN_DB_PREFIX."societe as s";
2146
		if (!$user->rights->societe->client->voir && !$user->societe_id) $this->from.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

2146
		if (!$user->rights->societe->client->voir && !/** @scrutinizer ignore-deprecated */ $user->societe_id) $this->from.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
Loading history...
2147
2148
		if ($mode == 'inactive')
2149
		{
2150
			$sql = "SELECT cd.rowid, cd.date_ouverture_prevue as datefin";
2151
			$sql.= $this->from;
2152
			$sql.= " WHERE c.statut = 1";
2153
			$sql.= " AND c.rowid = cd.fk_contrat";
2154
			$sql.= " AND cd.statut = 0";
2155
		}
2156
		elseif ($mode == 'expired')
2157
		{
2158
			$sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2159
			$sql.= $this->from;
2160
			$sql.= " WHERE c.statut = 1";
2161
			$sql.= " AND c.rowid = cd.fk_contrat";
2162
			$sql.= " AND cd.statut = 4";
2163
			$sql.= " AND cd.date_fin_validite < '".$this->db->idate(dol_now())."'";
2164
		}
2165
		elseif ($mode == 'active')
2166
		{
2167
		    $sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2168
		    $sql.= $this->from;
2169
		    $sql.= " WHERE c.statut = 1";
2170
		    $sql.= " AND c.rowid = cd.fk_contrat";
2171
		    $sql.= " AND cd.statut = 4";
2172
		    //$datetouse = dol_now();
2173
		    //$sql.= " AND cd.date_fin_validite < '".$this->db->idate($datetouse)."'";
2174
		}
2175
		$sql.= " AND c.fk_soc = s.rowid";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sql does not seem to be defined for all execution paths leading up to this point.
Loading history...
2176
		$sql.= " AND c.entity = ".$conf->entity;
2177
		if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

2177
		if ($user->societe_id) $sql.=" AND c.fk_soc = "./** @scrutinizer ignore-deprecated */ $user->societe_id;
Loading history...
2178
		if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

2178
		if (!$user->rights->societe->client->voir && !/** @scrutinizer ignore-deprecated */ $user->societe_id) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id;
Loading history...
2179
2180
		$resql=$this->db->query($sql);
2181
		if ($resql)
2182
		{
2183
			$langs->load("contracts");
2184
			$now=dol_now();
2185
2186
			if ($mode == 'inactive') {
2187
				$warning_delay = $conf->contrat->services->inactifs->warning_delay;
2188
				$label = $langs->trans("BoardNotActivatedServices");
2189
				$url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=0&sortfield=cd.date_fin_validite&sortorder=asc';
2190
			}
2191
			elseif ($mode == 'expired') {
2192
			    $warning_delay = $conf->contrat->services->expires->warning_delay;
2193
			    $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=4&filter=expired&sortfield=cd.date_fin_validite&sortorder=asc';
2194
			    $label = $langs->trans("BoardExpiredServices");
2195
			} else {
2196
				$warning_delay = $conf->contrat->services->expires->warning_delay;
2197
				$url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=4&sortfield=cd.date_fin_validite&sortorder=asc';
2198
				//$url.= '&op2day='.$arraydatetouse['mday'].'&op2month='.$arraydatetouse['mon'].'&op2year='.$arraydatetouse['year'];
2199
				//if ($warning_delay >= 0) $url.='&amp;filter=expired';
2200
				$label = $langs->trans("BoardRunningServices");
2201
			}
2202
2203
			$response = new WorkboardResponse();
2204
			$response->warning_delay = $warning_delay/60/60/24;
2205
			$response->label = $label;
2206
			$response->url = $url;
2207
			$response->img = img_object('', "contract");
2208
2209
			while ($obj=$this->db->fetch_object($resql))
2210
			{
2211
				$response->nbtodo++;
2212
2213
				if ($obj->datefin && $this->db->jdate($obj->datefin) < ($now - $warning_delay)) {
2214
					$response->nbtodolate++;
2215
				}
2216
			}
2217
2218
			return $response;
2219
		}
2220
		else
2221
		{
2222
			dol_print_error($this->db);
2223
			$this->error=$this->db->error();
2224
			return -1;
2225
		}
2226
	}
2227
2228
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2229
	/**
2230
	 *   Charge indicateurs this->nb de tableau de bord
2231
	 *
2232
	 *   @return     int         <0 si ko, >0 si ok
2233
	 */
2234
	public function load_state_board()
2235
	{
2236
        // phpcs:enable
2237
		global $conf, $user;
2238
2239
		$this->nb=array();
2240
		$clause = "WHERE";
2241
2242
		$sql = "SELECT count(c.rowid) as nb";
2243
		$sql.= " FROM ".MAIN_DB_PREFIX."contrat as c";
2244
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
2245
		if (!$user->rights->societe->client->voir && !$user->societe_id)
2246
		{
2247
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2248
			$sql.= " WHERE sc.fk_user = " .$user->id;
2249
			$clause = "AND";
2250
		}
2251
		$sql.= " ".$clause." c.entity = ".$conf->entity;
2252
2253
		$resql=$this->db->query($sql);
2254
		if ($resql)
2255
		{
2256
			while ($obj=$this->db->fetch_object($resql))
2257
			{
2258
				$this->nb["Contracts"]=$obj->nb;
2259
			}
2260
            $this->db->free($resql);
2261
			return 1;
2262
		}
2263
		else
2264
		{
2265
			dol_print_error($this->db);
2266
			$this->error=$this->db->error();
2267
			return -1;
2268
		}
2269
	}
2270
2271
2272
	/* gestion des contacts d'un contrat */
2273
2274
	/**
2275
	 *  Return id des contacts clients de facturation
2276
	 *
2277
	 *  @return     array       Liste des id contacts facturation
2278
	 */
2279
	public function getIdBillingContact()
2280
	{
2281
		return $this->getIdContact('external', 'BILLING');
2282
	}
2283
2284
	/**
2285
	 *  Return id des contacts clients de prestation
2286
	 *
2287
	 *  @return     array       Liste des id contacts prestation
2288
	 */
2289
	public function getIdServiceContact()
2290
	{
2291
		return $this->getIdContact('external', 'SERVICE');
2292
	}
2293
2294
2295
	/**
2296
     *  Initialise an instance with random values.
2297
     *  Used to build previews or test instances.
2298
     *	id must be 0 if object instance is a specimen.
2299
     *
2300
     *  @return	void
2301
     */
2302
    public function initAsSpecimen()
2303
    {
2304
		global $user,$langs,$conf;
2305
2306
        // Load array of products prodids
2307
		$num_prods = 0;
2308
		$prodids = array();
2309
		$sql = "SELECT rowid";
2310
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
2311
		$sql.= " WHERE entity IN (".getEntity('product').")";
2312
		$sql.= " AND tosell = 1";
2313
		$resql = $this->db->query($sql);
2314
		if ($resql)
2315
		{
2316
			$num_prods = $this->db->num_rows($resql);
2317
			$i = 0;
2318
			while ($i < $num_prods)
2319
			{
2320
				$i++;
2321
				$row = $this->db->fetch_row($resql);
2322
				$prodids[$i] = $row[0];
2323
			}
2324
		}
2325
2326
		// Initialise parametres
2327
		$this->id=0;
2328
		$this->specimen=1;
2329
2330
		$this->ref = 'SPECIMEN';
2331
		$this->ref_customer = 'SPECIMENCUST';
2332
		$this->ref_supplier = 'SPECIMENSUPP';
2333
		$this->socid = 1;
2334
		$this->statut= 0;
2335
		$this->date_creation = (dol_now() - 3600 * 24 * 7);
2336
		$this->date_contrat = dol_now();
2337
		$this->commercial_signature_id = 1;
2338
		$this->commercial_suivi_id = 1;
2339
		$this->note_private='This is a comment (private)';
2340
		$this->note_public='This is a comment (public)';
2341
		$this->fk_projet = 0;
1 ignored issue
show
Deprecated Code introduced by
The property Contrat::$fk_projet has been deprecated: Use fk_project instead ( Ignorable by Annotation )

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

2341
		/** @scrutinizer ignore-deprecated */ $this->fk_projet = 0;

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

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

Loading history...
2342
		// Lines
2343
		$nbp = 5;
2344
		$xnbp = 0;
2345
		while ($xnbp < $nbp)
2346
		{
2347
			$line=new ContratLigne($this->db);
2348
			$line->qty=1;
2349
			$line->subprice=100;
2350
			$line->price=100;
1 ignored issue
show
Deprecated Code introduced by
The property ContratLigne::$price has been deprecated: Use $price_ht instead ( Ignorable by Annotation )

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

2350
			/** @scrutinizer ignore-deprecated */ $line->price=100;

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

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

Loading history...
2351
			$line->tva_tx=19.6;
2352
			$line->remise_percent=10;
2353
			$line->total_ht=90;
2354
			$line->total_ttc=107.64;	// 90 * 1.196
2355
			$line->total_tva=17.64;
2356
			$line->date_start = dol_now() - 500000;
2357
			$line->date_start_real = dol_now() - 200000;
2358
			$line->date_end = dol_now() + 500000;
2359
			$line->date_end_real = dol_now() - 100000;
2360
			if ($num_prods > 0)
2361
            {
2362
				$prodid = mt_rand(1, $num_prods);
2363
				$line->fk_product=$prodids[$prodid];
2364
            }
2365
			$this->lines[$xnbp]=$line;
2366
			$xnbp++;
2367
		}
2368
	}
2369
2370
	/**
2371
	 * 	Create an array of order lines
2372
	 *
2373
	 * 	@return int		>0 if OK, <0 if KO
2374
	 */
2375
	public function getLinesArray()
2376
	{
2377
		return $this->fetch_lines();
2378
	}
2379
2380
2381
	/**
2382
	 *  Create a document onto disk according to template module.
2383
	 *
2384
	 * 	@param	    string		$modele			Force model to use ('' to not force)
2385
	 * 	@param		Translate	$outputlangs	Object langs to use for output
2386
	 *  @param      int			$hidedetails    Hide details of lines
2387
	 *  @param      int			$hidedesc       Hide description
2388
	 *  @param      int			$hideref        Hide ref
2389
         *  @param   null|array  $moreparams     Array to provide more information
2390
	 * 	@return     int         				0 if KO, 1 if OK
2391
	 */
2392
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2393
	{
2394
		global $conf,$langs;
2395
2396
		$langs->load("contracts");
2397
2398
		if (! dol_strlen($modele)) {
2399
2400
			$modele = 'strato';
2401
2402
			if ($this->modelpdf) {
2403
				$modele = $this->modelpdf;
2404
			} elseif (! empty($conf->global->CONTRACT_ADDON_PDF)) {
2405
				$modele = $conf->global->CONTRACT_ADDON_PDF;
2406
			}
2407
		}
2408
2409
		$modelpath = "core/modules/contract/doc/";
2410
2411
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2412
	}
2413
2414
	/**
2415
	 * Function used to replace a thirdparty id with another one.
2416
	 *
2417
	 * @param DoliDB $db Database handler
2418
	 * @param int $origin_id Old thirdparty id
2419
	 * @param int $dest_id New thirdparty id
2420
	 * @return bool
2421
	 */
2422
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2423
	{
2424
		$tables = array(
2425
			'contrat'
2426
		);
2427
2428
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2429
	}
2430
2431
	/**
2432
	 * Load an object from its id and create a new one in database
2433
	 *
2434
	 * @param	User	$user		  User making the clone
2435
	 * @param   int     $socid        Id of thirdparty
2436
	 * @param   int     $notrigger	  1=Does not execute triggers, 0= execute triggers
2437
	 * @return  int                   New id of clone
2438
	 */
2439
    public function createFromClone(User $user, $socid = 0, $notrigger = 0)
2440
    {
2441
		global $db, $langs, $conf, $hookmanager, $extrafields;
2442
2443
		dol_include_once('/projet/class/project.class.php');
2444
2445
		$error = 0;
2446
2447
		$this->fetch($this->id);
2448
2449
		// Load dest object
2450
		$clonedObj = clone $this;
2451
        $clonedObj->socid = $socid;
2452
2453
		$this->db->begin();
2454
2455
		$objsoc = new Societe($this->db);
2456
2457
		$objsoc->fetch($clonedObj->socid);
2458
2459
		// Clean data
2460
		$clonedObj->statut = 0;
2461
		// Clean extrafields
2462
		if (is_array($clonedObj->array_options) && count($clonedObj->array_options) > 0)
2463
		{
2464
			$extrafields->fetch_name_optionals_label($this->element);
2465
			foreach($clonedObj->array_options as $key => $option)
2466
			{
2467
				$shortkey = preg_replace('/options_/', '', $key);
2468
				//var_dump($shortkey); var_dump($extrafields->attributes[$this->element]['unique'][$shortkey]);
2469
				if (! empty($extrafields->attributes[$this->element]['unique'][$shortkey]))
2470
				{
2471
					//var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
2472
					unset($clonedObj->array_options[$key]);
2473
				}
2474
			}
2475
		}
2476
2477
		if (empty($conf->global->CONTRACT_ADDON) || ! is_readable(DOL_DOCUMENT_ROOT . "/core/modules/contract/" . $conf->global->CONTRACT_ADDON . ".php")) {
2478
			$this->error = 'ErrorSetupNotComplete';
2479
			dol_syslog($this->error);
2480
			return - 1;
2481
		}
2482
2483
		// Set ref
2484
		require_once DOL_DOCUMENT_ROOT . "/core/modules/contract/" . $conf->global->CONTRACT_ADDON . '.php';
2485
		$obj = $conf->global->CONTRACT_ADDON;
2486
		$modContract = new $obj();
2487
		$clonedObj->ref = $modContract->getNextValue($objsoc, $clonedObj);
2488
2489
		// get extrafields so they will be clone
2490
		foreach ($this->lines as $line) {
2491
			$line->fetch_optionals($line->id);
2492
		}
2493
2494
		// Create clone
2495
		$clonedObj->context['createfromclone'] = 'createfromclone';
2496
		$result = $clonedObj->create($user);
2497
		if ($result < 0) {
2498
			$error ++;
2499
			$this->error = $clonedObj->error;
2500
			$this->errors[] = $clonedObj->error;
2501
		} else {
2502
            // copy external contacts if same company
2503
            if ($this->socid == $clonedObj->socid) {
2504
                if ($clonedObj->copy_linked_contact($this, 'external') < 0) {
2505
                    $error++;
2506
                }
2507
            }
2508
        }
2509
2510
		if (! $error) {
2511
			foreach ($this->lines as $line) {
2512
				$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);
0 ignored issues
show
Bug introduced by
The property desc does not seem to exist on ContratLigne.
Loading history...
2513
				if ($result < 0) {
2514
					$error ++;
2515
					$this->error = $clonedObj->error;
2516
					$this->errors[] = $clonedObj->error;
2517
				}
2518
			}
2519
		}
2520
2521
		if (! $error) {
2522
			// Hook of thirdparty module
2523
			if (is_object($hookmanager)) {
2524
				$parameters = array (
2525
						'objFrom' => $this,
2526
						'clonedObj' => $clonedObj
2527
				);
2528
				$action = '';
2529
				$reshook = $hookmanager->executeHooks('createFrom', $parameters, $clonedObj, $action); // Note that $action and $object may have been modified by some hooks
2530
				if ($reshook < 0)
2531
					$error ++;
2532
			}
2533
		}
2534
2535
		unset($clonedObj->context['createfromclone']);
2536
2537
		// End
2538
		if (! $error) {
2539
			$this->db->commit();
2540
			return $clonedObj->id;
2541
		} else {
2542
			$this->db->rollback();
2543
			return - 1;
2544
		}
2545
	}
2546
}
2547
2548
2549
/**
2550
 *	Class to manage lines of contracts
2551
 */
2552
class ContratLigne extends CommonObjectLine
2553
{
2554
    /**
2555
	 * @var string ID to identify managed object
2556
	 */
2557
	public $element='contratdet';
2558
2559
    /**
2560
	 * @var string Name of table without prefix where object is stored
2561
	 */
2562
	public $table_element='contratdet';
2563
2564
	/**
2565
	 * @var int ID
2566
	 */
2567
	public $id;
2568
2569
	/**
2570
	 * @var string Ref
2571
	 */
2572
	public $ref;
2573
2574
	public $tms;
2575
2576
	/**
2577
     * @var int ID
2578
     */
2579
	public $fk_contrat;
2580
2581
	/**
2582
     * @var int ID
2583
     */
2584
	public $fk_product;
2585
2586
	public $statut;					// 0 inactive, 4 active, 5 closed
2587
	public $type;						// 0 for product, 1 for service
2588
2589
	/**
2590
	 * @var string
2591
	 * @deprecated
2592
	 */
2593
	public $label;
2594
2595
	/**
2596
	 * @var string
2597
	 * @deprecated
2598
	 */
2599
	public $libelle;
2600
2601
	/**
2602
	 * @var string description
2603
	 */
2604
	public $description;
2605
2606
	public $product_ref;
2607
	public $product_label;
2608
2609
	public $date_commande;
2610
2611
	public $date_start;				// date start planned
2612
	public $date_start_real;			// date start real
2613
	public $date_end;					// date end planned
2614
	public $date_end_real;				// date end real
2615
	// For backward compatibility
2616
	public $date_ouverture_prevue;		// date start planned
2617
	public $date_ouverture;			// date start real
2618
	public $date_fin_validite;			// date end planned
2619
	public $date_cloture;				// date end real
2620
	public $tva_tx;
2621
	public $localtax1_tx;
2622
	public $localtax2_tx;
2623
	public $localtax1_type;	// Local tax 1 type
2624
	public $localtax2_type;	// Local tax 2 type
2625
	public $qty;
2626
	public $remise_percent;
2627
	public $remise;
2628
2629
	/**
2630
     * @var int ID
2631
     */
2632
	public $fk_remise_except;
2633
2634
	public $subprice;					// Unit price HT
2635
2636
	/**
2637
	 * @var float
2638
	 * @deprecated Use $price_ht instead
2639
	 * @see $price_ht
2640
	 */
2641
	public $price;
2642
2643
	public $price_ht;
2644
2645
	public $total_ht;
2646
	public $total_tva;
2647
	public $total_localtax1;
2648
	public $total_localtax2;
2649
	public $total_ttc;
2650
2651
	/**
2652
     * @var int ID
2653
     */
2654
	public $fk_fournprice;
2655
2656
	public $pa_ht;
2657
2658
	public $info_bits;
2659
2660
	/**
2661
     * @var int ID
2662
     */
2663
	public $fk_user_author;
2664
2665
	/**
2666
     * @var int ID
2667
     */
2668
	public $fk_user_ouverture;
2669
2670
	/**
2671
     * @var int ID
2672
     */
2673
	public $fk_user_cloture;
2674
2675
	public $commentaire;
2676
2677
	const STATUS_INITIAL = 0;
2678
	const STATUS_OPEN = 4;
2679
	const STATUS_CLOSED = 5;
2680
2681
2682
2683
	/**
2684
     *  Constructor
2685
     *
2686
     *  @param      DoliDb		$db      Database handler
2687
	 */
2688
	public function __construct($db)
2689
	{
2690
		$this->db = $db;
2691
	}
2692
2693
2694
	/**
2695
	 *  Return label of this contract line status
2696
	 *
2697
	 *	@param	int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
2698
	 *  @return string      		Libelle
2699
	 */
2700
	public function getLibStatut($mode)
2701
	{
2702
		return $this->LibStatut($this->statut, $mode, ((! empty($this->date_fin_validite))?($this->date_fin_validite < dol_now()?1:0):-1));
2703
	}
2704
2705
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2706
	/**
2707
	 *  Return label of a contract line status
2708
	 *
2709
	 *  @param	int		$statut     Id statut
2710
	 *	@param  int		$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
2711
	 *	@param	int		$expired	0=Not expired, 1=Expired, -1=Both or unknown
2712
	 *  @param	string	$moreatt	More attribute
2713
	 *  @return string      		Libelle
2714
	 */
2715
	public static function LibStatut($statut, $mode, $expired = -1, $moreatt = '')
2716
	{
2717
        // phpcs:enable
2718
		global $langs;
2719
		$langs->load("contracts");
2720
		if ($mode == 0)
2721
		{
2722
			if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial"); }
2723
			elseif ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning"); }
2724
			elseif ($statut == self::STATUS_OPEN && $expired == 0)  { return $langs->trans("ServiceStatusNotLate"); }
2725
			elseif ($statut == self::STATUS_OPEN && $expired == 1)  { return $langs->trans("ServiceStatusLate"); }
2726
			elseif ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed");  }
2727
		}
2728
		elseif ($mode == 1)
2729
		{
2730
			if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial"); }
2731
			elseif ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning"); }
2732
			elseif ($statut == self::STATUS_OPEN && $expired == 0)  { return $langs->trans("ServiceStatusNotLateShort"); }
2733
			elseif ($statut == self::STATUS_OPEN && $expired == 1)  { return $langs->trans("ServiceStatusLateShort"); }
2734
			elseif ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed");  }
2735
		}
2736
		elseif ($mode == 2)
2737
		{
2738
			if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'), 'statut0').' '.$langs->trans("ServiceStatusInitial"); }
2739
			elseif ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'), 'statut4').' '.$langs->trans("ServiceStatusRunning"); }
2740
			elseif ($statut == self::STATUS_OPEN && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'), 'statut4').' '.$langs->trans("ServiceStatusNotLateShort"); }
2741
			elseif ($statut == self::STATUS_OPEN && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'), 'statut3').' '.$langs->trans("ServiceStatusLateShort"); }
2742
			elseif ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'), 'statut6') .' '.$langs->trans("ServiceStatusClosed"); }
2743
		}
2744
		elseif ($mode == 3)
2745
		{
2746
			if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'), 'statut0', $moreatt); }
2747
			elseif ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'), 'statut4', $moreatt); }
2748
			elseif ($statut == self::STATUS_OPEN && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'), 'statut4', $moreatt); }
2749
			elseif ($statut == self::STATUS_OPEN && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'), 'statut3', $moreatt); }
2750
			elseif ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'), 'statut6', $moreatt); }
2751
		}
2752
		elseif ($mode == 4)
2753
		{
2754
			if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'), 'statut0').' '.$langs->trans("ServiceStatusInitial"); }
2755
			elseif ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'), 'statut4').' '.$langs->trans("ServiceStatusRunning"); }
2756
			elseif ($statut == self::STATUS_OPEN && $expired == 0)  { return img_picto($langs->trans('ServiceStatusNotLate'), 'statut4').' '.$langs->trans("ServiceStatusNotLate"); }
2757
			elseif ($statut == self::STATUS_OPEN && $expired == 1)  { return img_picto($langs->trans('ServiceStatusLate'), 'statut3').' '.$langs->trans("ServiceStatusLate"); }
2758
			elseif ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'), 'statut6') .' '.$langs->trans("ServiceStatusClosed"); }
2759
		}
2760
		elseif ($mode == 5)
2761
		{
2762
			if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial").' '.img_picto($langs->trans('ServiceStatusInitial'), 'statut0'); }
2763
			elseif ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning").' '.img_picto($langs->trans('ServiceStatusRunning'), 'statut4'); }
2764
			elseif ($statut == self::STATUS_OPEN && $expired == 0)  { return $langs->trans("ServiceStatusNotLateShort").' '.img_picto($langs->trans('ServiceStatusNotLateShort'), 'statut4'); }
2765
			elseif ($statut == self::STATUS_OPEN && $expired == 1)  { return $langs->trans("ServiceStatusLateShort").' '.img_picto($langs->trans('ServiceStatusLate'), 'statut3'); }
2766
			elseif ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed").' '.img_picto($langs->trans('ServiceStatusClosed'), 'statut6'); }
2767
		}
2768
	}
2769
2770
	/**
2771
	 *	Return clicable name (with picto eventually)
2772
	 *
2773
	 *  @param	int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
2774
	 *  @param	int		$maxlength		Max length
2775
	 *  @return	string					Chaine avec URL
2776
 	 */
2777
    public function getNomUrl($withpicto = 0, $maxlength = 0)
2778
	{
2779
		global $langs;
2780
2781
		$result='';
2782
        $label=$langs->trans("ShowContractOfService").': '.$this->label;
1 ignored issue
show
Deprecated Code introduced by
The property ContratLigne::$label has been deprecated. ( Ignorable by Annotation )

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

2782
        $label=$langs->trans("ShowContractOfService").': './** @scrutinizer ignore-deprecated */ $this->label;
Loading history...
2783
        if (empty($label)) $label=$this->description;
2784
2785
        $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
2786
		$linkend='</a>';
2787
2788
		$picto='service';
2789
		if ($this->type == 0) $picto='product';
2790
2791
        if ($withpicto) $result.=($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
2792
		if ($withpicto && $withpicto != 2) $result.=' ';
2793
		if ($withpicto != 2) $result.=$link.($this->product_ref?$this->product_ref.' ':'').($this->label?$this->label:$this->description).$linkend;
1 ignored issue
show
Deprecated Code introduced by
The property ContratLigne::$label has been deprecated. ( Ignorable by Annotation )

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

2793
		if ($withpicto != 2) $result.=$link.($this->product_ref?$this->product_ref.' ':'').($this->label?/** @scrutinizer ignore-deprecated */ $this->label:$this->description).$linkend;
Loading history...
2794
		return $result;
2795
	}
2796
2797
	/**
2798
	 *  Load object in memory from database
2799
	 *
2800
	 *  @param	int		$id         Id object
2801
	 *  @param	string	$ref		Ref of contract
2802
	 *  @return int         		<0 if KO, >0 if OK
2803
	 */
2804
    public function fetch($id, $ref = '')
2805
	{
2806
2807
		// Check parameters
2808
		if (empty($id) && empty($ref)) return -1;
2809
2810
		$sql = "SELECT";
2811
		$sql.= " t.rowid,";
2812
2813
		$sql.= " t.tms,";
2814
		$sql.= " t.fk_contrat,";
2815
		$sql.= " t.fk_product,";
2816
		$sql.= " t.statut,";
2817
		$sql.= " t.label,";			// This field is not used. Only label of product
2818
		$sql.= " p.ref as product_ref,";
2819
		$sql.= " p.label as product_label,";
2820
		$sql.= " p.description as product_desc,";
2821
		$sql.= " p.fk_product_type as product_type,";
2822
		$sql.= " t.description,";
2823
		$sql.= " t.date_commande,";
2824
		$sql.= " t.date_ouverture_prevue as date_ouverture_prevue,";
2825
		$sql.= " t.date_ouverture as date_ouverture,";
2826
		$sql.= " t.date_fin_validite as date_fin_validite,";
2827
		$sql.= " t.date_cloture as date_cloture,";
2828
		$sql.= " t.tva_tx,";
2829
		$sql.= " t.vat_src_code,";
2830
		$sql.= " t.localtax1_tx,";
2831
		$sql.= " t.localtax2_tx,";
2832
		$sql.= " t.localtax1_type,";
2833
		$sql.= " t.localtax2_type,";
2834
		$sql.= " t.qty,";
2835
		$sql.= " t.remise_percent,";
2836
		$sql.= " t.remise,";
2837
		$sql.= " t.fk_remise_except,";
2838
		$sql.= " t.subprice,";
2839
		$sql.= " t.price_ht,";
2840
		$sql.= " t.total_ht,";
2841
		$sql.= " t.total_tva,";
2842
		$sql.= " t.total_localtax1,";
2843
		$sql.= " t.total_localtax2,";
2844
		$sql.= " t.total_ttc,";
2845
		$sql.= " t.fk_product_fournisseur_price as fk_fournprice,";
2846
		$sql.= " t.buy_price_ht as pa_ht,";
2847
		$sql.= " t.info_bits,";
2848
		$sql.= " t.fk_user_author,";
2849
		$sql.= " t.fk_user_ouverture,";
2850
		$sql.= " t.fk_user_cloture,";
2851
		$sql.= " t.commentaire,";
2852
		$sql.= " t.fk_unit";
2853
		$sql.= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
2854
		if ($id)  $sql.= " WHERE t.rowid = ".$id;
2855
		if ($ref) $sql.= " WHERE t.rowid = '".$this->db->escape($ref)."'";
2856
2857
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2858
		$resql=$this->db->query($sql);
2859
		if ($resql)
2860
		{
2861
			if ($this->db->num_rows($resql))
2862
			{
2863
				$obj = $this->db->fetch_object($resql);
2864
2865
				$this->id    = $obj->rowid;
2866
				$this->ref   = $obj->rowid;
2867
2868
				$this->tms = $this->db->jdate($obj->tms);
2869
				$this->fk_contrat = $obj->fk_contrat;
2870
				$this->fk_product = $obj->fk_product;
2871
				$this->statut = $obj->statut;
2872
				$this->product_ref = $obj->product_ref;
2873
				$this->product_label = $obj->product_label;
2874
				$this->product_description = $obj->product_description;
2875
				$this->product_type = $obj->product_type;
2876
				$this->label = $obj->label;					// deprecated. We do not use this field. Only ref and label of product, and description of contract line
2877
				$this->description = $obj->description;
2878
				$this->date_commande = $this->db->jdate($obj->date_commande);
2879
2880
				$this->date_start = $this->db->jdate($obj->date_ouverture_prevue);
2881
				$this->date_start_real = $this->db->jdate($obj->date_ouverture);
2882
				$this->date_end = $this->db->jdate($obj->date_fin_validite);
2883
				$this->date_end_real = $this->db->jdate($obj->date_cloture);
2884
				// For backward compatibility
2885
				$this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
2886
				$this->date_ouverture = $this->db->jdate($obj->date_ouverture);
2887
				$this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
2888
				$this->date_cloture = $this->db->jdate($obj->date_cloture);
2889
2890
				$this->tva_tx = $obj->tva_tx;
2891
				$this->vat_src_code = $obj->vat_src_code;
2892
				$this->localtax1_tx = $obj->localtax1_tx;
2893
				$this->localtax2_tx = $obj->localtax2_tx;
2894
				$this->localtax1_type = $obj->localtax1_type;
2895
				$this->localtax2_type = $obj->localtax2_type;
2896
				$this->qty = $obj->qty;
2897
				$this->remise_percent = $obj->remise_percent;
2898
				$this->remise = $obj->remise;
2899
				$this->fk_remise_except = $obj->fk_remise_except;
2900
				$this->subprice = $obj->subprice;
2901
				$this->price_ht = $obj->price_ht;
2902
				$this->total_ht = $obj->total_ht;
2903
				$this->total_tva = $obj->total_tva;
2904
				$this->total_localtax1 = $obj->total_localtax1;
2905
				$this->total_localtax2 = $obj->total_localtax2;
2906
				$this->total_ttc = $obj->total_ttc;
2907
				$this->info_bits = $obj->info_bits;
2908
				$this->fk_user_author = $obj->fk_user_author;
2909
				$this->fk_user_ouverture = $obj->fk_user_ouverture;
2910
				$this->fk_user_cloture = $obj->fk_user_cloture;
2911
				$this->commentaire = $obj->commentaire;
2912
				$this->fk_fournprice = $obj->fk_fournprice;
2913
				$marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
2914
				$this->pa_ht = $marginInfos[0];
2915
				$this->fk_unit     = $obj->fk_unit;
2916
			}
2917
			$this->db->free($resql);
2918
2919
			return 1;
2920
		}
2921
		else
2922
		{
2923
			$this->error="Error ".$this->db->lasterror();
2924
			return -1;
2925
		}
2926
	}
2927
2928
2929
	/**
2930
	 *      Update database for contract line
2931
	 *
2932
	 *      @param	User	$user        	User that modify
2933
	 *      @param  int		$notrigger	    0=no, 1=yes (no update trigger)
2934
	 *      @return int         			<0 if KO, >0 if OK
2935
	 */
2936
	public function update($user, $notrigger = 0)
2937
	{
2938
		global $conf, $langs, $mysoc;
2939
2940
		$error=0;
2941
2942
		// Clean parameters
2943
		$this->fk_contrat = (int) $this->fk_contrat;
2944
		$this->fk_product = (int) $this->fk_product;
2945
		$this->statut = (int) $this->statut;
2946
		$this->label=trim($this->label);
1 ignored issue
show
Deprecated Code introduced by
The property ContratLigne::$label has been deprecated. ( Ignorable by Annotation )

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

2946
		$this->label=trim(/** @scrutinizer ignore-deprecated */ $this->label);
Loading history...
2947
		$this->description=trim($this->description);
2948
		$this->vat_src_code=trim($this->vat_src_code);
2949
		$this->tva_tx=trim($this->tva_tx);
2950
		$this->localtax1_tx=trim($this->localtax1_tx);
2951
		$this->localtax2_tx=trim($this->localtax2_tx);
2952
		$this->qty=trim($this->qty);
2953
		$this->remise_percent=trim($this->remise_percent);
2954
		$this->remise=trim($this->remise);
2955
		$this->fk_remise_except = (int) $this->fk_remise_except;
2956
		$this->subprice=price2num($this->subprice);
2957
		$this->price_ht=price2num($this->price_ht);
2958
		$this->total_ht=trim($this->total_ht);
2959
		$this->total_tva=trim($this->total_tva);
2960
		$this->total_localtax1=trim($this->total_localtax1);
2961
		$this->total_localtax2=trim($this->total_localtax2);
2962
		$this->total_ttc=trim($this->total_ttc);
2963
		$this->info_bits=trim($this->info_bits);
2964
		$this->fk_user_author = (int) $this->fk_user_author;
2965
		$this->fk_user_ouverture = (int) $this->fk_user_ouverture;
2966
		$this->fk_user_cloture = (int) $this->fk_user_cloture;
2967
		$this->commentaire=trim($this->commentaire);
2968
		//if (empty($this->subprice)) $this->subprice = 0;
2969
		if (empty($this->price_ht)) $this->price_ht = 0;
2970
		if (empty($this->total_ht)) $this->total_ht = 0;
2971
		if (empty($this->total_tva)) $this->total_tva = 0;
2972
		if (empty($this->total_ttc)) $this->total_ttc = 0;
2973
		if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
2974
		if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
2975
		if (empty($this->remise_percent)) $this->remise_percent = 0;
2976
		// For backward compatibility
2977
		if (empty($this->date_start))      $this->date_start=$this->date_ouverture_prevue;
2978
		if (empty($this->date_start_real)) $this->date_start=$this->date_ouverture;
2979
		if (empty($this->date_end))        $this->date_start=$this->date_fin_validite;
2980
		if (empty($this->date_end_real))   $this->date_start=$this->date_cloture;
2981
2982
2983
		// Check parameters
2984
		// Put here code to add control on parameters values
2985
2986
		// Calcul du total TTC et de la TVA pour la ligne a partir de
2987
		// qty, pu, remise_percent et txtva
2988
		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2989
		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2990
		$localtaxes_type = getLocalTaxesFromRate($this->txtva, 0, $this->societe, $mysoc);
0 ignored issues
show
Bug Best Practice introduced by
The property societe does not exist on ContratLigne. Did you maybe forget to declare it?
Loading history...
Bug Best Practice introduced by
The property txtva does not exist on ContratLigne. Did you maybe forget to declare it?
Loading history...
2991
2992
		$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);
2993
		$this->total_ht  = $tabprice[0];
2994
		$this->total_tva = $tabprice[1];
2995
		$this->total_ttc = $tabprice[2];
2996
		$this->total_localtax1= $tabprice[9];
2997
		$this->total_localtax2= $tabprice[10];
2998
2999
	    if (empty($this->pa_ht)) $this->pa_ht=0;
3000
3001
		// if buy price not defined, define buyprice as configured in margin admin
3002
		if ($this->pa_ht == 0)
3003
		{
3004
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
3005
			{
3006
				return $result;
3007
			}
3008
			else
3009
			{
3010
				$this->pa_ht = $result;
3011
			}
3012
		}
3013
3014
3015
		$this->db->begin();
3016
3017
		$this->oldcopy = new ContratLigne($this->db);
3018
		$this->oldcopy->fetch($this->id);
3019
3020
		// Update request
3021
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
3022
		$sql.= " fk_contrat=".$this->fk_contrat.",";
3023
		$sql.= " fk_product=".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":'null').",";
3024
		$sql.= " statut=".$this->statut.",";
3025
		$sql.= " label='".$this->db->escape($this->label)."',";
1 ignored issue
show
Deprecated Code introduced by
The property ContratLigne::$label has been deprecated. ( Ignorable by Annotation )

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

3025
		$sql.= " label='".$this->db->escape(/** @scrutinizer ignore-deprecated */ $this->label)."',";
Loading history...
3026
		$sql.= " description='".$this->db->escape($this->description)."',";
3027
		$sql.= " date_commande=".($this->date_commande!=''?"'".$this->db->idate($this->date_commande)."'":"null").",";
3028
		$sql.= " date_ouverture_prevue=".($this->date_ouverture_prevue!=''?"'".$this->db->idate($this->date_ouverture_prevue)."'":"null").",";
3029
		$sql.= " date_ouverture=".($this->date_ouverture!=''?"'".$this->db->idate($this->date_ouverture)."'":"null").",";
3030
		$sql.= " date_fin_validite=".($this->date_fin_validite!=''?"'".$this->db->idate($this->date_fin_validite)."'":"null").",";
3031
		$sql.= " date_cloture=".($this->date_cloture!=''?"'".$this->db->idate($this->date_cloture)."'":"null").",";
3032
		$sql.= " vat_src_code='".$this->db->escape($this->vat_src_code)."',";
3033
		$sql.= " tva_tx=".price2num($this->tva_tx).",";
3034
		$sql.= " localtax1_tx=".price2num($this->localtax1_tx).",";
3035
		$sql.= " localtax2_tx=".price2num($this->localtax2_tx).",";
3036
		$sql.= " qty=".price2num($this->qty).",";
3037
		$sql.= " remise_percent=".price2num($this->remise_percent).",";
3038
		$sql.= " remise=".($this->remise?price2num($this->remise):"null").",";
3039
		$sql.= " fk_remise_except=".($this->fk_remise_except > 0?$this->fk_remise_except:"null").",";
3040
		$sql.= " subprice=".($this->subprice != '' ? $this->subprice : "null").",";
3041
		$sql.= " price_ht=".($this->price_ht != '' ? $this->price_ht : "null").",";
3042
		$sql.= " total_ht=".$this->total_ht.",";
3043
		$sql.= " total_tva=".$this->total_tva.",";
3044
		$sql.= " total_localtax1=".$this->total_localtax1.",";
3045
		$sql.= " total_localtax2=".$this->total_localtax2.",";
3046
		$sql.= " total_ttc=".$this->total_ttc.",";
3047
		$sql.= " fk_product_fournisseur_price=".(!empty($this->fk_fournprice)?$this->fk_fournprice:"NULL").",";
3048
		$sql.= " buy_price_ht='".price2num($this->pa_ht)."',";
3049
		$sql.= " info_bits='".$this->db->escape($this->info_bits)."',";
3050
		$sql.= " fk_user_author=".($this->fk_user_author >= 0?$this->fk_user_author:"NULL").",";
3051
		$sql.= " fk_user_ouverture=".($this->fk_user_ouverture > 0?$this->fk_user_ouverture:"NULL").",";
3052
		$sql.= " fk_user_cloture=".($this->fk_user_cloture > 0?$this->fk_user_cloture:"NULL").",";
3053
		$sql.= " commentaire='".$this->db->escape($this->commentaire)."',";
3054
		$sql.= " fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3055
		$sql.= " WHERE rowid=".$this->id;
3056
3057
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
3058
		$resql = $this->db->query($sql);
3059
		if (! $resql)
3060
		{
3061
			$this->error="Error ".$this->db->lasterror();
3062
			$error++;
3063
		}
3064
3065
		if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
3066
		{
3067
			$result=$this->insertExtraFields();
3068
			if ($result < 0)
3069
			{
3070
				$error++;
3071
			}
3072
		}
3073
3074
		// If we change a planned date (start or end), sync dates for all services
3075
		if (! $error && ! empty($conf->global->CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES))
3076
		{
3077
			if ($this->date_ouverture_prevue != $this->oldcopy->date_ouverture_prevue)
3078
			{
3079
				$sql ='UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
3080
				$sql.= " date_ouverture_prevue = ".($this->date_ouverture_prevue!=''?"'".$this->db->idate($this->date_ouverture_prevue)."'":"null");
3081
				$sql.= " WHERE fk_contrat = ".$this->fk_contrat;
3082
3083
				$resql = $this->db->query($sql);
3084
				if (! $resql)
3085
				{
3086
					$error++;
3087
					$this->error="Error ".$this->db->lasterror();
3088
				}
3089
			}
3090
			if ($this->date_fin_validite != $this->oldcopy->date_fin_validite)
3091
			{
3092
				$sql ='UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
3093
				$sql.= " date_fin_validite = ".($this->date_fin_validite!=''?"'".$this->db->idate($this->date_fin_validite)."'":"null");
3094
				$sql.= " WHERE fk_contrat = ".$this->fk_contrat;
3095
3096
				$resql = $this->db->query($sql);
3097
				if (! $resql)
3098
				{
3099
					$error++;
3100
					$this->error="Error ".$this->db->lasterror();
3101
				}
3102
			}
3103
		}
3104
3105
		if (! $error && ! $notrigger) {
3106
	        // Call trigger
3107
	        $result=$this->call_trigger('LINECONTRACT_UPDATE', $user);
3108
	        if ($result < 0) {
3109
                $error++;
3110
                $this->db->rollback();
3111
            }
3112
	        // End call triggers
3113
		}
3114
3115
		if (! $error)
3116
		{
3117
        	$this->db->commit();
3118
			return 1;
3119
		} else {
3120
			$this->db->rollback();
3121
			$this->errors[]=$this->error;
3122
			return -1;
3123
		}
3124
	}
3125
3126
3127
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3128
	/**
3129
	 *      Mise a jour en base des champs total_xxx de ligne
3130
	 *		Used by migration process
3131
	 *
3132
	 *		@return		int		<0 if KO, >0 if OK
3133
	 */
3134
	public function update_total()
3135
	{
3136
        // phpcs:enable
3137
		$this->db->begin();
3138
3139
		// Mise a jour ligne en base
3140
		$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
3141
		$sql.= " total_ht=".price2num($this->total_ht, 'MT')."";
3142
		$sql.= ",total_tva=".price2num($this->total_tva, 'MT')."";
3143
		$sql.= ",total_localtax1=".price2num($this->total_localtax1, 'MT')."";
3144
		$sql.= ",total_localtax2=".price2num($this->total_localtax2, 'MT')."";
3145
		$sql.= ",total_ttc=".price2num($this->total_ttc, 'MT')."";
3146
		$sql.= " WHERE rowid = ".$this->id;
3147
3148
		dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
3149
3150
		$resql=$this->db->query($sql);
3151
		if ($resql)
3152
		{
3153
			$this->db->commit();
3154
			return 1;
3155
		}
3156
		else
3157
		{
3158
			$this->error=$this->db->error();
3159
			$this->db->rollback();
3160
			return -2;
3161
		}
3162
	}
3163
3164
3165
	/**
3166
	 * Inserts a contrat line into database
3167
	 *
3168
	 * @param int $notrigger Set to 1 if you don't want triggers to be fired
3169
	 * @return int <0 if KO, >0 if OK
3170
	 */
3171
	public function insert($notrigger = 0)
3172
	{
3173
		global $conf, $user;
3174
3175
		// Insertion dans la base
3176
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
3177
		$sql.= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
3178
		$sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
3179
		$sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
3180
		$sql.= " info_bits,";
3181
		$sql.= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
3182
		if ($this->date_ouverture_prevue > 0) { $sql.= ",date_ouverture_prevue"; }
3183
		if ($this->date_fin_validite > 0)     { $sql.= ",date_fin_validite"; }
3184
		$sql.= ") VALUES ($this->fk_contrat, '', '" . $this->db->escape($this->description) . "',";
3185
		$sql.= ($this->fk_product>0 ? $this->fk_product : "null").",";
3186
		$sql.= " '".$this->db->escape($this->qty)."',";
3187
		$sql.= " '".$this->db->escape($this->vat_src_code)."',";
3188
		$sql.= " '".$this->db->escape($this->tva_tx)."',";
3189
		$sql.= " '".$this->db->escape($this->localtax1_tx)."',";
3190
		$sql.= " '".$this->db->escape($this->localtax2_tx)."',";
3191
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
3192
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
3193
		$sql.= " ".price2num($this->remise_percent).",".price2num($this->subprice).",";
3194
		$sql.= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
3195
		$sql.= " '".$this->db->escape($this->info_bits)."',";
3196
		$sql.= " ".price2num($this->price_ht).",".price2num($this->remise).",";
3197
		if ($this->fk_fournprice > 0) $sql.= ' '.$this->fk_fournprice.',';
3198
		else $sql.= ' null,';
3199
		if ($this->pa_ht > 0) $sql.= ' '.price2num($this->pa_ht);
3200
		else $sql.= ' null';
3201
		if ($this->date_ouverture > 0) { $sql.= ",'".$this->db->idate($this->date_ouverture)."'"; }
3202
		if ($this->date_cloture > 0)   { $sql.= ",'".$this->db->idate($this->date_cloture)."'"; }
3203
		$sql.= ")";
3204
3205
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3206
3207
		$resql=$this->db->query($sql);
3208
		if ($resql)
3209
		{
3210
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
3211
3212
			// Insert of extrafields
3213
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0) // For avoid conflicts if trigger used
3214
			{
3215
				$result = $this->insertExtraFields();
3216
				if ($result < 0)
3217
				{
3218
					$this->db->rollback();
3219
					return -1;
3220
				}
3221
			}
3222
3223
			if (!$notrigger)
3224
			{
3225
				// Call trigger
3226
				$result = $this->call_trigger('LINECONTRACT_INSERT', $user);
3227
				if ($result < 0) {
3228
					$this->db->rollback();
3229
					return -1;
3230
				}
3231
				// End call triggers
3232
			}
3233
3234
			$this->db->commit();
3235
			return 1;
3236
		}
3237
		else
3238
		{
3239
			$this->db->rollback();
3240
			$this->error=$this->db->error()." sql=".$sql;
3241
			return -1;
3242
		}
3243
	}
3244
3245
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3246
	/**
3247
	 *  Activate a contract line
3248
	 *
3249
	 * @param   User 		$user 		Objet User who activate contract
3250
	 * @param  	int 		$date 		Date activation
3251
	 * @param  	int|string 	$date_end 	Date planned end. Use '-1' to keep it unchanged.
3252
	 * @param   string 		$comment 	A comment typed by user
3253
	 * @return 	int                    	<0 if KO, >0 if OK
3254
	 */
3255
	public function active_line($user, $date, $date_end = '', $comment = '')
3256
	{
3257
        // phpcs:enable
3258
		global $langs, $conf;
3259
3260
		$error = 0;
3261
3262
		$this->db->begin();
3263
3264
		$sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = ".ContratLigne::STATUS_OPEN.",";
3265
		$sql .= " date_ouverture = " . (dol_strlen($date) != 0 ? "'" . $this->db->idate($date) . "'" : "null") . ",";
3266
		if ($date_end >= 0) $sql .= " date_fin_validite = " . (dol_strlen($date_end) != 0 ? "'" . $this->db->idate($date_end) . "'" : "null") . ",";
3267
		$sql .= " fk_user_ouverture = " . $user->id . ",";
3268
		$sql .= " date_cloture = null,";
3269
		$sql .= " commentaire = '" . $this->db->escape($comment) . "'";
3270
		$sql .= " WHERE rowid = " . $this->id . " AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")";
3271
3272
		dol_syslog(get_class($this) . "::active_line", LOG_DEBUG);
3273
		$resql = $this->db->query($sql);
3274
		if ($resql) {
3275
			// Call trigger
3276
			$result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
3277
			if ($result < 0) $error++;
3278
			// End call triggers
3279
3280
			if (! $error)
3281
			{
3282
				$this->statut = ContratLigne::STATUS_OPEN;
3283
				$this->date_ouverture = $date;
3284
				$this->date_fin_validite = $date_end;
3285
				$this->fk_user_ouverture = $user->id;
3286
				$this->date_cloture = null;
3287
				$this->commentaire = $comment;
3288
3289
				$this->db->commit();
3290
				return 1;
3291
			}
3292
			else
3293
			{
3294
				$this->db->rollback();
3295
				return -1;
3296
			}
3297
		} else {
3298
			$this->error = $this->db->lasterror();
3299
			$this->db->rollback();
3300
			return -1;
3301
		}
3302
	}
3303
3304
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3305
	/**
3306
	 *  Close a contract line
3307
	 *
3308
	 * @param    User 	$user 			Objet User who close contract
3309
	 * @param  	 int 	$date_end 		Date end
3310
	 * @param    string $comment 		A comment typed by user
3311
     * @param    int	$notrigger		1=Does not execute triggers, 0=Execute triggers
3312
	 * @return int                    	<0 if KO, >0 if OK
3313
	 */
3314
	public function close_line($user, $date_end, $comment = '', $notrigger = 0)
3315
	{
3316
        // phpcs:enable
3317
		global $langs, $conf;
3318
3319
		// Update object
3320
		$this->date_cloture = $date_end;
3321
		$this->fk_user_cloture = $user->id;
3322
		$this->commentaire = $comment;
3323
3324
		$error = 0;
3325
3326
		// statut actif : 4
3327
3328
		$this->db->begin();
3329
3330
		$sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = ".ContratLigne::STATUS_CLOSED.",";
3331
		$sql .= " date_cloture = '" . $this->db->idate($date_end) . "',";
3332
		$sql .= " fk_user_cloture = " . $user->id . ",";
3333
		$sql .= " commentaire = '" . $this->db->escape($comment) . "'";
3334
		$sql .= " WHERE rowid = " . $this->id . " AND statut = ".ContratLigne::STATUS_OPEN;
3335
3336
		$resql = $this->db->query($sql);
3337
		if ($resql)
3338
		{
3339
			if (! $notrigger)
3340
			{
3341
				// Call trigger
3342
				$result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
3343
				if ($result < 0) {
3344
					$error++;
3345
					$this->db->rollback();
3346
					return -1;
3347
				}
3348
				// End call triggers
3349
			}
3350
3351
			$this->db->commit();
3352
			return 1;
3353
		} else {
3354
			$this->error = $this->db->lasterror();
3355
			$this->db->rollback();
3356
			return -1;
3357
		}
3358
	}
3359
}
3360