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

Task::createFromClone()   F

Complexity

Conditions 27
Paths 9760

Size

Total Lines 217
Code Lines 111

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 27
eloc 111
c 0
b 0
f 0
nc 9760
nop 10
dl 0
loc 217
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* Copyright (C) 2008-2014	Laurent Destailleur	<[email protected]>
3
 * Copyright (C) 2010-2012	Regis Houssin		<[email protected]>
4
 * Copyright (C) 2014       Marcos García       <[email protected]>
5
 * Copyright (C) 2018       Frédéric France     <[email protected]>
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
/**
22
 *      \file       htdocs/projet/class/task.class.php
23
 *      \ingroup    project
24
 *      \brief      This file is a CRUD class file for Task (Create/Read/Update/Delete)
25
 */
26
27
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
28
29
30
/**
31
 * 	Class to manage tasks
32
 */
33
class Task extends CommonObject
34
{
35
	/**
36
	 * @var string ID to identify managed object
37
	 */
38
	public $element='project_task';
39
40
	/**
41
	 * @var string Name of table without prefix where object is stored
42
	 */
43
	public $table_element='projet_task';
44
45
	/**
46
	 * @var int Field with ID of parent key if this field has a parent
47
	 */
48
	public $fk_element='fk_task';
49
50
	public $picto = 'task';
51
52
	/**
53
	 * @var array	List of child tables. To test if we can delete object.
54
	 */
55
	protected $childtables=array('projet_task_time');
56
57
	/**
58
     * @var int ID parent task
59
     */
60
    public $fk_task_parent;
61
62
    /**
63
     * @var string Label of task
64
     */
65
    public $label;
66
67
	/**
68
	 * @var string description
69
	 */
70
	public $description;
71
72
	public $duration_effective;		// total of time spent on this task
73
	public $planned_workload;
74
	public $date_c;
75
	public $date_start;
76
	public $date_end;
77
	public $progress;
78
79
	/**
80
     * @var int ID
81
     */
82
	public $fk_statut;
83
84
	public $priority;
85
86
	/**
87
     * @var int ID
88
     */
89
	public $fk_user_creat;
90
91
	/**
92
     * @var int ID
93
     */
94
	public $fk_user_valid;
95
96
	public $rang;
97
98
	public $timespent_min_date;
99
	public $timespent_max_date;
100
	public $timespent_total_duration;
101
	public $timespent_total_amount;
102
	public $timespent_nblinesnull;
103
	public $timespent_nblines;
104
	// For detail of lines of timespent record, there is the property ->lines in common
105
106
	// Var used to call method addTimeSpent(). Bad practice.
107
	public $timespent_id;
108
	public $timespent_duration;
109
	public $timespent_old_duration;
110
	public $timespent_date;
111
	public $timespent_datehour;		// More accurate start date (same than timespent_date but includes hours, minutes and seconds)
112
	public $timespent_withhour;		// 1 = we entered also start hours for timesheet line
113
	public $timespent_fk_user;
114
	public $timespent_note;
115
116
	public $comments = array();
117
118
	public $oldcopy;
119
120
121
	/**
122
	 *  Constructor
123
	 *
124
	 *  @param      DoliDB		$db      Database handler
125
	 */
126
	public function __construct($db)
127
	{
128
		$this->db = $db;
129
	}
130
131
132
	/**
133
	 *  Create into database
134
	 *
135
	 *  @param	User	$user        	User that create
136
	 *  @param 	int		$notrigger	    0=launch triggers after, 1=disable triggers
137
	 *  @return int 		        	<0 if KO, Id of created object if OK
138
	 */
139
	public function create($user, $notrigger = 0)
140
	{
141
		global $conf, $langs;
142
143
		$error=0;
144
145
		// Clean parameters
146
		$this->label = trim($this->label);
147
		$this->description = trim($this->description);
148
149
		// Check parameters
150
		// Put here code to add control on parameters values
151
152
		// Insert request
153
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
154
		$sql.= "fk_projet";
155
		$sql.= ", ref";
156
		$sql.= ", fk_task_parent";
157
		$sql.= ", label";
158
		$sql.= ", description";
159
		$sql.= ", datec";
160
		$sql.= ", fk_user_creat";
161
		$sql.= ", dateo";
162
		$sql.= ", datee";
163
		$sql.= ", planned_workload";
164
		$sql.= ", progress";
165
		$sql.= ") VALUES (";
166
		$sql.= $this->fk_project;
167
		$sql.= ", ".(!empty($this->ref)?"'".$this->db->escape($this->ref)."'":'null');
168
		$sql.= ", ".$this->fk_task_parent;
169
		$sql.= ", '".$this->db->escape($this->label)."'";
170
		$sql.= ", '".$this->db->escape($this->description)."'";
171
		$sql.= ", '".$this->db->idate($this->date_c)."'";
172
		$sql.= ", ".$user->id;
173
		$sql.= ", ".($this->date_start!=''?"'".$this->db->idate($this->date_start)."'":'null');
174
		$sql.= ", ".($this->date_end!=''?"'".$this->db->idate($this->date_end)."'":'null');
175
		$sql.= ", ".(($this->planned_workload!='' && $this->planned_workload >= 0)?$this->planned_workload:'null');
176
		$sql.= ", ".(($this->progress!='' && $this->progress >= 0)?$this->progress:'null');
177
		$sql.= ")";
178
179
		$this->db->begin();
180
181
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
182
		$resql=$this->db->query($sql);
183
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
184
185
		if (! $error)
186
		{
187
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
188
189
			if (! $notrigger)
190
			{
191
				// Call trigger
192
				$result=$this->call_trigger('TASK_CREATE', $user);
193
				if ($result < 0) { $error++; }
194
				// End call triggers
195
			}
196
		}
197
198
		// Update extrafield
199
		if (! $error)
200
		{
201
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
202
			{
203
				$result=$this->insertExtraFields();
204
				if ($result < 0)
205
				{
206
					$error++;
207
				}
208
			}
209
		}
210
211
		// Commit or rollback
212
		if ($error)
213
		{
214
			foreach($this->errors as $errmsg)
215
			{
216
				dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
217
				$this->error.=($this->error?', '.$errmsg:$errmsg);
218
			}
219
			$this->db->rollback();
220
			return -1*$error;
221
		}
222
		else
223
		{
224
			$this->db->commit();
225
			return $this->id;
226
		}
227
	}
228
229
230
	/**
231
	 *  Load object in memory from database
232
	 *
233
	 *  @param	int		$id					Id object
234
	 *  @param	int		$ref				ref object
235
	 *  @param	int		$loadparentdata		Also load parent data
236
	 *  @return int 		        		<0 if KO, 0 if not found, >0 if OK
237
	 */
238
	public function fetch($id, $ref = '', $loadparentdata = 0)
239
	{
240
		global $langs, $conf;
241
242
		$sql = "SELECT";
243
		$sql.= " t.rowid,";
244
		$sql.= " t.ref,";
245
		$sql.= " t.fk_projet as fk_project,";
246
		$sql.= " t.fk_task_parent,";
247
		$sql.= " t.label,";
248
		$sql.= " t.description,";
249
		$sql.= " t.duration_effective,";
250
		$sql.= " t.planned_workload,";
251
		$sql.= " t.datec,";
252
		$sql.= " t.dateo,";
253
		$sql.= " t.datee,";
254
		$sql.= " t.fk_user_creat,";
255
		$sql.= " t.fk_user_valid,";
256
		$sql.= " t.fk_statut,";
257
		$sql.= " t.progress,";
258
		$sql.= " t.priority,";
259
		$sql.= " t.note_private,";
260
		$sql.= " t.note_public,";
261
		$sql.= " t.rang";
262
		if (! empty($loadparentdata))
263
		{
264
			$sql.=", t2.ref as task_parent_ref";
265
			$sql.=", t2.rang as task_parent_position";
266
		}
267
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task as t";
268
		if (! empty($loadparentdata)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
269
		$sql.= " WHERE ";
270
		if (!empty($ref)) {
271
			$sql.="t.ref = '".$this->db->escape($ref)."'";
272
		}else {
273
			$sql.="t.rowid = ".$id;
274
		}
275
276
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
277
		$resql=$this->db->query($sql);
278
		if ($resql)
279
		{
280
			$num_rows = $this->db->num_rows($resql);
281
282
			if ($num_rows)
283
			{
284
				$obj = $this->db->fetch_object($resql);
285
286
				$this->id					= $obj->rowid;
287
				$this->ref					= $obj->ref;
288
				$this->fk_project			= $obj->fk_project;
289
				$this->fk_task_parent		= $obj->fk_task_parent;
290
				$this->label				= $obj->label;
291
				$this->description			= $obj->description;
292
				$this->duration_effective	= $obj->duration_effective;
293
				$this->planned_workload		= $obj->planned_workload;
294
				$this->date_c				= $this->db->jdate($obj->datec);
295
				$this->date_start			= $this->db->jdate($obj->dateo);
296
				$this->date_end				= $this->db->jdate($obj->datee);
297
				$this->fk_user_creat		= $obj->fk_user_creat;
298
				$this->fk_user_valid		= $obj->fk_user_valid;
299
				$this->fk_statut			= $obj->fk_statut;
300
				$this->progress				= $obj->progress;
301
				$this->priority				= $obj->priority;
302
				$this->note_private			= $obj->note_private;
303
				$this->note_public			= $obj->note_public;
304
				$this->rang					= $obj->rang;
305
306
				if (! empty($loadparentdata))
307
				{
308
					$this->task_parent_ref      = $obj->task_parent_ref;
309
					$this->task_parent_position = $obj->task_parent_position;
310
				}
311
312
				// Retreive all extrafield
313
				$this->fetch_optionals();
314
			}
315
316
			$this->db->free($resql);
317
318
			if ($num_rows)
319
			{
320
				return 1;
321
			}else {
322
				return 0;
323
			}
324
		}
325
		else
326
		{
327
			$this->error="Error ".$this->db->lasterror();
328
			return -1;
329
		}
330
	}
331
332
333
	/**
334
	 *  Update database
335
	 *
336
	 *  @param	User	$user        	User that modify
337
	 *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
338
	 *  @return int			         	<=0 if KO, >0 if OK
339
	 */
340
	public function update($user = null, $notrigger = 0)
341
	{
342
		global $conf, $langs;
343
		$error=0;
344
345
		// Clean parameters
346
		if (isset($this->fk_project)) $this->fk_project=trim($this->fk_project);
1 ignored issue
show
Documentation Bug introduced by
The property $fk_project was declared of type integer, but trim($this->fk_project) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
347
		if (isset($this->ref)) $this->ref=trim($this->ref);
348
		if (isset($this->fk_task_parent)) $this->fk_task_parent = (int) $this->fk_task_parent;
349
		if (isset($this->label)) $this->label=trim($this->label);
350
		if (isset($this->description)) $this->description=trim($this->description);
351
		if (isset($this->duration_effective)) $this->duration_effective=trim($this->duration_effective);
352
		if (isset($this->planned_workload)) $this->planned_workload=trim($this->planned_workload);
353
354
		// Check parameters
355
		// Put here code to add control on parameters values
356
357
		// Update request
358
		$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
359
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
360
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"'".$this->db->escape($this->id)."'").",";
361
		$sql.= " fk_task_parent=".(isset($this->fk_task_parent)?$this->fk_task_parent:"null").",";
362
		$sql.= " label=".(isset($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
363
		$sql.= " description=".(isset($this->description)?"'".$this->db->escape($this->description)."'":"null").",";
364
		$sql.= " duration_effective=".(isset($this->duration_effective)?$this->duration_effective:"null").",";
365
		$sql.= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '')?$this->planned_workload:"null").",";
366
		$sql.= " dateo=".($this->date_start!=''?"'".$this->db->idate($this->date_start)."'":'null').",";
367
		$sql.= " datee=".($this->date_end!=''?"'".$this->db->idate($this->date_end)."'":'null').",";
368
		$sql.= " progress=".(($this->progress!='' && $this->progress >= 0)?$this->progress:'null').",";
369
		$sql.= " rang=".((!empty($this->rang))?$this->rang:"0");
370
		$sql.= " WHERE rowid=".$this->id;
371
372
		$this->db->begin();
373
374
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
375
		$resql = $this->db->query($sql);
376
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
377
378
		// Update extrafield
379
		if (! $error) {
380
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
381
			{
382
				$result=$this->insertExtraFields();
383
				if ($result < 0)
384
				{
385
					$error++;
386
				}
387
			}
388
		}
389
390
		if (! $error)
391
		{
392
			if (! $notrigger)
393
			{
394
				// Call trigger
395
				$result=$this->call_trigger('TASK_MODIFY', $user);
396
				if ($result < 0) { $error++; }
397
				// End call triggers
398
			}
399
		}
400
401
		if (! $error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref))
402
		{
403
			// We remove directory
404
			if ($conf->projet->dir_output)
405
			{
406
				$project = new Project($this->db);
407
				$project->fetch($this->fk_project);
408
409
				$olddir = $conf->projet->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
410
				$newdir = $conf->projet->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
411
				if (file_exists($olddir))
412
				{
413
					include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
414
					$res=dol_move($olddir, $newdir);
415
					if (! $res)
416
					{
417
						$langs->load("errors");
418
						$this->error=$langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
419
						$error++;
420
					}
421
				}
422
			}
423
		}
424
425
		// Commit or rollback
426
		if ($error)
427
		{
428
			foreach($this->errors as $errmsg)
429
			{
430
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
431
				$this->error.=($this->error?', '.$errmsg:$errmsg);
432
			}
433
			$this->db->rollback();
434
			return -1*$error;
435
		}
436
		else
437
		{
438
			$this->db->commit();
439
			return 1;
440
		}
441
	}
442
443
444
	/**
445
	 *	Delete task from database
446
	 *
447
	 *	@param	User	$user        	User that delete
448
	 *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
449
	 *	@return	int						<0 if KO, >0 if OK
450
	 */
451
	public function delete($user, $notrigger = 0)
452
	{
453
454
		global $conf, $langs;
455
		require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
456
457
		$error=0;
458
459
		$this->db->begin();
460
461
		if ($this->hasChildren() > 0)
462
		{
463
			dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
464
			$this->error='ErrorRecordHasSubTasks';
465
			$this->db->rollback();
466
			return 0;
467
		}
468
469
		$objectisused = $this->isObjectUsed($this->id);
470
		if (! empty($objectisused))
471
		{
472
			dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
473
			$this->error='ErrorRecordHasChildren';
474
			$this->db->rollback();
475
			return 0;
476
		}
477
478
		if (! $error)
479
		{
480
			// Delete linked contacts
481
			$res = $this->delete_linked_contact();
482
			if ($res < 0)
483
			{
484
				$this->error='ErrorFailToDeleteLinkedContact';
485
				//$error++;
486
				$this->db->rollback();
487
				return 0;
488
			}
489
		}
490
491
		if (! $error)
492
		{
493
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
494
			$sql.= " WHERE fk_task=".$this->id;
495
496
			$resql = $this->db->query($sql);
497
			if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
498
		}
499
500
		if (! $error)
501
		{
502
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
503
			$sql.= " WHERE fk_object=".$this->id;
504
505
			$resql = $this->db->query($sql);
506
			if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
507
		}
508
509
		if (! $error)
510
		{
511
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
512
			$sql.= " WHERE rowid=".$this->id;
513
514
			$resql = $this->db->query($sql);
515
			if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
516
		}
517
518
		if (! $error)
519
		{
520
			if (! $notrigger)
521
			{
522
				// Call trigger
523
				$result=$this->call_trigger('TASK_DELETE', $user);
524
				if ($result < 0) { $error++; }
525
				// End call triggers
526
			}
527
		}
528
529
		// Commit or rollback
530
		if ($error)
531
		{
532
			foreach($this->errors as $errmsg)
533
			{
534
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
535
				$this->error.=($this->error?', '.$errmsg:$errmsg);
536
			}
537
			$this->db->rollback();
538
			return -1*$error;
539
		}
540
		else
541
		{
542
			//Delete associated link file
543
			if ($conf->projet->dir_output)
544
			{
545
				$projectstatic=new Project($this->db);
546
				$projectstatic->fetch($this->fk_project);
547
548
				$dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($projectstatic->ref) . '/' . dol_sanitizeFileName($this->id);
549
				dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
550
				if (file_exists($dir))
551
				{
552
					require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
553
					$res = @dol_delete_dir_recursive($dir);
554
					if (!$res)
555
					{
556
						$this->error = 'ErrorFailToDeleteDir';
557
						$this->db->rollback();
558
						return 0;
559
					}
560
				}
561
			}
562
563
			$this->db->commit();
564
565
			return 1;
566
		}
567
	}
568
569
	/**
570
	 *	Return nb of children
571
	 *
572
	 *	@return	int		<0 if KO, 0 if no children, >0 if OK
573
	 */
574
	public function hasChildren()
575
	{
576
		$error=0;
577
		$ret=0;
578
579
		$sql = "SELECT COUNT(*) as nb";
580
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task";
581
		$sql.= " WHERE fk_task_parent=".$this->id;
582
583
		dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
584
		$resql = $this->db->query($sql);
585
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
586
		else
587
		{
588
			$obj=$this->db->fetch_object($resql);
589
			if ($obj) $ret=$obj->nb;
590
			$this->db->free($resql);
591
		}
592
593
		if (! $error)
594
		{
595
			return $ret;
596
		}
597
		else
598
		{
599
			return -1;
600
		}
601
	}
602
603
	/**
604
	 *	Return nb of time spent
605
	 *
606
	 *	@return	int		<0 if KO, 0 if no children, >0 if OK
607
	 */
608
	public function hasTimeSpent()
609
	{
610
		$error=0;
611
		$ret=0;
612
613
		$sql = "SELECT COUNT(*) as nb";
614
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time";
615
		$sql.= " WHERE fk_task=".$this->id;
616
617
		dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
618
		$resql = $this->db->query($sql);
619
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
620
		else
621
		{
622
			$obj=$this->db->fetch_object($resql);
623
			if ($obj) $ret=$obj->nb;
624
			$this->db->free($resql);
625
		}
626
627
		if (! $error)
628
		{
629
			return $ret;
630
		}
631
		else
632
		{
633
			return -1;
634
		}
635
	}
636
637
638
	/**
639
	 *	Return clicable name (with picto eventually)
640
	 *
641
	 *	@param	int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
642
	 *	@param	string	$option			'withproject' or ''
643
	 *  @param	string	$mode			Mode 'task', 'time', 'contact', 'note', document' define page to link to.
644
	 * 	@param	int		$addlabel		0=Default, 1=Add label into string, >1=Add first chars into string
645
	 *  @param	string	$sep			Separator between ref and label if option addlabel is set
646
	 *  @param	int   	$notooltip		1=Disable tooltip
647
	 *  @param  int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
648
	 *	@return	string					Chaine avec URL
649
	 */
650
	public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
651
	{
652
		global $conf, $langs, $user;
653
654
		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
655
656
		$result='';
657
		$label = '<u>' . $langs->trans("ShowTask") . '</u>';
658
		if (! empty($this->ref))
659
			$label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
660
		if (! empty($this->label))
661
			$label .= '<br><b>' . $langs->trans('LabelTask') . ':</b> ' . $this->label;
662
		if ($this->date_start || $this->date_end)
663
		{
664
			$label .= "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
665
		}
666
667
		$url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option=='withproject'?'&withproject=1':'');
668
		// Add param to save lastsearch_values or not
669
		$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
670
		if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
671
		if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
672
673
		$linkclose = '';
674
		if (empty($notooltip))
675
		{
676
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
677
			{
678
				$label=$langs->trans("ShowTask");
679
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
680
			}
681
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
682
			$linkclose.=' class="classfortooltip"';
683
		}
684
685
		$linkstart = '<a href="'.$url.'"';
686
		$linkstart.=$linkclose.'>';
687
		$linkend='</a>';
688
689
		$picto='projecttask';
690
691
		$result .= $linkstart;
692
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
693
		if ($withpicto != 2) $result.= $this->ref;
694
		$result .= $linkend;
695
		if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
696
697
		return $result;
698
	}
699
700
	/**
701
	 *  Initialise an instance with random values.
702
	 *  Used to build previews or test instances.
703
	 *	id must be 0 if object instance is a specimen.
704
	 *
705
	 *  @return	void
706
	 */
707
	public function initAsSpecimen()
708
	{
709
		$this->id=0;
710
711
		$this->fk_project='';
1 ignored issue
show
Documentation Bug introduced by
The property $fk_project was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
712
		$this->ref='TK01';
713
		$this->fk_task_parent=null;
714
		$this->label='Specimen task TK01';
715
		$this->duration_effective='';
716
		$this->fk_user_creat=null;
717
		$this->progress='25';
718
		$this->fk_statut=null;
719
		$this->note='This is a specimen task not';
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$note 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

719
		/** @scrutinizer ignore-deprecated */ $this->note='This is a specimen task not';
Loading history...
720
	}
721
722
	/**
723
	 * Return list of tasks for all projects or for one particular project
724
	 * Sort order is on project, then on position of task, and last on start date of first level task
725
	 *
726
	 * @param	User	$usert				Object user to limit tasks affected to a particular user
727
	 * @param	User	$userp				Object user to limit projects of a particular user and public projects
728
	 * @param	int		$projectid			Project id
729
	 * @param	int		$socid				Third party id
730
	 * @param	int		$mode				0=Return list of tasks and their projects, 1=Return projects and tasks if exists
731
	 * @param	string	$filteronproj    	Filter on project ref or label
732
	 * @param	string	$filteronprojstatus	Filter on project status ('-1'=no filter, '0,1'=Draft+Validated only)
733
	 * @param	string	$morewherefilter	Add more filter into where SQL request (must start with ' AND ...')
734
	 * @param	string	$filteronprojuser	Filter on user that is a contact of project
735
	 * @param	string	$filterontaskuser	Filter on user assigned to task
736
	 * @param	array	$extrafields	    Show additional column from project or task
737
	 * @param   int     $includebilltime    Calculate also the time to bill and billed
738
	 * @return 	array						Array of tasks
739
	 */
740
	public function getTasksArray($usert = null, $userp = null, $projectid = 0, $socid = 0, $mode = 0, $filteronproj = '', $filteronprojstatus = '-1', $morewherefilter = '', $filteronprojuser = 0, $filterontaskuser = 0, $extrafields = array(), $includebilltime = 0)
741
	{
742
		global $conf;
743
744
		$tasks = array();
745
746
		//print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
747
748
		// List of tasks (does not care about permissions. Filtering will be done later)
749
		$sql = "SELECT ";
750
		if ($filteronprojuser > 0 || $filterontaskuser > 0) $sql.= " DISTINCT";		// We may get several time the same record if user has several roles on same project/task
751
		$sql.= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.bill_time,";
752
		$sql.= " t.rowid as taskid, t.ref as taskref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut as status,";
753
		$sql.= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang,";
754
		$sql.= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
755
    $sql.= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
756
    if (!empty($extrafields->attributes['projet']['label']))
757
    {
758
        foreach ($extrafields->attributes['projet']['label'] as $key => $val) $sql.=($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key.' as options_'.$key : '');
759
    }
760
    if (!empty($extrafields->attributes['projet_task']['label']))
761
    {
762
        foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) $sql.=($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key.' as options_'.$key : '');
763
    }
764
		if ($includebilltime)
765
		{
766
		    $sql.=", SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed";
767
		}
768
769
    $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
770
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
771
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
772
773
		if ($mode == 0)
774
		{
775
			if ($filteronprojuser > 0)
776
			{
777
				$sql.= ", ".MAIN_DB_PREFIX."element_contact as ec";
778
				$sql.= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
779
			}
780
			$sql.= ", ".MAIN_DB_PREFIX."projet_task as t";
781
			if ($includebilltime)
782
			{
783
                $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
784
			}
785
			if ($filterontaskuser > 0)
786
			{
787
				$sql.= ", ".MAIN_DB_PREFIX."element_contact as ec2";
788
				$sql.= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
789
			}
790
            $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
791
            $sql.= " WHERE p.entity IN (".getEntity('project').")";
792
			$sql.= " AND t.fk_projet = p.rowid";
793
		}
794
		elseif ($mode == 1)
795
		{
796
			if ($filteronprojuser > 0)
797
			{
798
				$sql.= ", ".MAIN_DB_PREFIX."element_contact as ec";
799
				$sql.= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
800
			}
801
			if ($filterontaskuser > 0)
802
			{
803
				$sql.= ", ".MAIN_DB_PREFIX."projet_task as t";
804
				if ($includebilltime)
805
				{
806
				    $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
807
				}
808
				$sql.= ", ".MAIN_DB_PREFIX."element_contact as ec2";
809
				$sql.= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
810
			}
811
			else
812
			{
813
				$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
814
				if ($includebilltime)
815
				{
816
				    $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
817
				}
818
			}
819
            $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
820
            $sql.= " WHERE p.entity IN (".getEntity('project').")";
821
		}
822
		else return 'BadValueForParameterMode';
823
824
		if ($filteronprojuser > 0)
825
		{
826
			$sql.= " AND p.rowid = ec.element_id";
827
			$sql.= " AND ctc.rowid = ec.fk_c_type_contact";
828
			$sql.= " AND ctc.element = 'project'";
829
			$sql.= " AND ec.fk_socpeople = ".$filteronprojuser;
830
			$sql.= " AND ec.statut = 4";
831
			$sql.= " AND ctc.source = 'internal'";
832
		}
833
		if ($filterontaskuser > 0)
834
		{
835
			$sql.= " AND t.fk_projet = p.rowid";
836
			$sql.= " AND p.rowid = ec2.element_id";
837
			$sql.= " AND ctc2.rowid = ec2.fk_c_type_contact";
838
			$sql.= " AND ctc2.element = 'project_task'";
839
			$sql.= " AND ec2.fk_socpeople = ".$filterontaskuser;
840
			$sql.= " AND ec2.statut = 4";
841
			$sql.= " AND ctc2.source = 'internal'";
842
		}
843
		if ($socid)	$sql.= " AND p.fk_soc = ".$socid;
844
		if ($projectid) $sql.= " AND p.rowid in (".$projectid.")";
845
		if ($filteronproj) $sql.= natural_search(array("p.ref", "p.title"), $filteronproj);
846
		if ($filteronprojstatus && $filteronprojstatus != '-1') $sql.= " AND p.fk_statut IN (".$filteronprojstatus.")";
847
		if ($morewherefilter) $sql.=$morewherefilter;
848
		if ($includebilltime)
849
		{
850
    		$sql.=" GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.bill_time,";
851
    		$sql.=" t.datec, t.dateo, t.datee, t.tms,";
852
    		$sql.=" t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
853
    		$sql.=" t.dateo, t.datee, t.planned_workload, t.rang,";
854
    		$sql.=" s.rowid, s.nom, s.email,";
855
        $sql.=" p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
856
        if (!empty($extrafields->attributes['projet']['label']))
857
        {
858
            foreach ($extrafields->attributes['projet']['label'] as $key => $val) $sql.=($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
859
        }
860
        if (!empty($extrafields->attributes['projet_task']['label']))
861
        {
862
            foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) $sql.=($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
863
        }
864
		}
865
866
		$sql.= " ORDER BY p.ref, t.rang, t.dateo";
867
868
		//print $sql;exit;
869
		dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
870
		$resql = $this->db->query($sql);
871
		if ($resql)
872
		{
873
			$num = $this->db->num_rows($resql);
874
			$i = 0;
875
			// Loop on each record found, so each couple (project id, task id)
876
			while ($i < $num)
877
			{
878
				$error=0;
879
880
				$obj = $this->db->fetch_object($resql);
881
882
				if ((! $obj->public) && (is_object($userp)))	// If not public project and we ask a filter on project owned by a user
883
				{
884
					if (! $this->getUserRolesForProjectsOrTasks($userp, 0, $obj->projectid, 0))
885
					{
886
						$error++;
887
					}
888
				}
889
				if (is_object($usert))							// If we ask a filter on a user affected to a task
890
				{
891
					if (! $this->getUserRolesForProjectsOrTasks(0, $usert, $obj->projectid, $obj->taskid))
892
					{
893
						$error++;
894
					}
895
				}
896
897
				if (! $error)
898
				{
899
					$tasks[$i] = new Task($this->db);
900
					$tasks[$i]->id				= $obj->taskid;
901
					$tasks[$i]->ref				= $obj->taskref;
902
					$tasks[$i]->fk_project		= $obj->projectid;
903
					$tasks[$i]->projectref		= $obj->ref;
904
					$tasks[$i]->projectlabel	= $obj->plabel;
905
					$tasks[$i]->projectstatus	= $obj->projectstatus;
906
					$tasks[$i]->bill_time	   	= $obj->bill_time;
907
					$tasks[$i]->label			= $obj->label;
908
					$tasks[$i]->description		= $obj->description;
909
					$tasks[$i]->fk_parent		= $obj->fk_task_parent;      // deprecated
910
					$tasks[$i]->fk_task_parent	= $obj->fk_task_parent;
911
					$tasks[$i]->duration		= $obj->duration_effective;
912
					$tasks[$i]->planned_workload= $obj->planned_workload;
913
914
					$tasks[$i]->tobill  		= $obj->tobill;
915
					$tasks[$i]->billed	    	= $obj->billed;
916
917
					$tasks[$i]->progress		= $obj->progress;
918
					$tasks[$i]->fk_statut		= $obj->status;
919
					$tasks[$i]->public			= $obj->public;
920
					$tasks[$i]->date_start		= $this->db->jdate($obj->date_start);
921
					$tasks[$i]->date_end		= $this->db->jdate($obj->date_end);
922
					$tasks[$i]->rang	   		= $obj->rang;
923
924
					$tasks[$i]->socid           = $obj->thirdparty_id;	// For backward compatibility
925
					$tasks[$i]->thirdparty_id	= $obj->thirdparty_id;
926
					$tasks[$i]->thirdparty_name	= $obj->thirdparty_name;
927
					$tasks[$i]->thirdparty_email= $obj->thirdparty_email;
928
929
930
                    $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
931
                    $tasks[$i]->opp_amount = $obj->opp_amount;
932
                    $tasks[$i]->opp_percent = $obj->opp_percent;
933
                    $tasks[$i]->budget_amount = $obj->budget_amount;
934
                    $tasks[$i]->bill_time = $obj->bill_time;
935
936
                    if (!empty($extrafields->attributes['projet']['label']))
937
                    {
938
                        foreach ($extrafields->attributes['projet']['label'] as $key => $val)
939
                        {
940
                            if ($extrafields->attributes['projet']['type'][$key] != 'separate')
941
                                $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
942
                        }
943
                    }
944
945
                    if (!empty($extrafields->attributes['projet_task']['label']))
946
                    {
947
                        foreach ($extrafields->attributes['projet_task']['label'] as $key => $val)
948
                        {
949
                            if ($extrafields->attributes['projet_task']['type'][$key] != 'separate')
950
                                $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
951
                        }
952
                    }
953
				}
954
955
				$i++;
956
			}
957
			$this->db->free($resql);
958
		}
959
		else
960
		{
961
			dol_print_error($this->db);
962
		}
963
964
		return $tasks;
965
	}
966
967
	/**
968
	 * Return list of roles for a user for each projects or each tasks (or a particular project or a particular task).
969
	 *
970
	 * @param	User	$userp			      Return roles on project for this internal user. If set, usert and taskid must not be defined.
971
	 * @param	User	$usert			      Return roles on task for this internal user. If set userp must NOT be defined. -1 means no filter.
972
	 * @param 	int		$projectid		      Project id list separated with , to filter on project
973
	 * @param 	int		$taskid			      Task id to filter on a task
974
	 * @param	integer	$filteronprojstatus	  Filter on project status if userp is set. Not used if userp not defined.
975
	 * @return 	array					      Array (projectid => 'list of roles for project' or taskid => 'list of roles for task')
976
	 */
977
	public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
978
	{
979
		$arrayroles = array();
980
981
		dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".is_object($userp)." usert=".is_object($usert)." projectid=".$projectid." taskid=".$taskid);
982
983
		// We want role of user for a projet or role of user for a task. Both are not possible.
984
		if (empty($userp) && empty($usert))
985
		{
986
			$this->error="CallWithWrongParameters";
987
			return -1;
988
		}
989
		if (! empty($userp) && ! empty($usert))
990
		{
991
			$this->error="CallWithWrongParameters";
992
			return -1;
993
		}
994
995
		/* Liste des taches et role sur les projets ou taches */
996
		$sql = "SELECT pt.rowid as pid, ec.element_id, ctc.code, ctc.source";
997
		if ($userp) $sql.= " FROM ".MAIN_DB_PREFIX."projet as pt";
998
		if ($usert && $filteronprojstatus > -1) $sql.= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
999
		if ($usert && $filteronprojstatus <= -1) $sql.= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1000
		$sql.= ", ".MAIN_DB_PREFIX."element_contact as ec";
1001
		$sql.= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1002
		$sql.= " WHERE pt.rowid = ec.element_id";
1003
		if ($userp && $filteronprojstatus > -1) $sql.= " AND pt.fk_statut = ".$filteronprojstatus;
1004
		if ($usert && $filteronprojstatus > -1) $sql.= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".$filteronprojstatus;
1005
		if ($userp) $sql.= " AND ctc.element = 'project'";
1006
		if ($usert) $sql.= " AND ctc.element = 'project_task'";
1007
		$sql.= " AND ctc.rowid = ec.fk_c_type_contact";
1008
		if ($userp) $sql.= " AND ec.fk_socpeople = ".$userp->id;
1009
		if ($usert) $sql.= " AND ec.fk_socpeople = ".$usert->id;
1010
		$sql.= " AND ec.statut = 4";
1011
		$sql.= " AND ctc.source = 'internal'";
1012
		if ($projectid)
1013
		{
1014
			if ($userp) $sql.= " AND pt.rowid in (".$projectid.")";
1015
			if ($usert) $sql.= " AND pt.fk_projet in (".$projectid.")";
1016
		}
1017
		if ($taskid)
1018
		{
1019
			if ($userp) $sql.= " ERROR SHOULD NOT HAPPENS";
1020
			if ($usert) $sql.= " AND pt.rowid = ".$taskid;
1021
		}
1022
		//print $sql;
1023
1024
		dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1025
		$resql = $this->db->query($sql);
1026
		if ($resql)
1027
		{
1028
			$num = $this->db->num_rows($resql);
1029
			$i = 0;
1030
			while ($i < $num)
1031
			{
1032
				$obj = $this->db->fetch_object($resql);
1033
				if (empty($arrayroles[$obj->pid])) $arrayroles[$obj->pid] = $obj->code;
1034
				else $arrayroles[$obj->pid].=','.$obj->code;
1035
				$i++;
1036
			}
1037
			$this->db->free($resql);
1038
		}
1039
		else
1040
		{
1041
			dol_print_error($this->db);
1042
		}
1043
1044
		return $arrayroles;
1045
	}
1046
1047
1048
	/**
1049
	 * 	Return list of id of contacts of task
1050
	 *
1051
	 *	@param	string	$source		Source
1052
	 *  @return array				Array of id of contacts
1053
	 */
1054
	public function getListContactId($source = 'internal')
1055
	{
1056
		$contactAlreadySelected = array();
1057
		$tab = $this->liste_contact(-1, $source);
1058
		//var_dump($tab);
1059
		$num=count($tab);
1060
		$i = 0;
1061
		while ($i < $num)
1062
		{
1063
			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1064
			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1065
			$i++;
1066
		}
1067
		return $contactAlreadySelected;
1068
	}
1069
1070
1071
	/**
1072
	 *  Add time spent
1073
	 *
1074
	 *  @param	User	$user           User object
1075
	 *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
1076
	 *  @return	int                     <=0 if KO, >0 if OK
1077
	 */
1078
	public function addTimeSpent($user, $notrigger = 0)
1079
	{
1080
		global $conf,$langs;
1081
1082
		dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1083
1084
		$ret = 0;
1085
1086
		// Check parameters
1087
		if (! is_object($user))
1088
		{
1089
			dol_print_error('', "Method addTimeSpent was called with wrong parameter user");
1090
			return -1;
1091
		}
1092
1093
		// Clean parameters
1094
		if (isset($this->timespent_note)) $this->timespent_note = trim($this->timespent_note);
1095
		if (empty($this->timespent_datehour)) $this->timespent_datehour = $this->timespent_date;
1096
1097
		$this->db->begin();
1098
1099
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task_time (";
1100
		$sql.= "fk_task";
1101
		$sql.= ", task_date";
1102
		$sql.= ", task_datehour";
1103
		$sql.= ", task_date_withhour";
1104
		$sql.= ", task_duration";
1105
		$sql.= ", fk_user";
1106
		$sql.= ", note";
1107
		$sql.= ") VALUES (";
1108
		$sql.= $this->id;
1109
		$sql.= ", '".$this->db->idate($this->timespent_date)."'";
1110
		$sql.= ", '".$this->db->idate($this->timespent_datehour)."'";
1111
		$sql.= ", ".(empty($this->timespent_withhour)?0:1);
1112
		$sql.= ", ".$this->timespent_duration;
1113
		$sql.= ", ".$this->timespent_fk_user;
1114
		$sql.= ", ".(isset($this->timespent_note)?"'".$this->db->escape($this->timespent_note)."'":"null");
1115
		$sql.= ")";
1116
1117
		$resql=$this->db->query($sql);
1118
		if ($resql)
1119
		{
1120
			$tasktime_id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task_time");
1121
			$ret = $tasktime_id;
1122
			$this->timespent_id = $ret;
1123
1124
			if (! $notrigger)
1125
			{
1126
				// Call trigger
1127
				$result=$this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1128
				if ($result < 0) { $ret=-1; }
1129
				// End call triggers
1130
			}
1131
		}
1132
		else
1133
		{
1134
			$this->error=$this->db->lasterror();
1135
			$ret = -1;
1136
		}
1137
1138
		if ($ret > 0)
1139
		{
1140
			// Recalculate amount of time spent for task and update denormalized field
1141
			$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1142
			$sql.= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".$this->id.")";
1143
			if (isset($this->progress)) $sql.= ", progress = " . $this->progress;	// Do not overwrite value if not provided
1144
			$sql.= " WHERE rowid = ".$this->id;
1145
1146
			dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1147
			if (! $this->db->query($sql) )
1148
			{
1149
				$this->error=$this->db->lasterror();
1150
				$ret = -2;
1151
			}
1152
1153
			$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
1154
			$sql.= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".$this->timespent_fk_user.")";	// set average hour rate of user
1155
			$sql.= " WHERE rowid = ".$tasktime_id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tasktime_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
1156
1157
			dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1158
			if (! $this->db->query($sql) )
1159
			{
1160
				$this->error=$this->db->lasterror();
1161
				$ret = -2;
1162
			}
1163
		}
1164
1165
		if ($ret >0)
1166
		{
1167
			$this->db->commit();
1168
		}
1169
		else
1170
		{
1171
			$this->db->rollback();
1172
		}
1173
		return $ret;
1174
	}
1175
1176
	/**
1177
	 *  Calculate total of time spent for task
1178
	 *
1179
	 *  @param  User|int	$userobj			Filter on user. null or 0=No filter
1180
	 *  @param	string		$morewherefilter	Add more filter into where SQL request (must start with ' AND ...')
1181
	 *  @return array		 					Array of info for task array('min_date', 'max_date', 'total_duration', 'total_amount', 'nblines', 'nblinesnull')
1182
	 */
1183
	public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1184
	{
1185
		global $langs;
1186
1187
		if (is_object($userobj)) $userid=$userobj->id;
1188
		else $userid=$userobj;	// old method
1189
1190
		$id=$this->id;
1191
		if (empty($id) && empty($userid))
1192
		{
1193
			dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1194
			return -1;
1195
		}
1196
1197
		$result=array();
1198
1199
		$sql = "SELECT";
1200
		$sql.= " MIN(t.task_datehour) as min_date,";
1201
		$sql.= " MAX(t.task_datehour) as max_date,";
1202
		$sql.= " SUM(t.task_duration) as total_duration,";
1203
		$sql.= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1204
		$sql.= " COUNT(t.rowid) as nblines,";
1205
		$sql.= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1206
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1207
		$sql.= " WHERE 1 = 1";
1208
		if ($morewherefilter) $sql.=$morewherefilter;
1209
		if ($id > 0) $sql.= " AND t.fk_task = ".$id;
1210
		if ($userid > 0) $sql.=" AND t.fk_user = ".$userid;
1 ignored issue
show
Bug introduced by
Are you sure $userid of type User|integer|null can be used in concatenation? ( Ignorable by Annotation )

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

1210
		if ($userid > 0) $sql.=" AND t.fk_user = "./** @scrutinizer ignore-type */ $userid;
Loading history...
1211
1212
		dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1213
		$resql=$this->db->query($sql);
1214
		if ($resql)
1215
		{
1216
			$obj = $this->db->fetch_object($resql);
1217
1218
			$result['min_date'] = $obj->min_date;               // deprecated. use the ->timespent_xxx instead
1219
			$result['max_date'] = $obj->max_date;               // deprecated. use the ->timespent_xxx instead
1220
			$result['total_duration'] = $obj->total_duration;   // deprecated. use the ->timespent_xxx instead
1221
1222
			$this->timespent_min_date=$this->db->jdate($obj->min_date);
1223
			$this->timespent_max_date=$this->db->jdate($obj->max_date);
1224
			$this->timespent_total_duration=$obj->total_duration;
1225
			$this->timespent_total_amount=$obj->total_amount;
1226
			$this->timespent_nblinesnull=($obj->nblinesnull?$obj->nblinesnull:0);
1227
			$this->timespent_nblines=($obj->nblines?$obj->nblines:0);
1228
1229
			$this->db->free($resql);
1230
		}
1231
		else
1232
		{
1233
			dol_print_error($this->db);
1234
		}
1235
		return $result;
1236
	}
1237
1238
	/**
1239
	 *  Calculate quantity and value of time consumed using the thm (hourly amount value of work for user entering time)
1240
	 *
1241
	 *	@param		User		$fuser		Filter on a dedicated user
1242
	 *  @param		string		$dates		Start date (ex 00:00:00)
1243
	 *  @param		string		$datee		End date (ex 23:59:59)
1244
	 *  @return 	array	        		Array of info for task array('amount','nbseconds','nblinesnull')
1245
	 */
1246
	public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1247
	{
1248
		global $langs;
1249
1250
		if (empty($id)) $id=$this->id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id seems to never exist and therefore empty should always be true.
Loading history...
1251
1252
		$result=array();
1253
1254
		$sql = "SELECT";
1255
		$sql.= " SUM(t.task_duration) as nbseconds,";
1256
		$sql.= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as amount, SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1257
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1258
		$sql.= " WHERE t.fk_task = ".$id;
1259
		if (is_object($fuser) && $fuser->id > 0)
1260
		{
1261
			$sql.=" AND fk_user = ".$fuser->id;
1262
		}
1263
		if ($dates > 0)
1264
		{
1265
			$datefieldname="task_datehour";
1266
			$sql.=" AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1267
		}
1268
		if ($datee > 0)
1269
		{
1270
			$datefieldname="task_datehour";
1271
			$sql.=" AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1272
		}
1273
		//print $sql;
1274
1275
		dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1276
		$resql=$this->db->query($sql);
1277
		if ($resql)
1278
		{
1279
			$obj = $this->db->fetch_object($resql);
1280
1281
			$result['amount'] = $obj->amount;
1282
			$result['nbseconds'] = $obj->nbseconds;
1283
			$result['nblinesnull'] = $obj->nblinesnull;
1284
1285
			$this->db->free($resql);
1286
			return $result;
1287
		}
1288
		else
1289
		{
1290
			dol_print_error($this->db);
1291
			return $result;
1292
		}
1293
	}
1294
1295
	/**
1296
	 *  Load one record of time spent
1297
	 *
1298
	 *  @param	int		$id 	Id object
1299
	 *  @return int		        <0 if KO, >0 if OK
1300
	 */
1301
	public function fetchTimeSpent($id)
1302
	{
1303
		global $langs;
1304
1305
		$sql = "SELECT";
1306
		$sql.= " t.rowid,";
1307
		$sql.= " t.fk_task,";
1308
		$sql.= " t.task_date,";
1309
		$sql.= " t.task_datehour,";
1310
		$sql.= " t.task_date_withhour,";
1311
		$sql.= " t.task_duration,";
1312
		$sql.= " t.fk_user,";
1313
		$sql.= " t.thm,";
1314
		$sql.= " t.note";
1315
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1316
		$sql.= " WHERE t.rowid = ".$id;
1317
1318
		dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1319
		$resql=$this->db->query($sql);
1320
		if ($resql)
1321
		{
1322
			if ($this->db->num_rows($resql))
1323
			{
1324
				$obj = $this->db->fetch_object($resql);
1325
1326
				$this->timespent_id			= $obj->rowid;
1327
				$this->id					= $obj->fk_task;
1328
				$this->timespent_date		= $this->db->jdate($obj->task_date);
1329
				$this->timespent_datehour   = $this->db->jdate($obj->task_datehour);
1330
				$this->timespent_withhour   = $obj->task_date_withhour;
1331
				$this->timespent_duration	= $obj->task_duration;
1332
				$this->timespent_fk_user	= $obj->fk_user;
1333
				$this->timespent_thm    	= $obj->thm;       // hourly rate
1334
				$this->timespent_note		= $obj->note;
1335
			}
1336
1337
			$this->db->free($resql);
1338
1339
			return 1;
1340
		}
1341
		else
1342
		{
1343
			$this->error="Error ".$this->db->lasterror();
1344
			return -1;
1345
		}
1346
	}
1347
1348
	/**
1349
	 *  Load all records of time spent
1350
	 *
1351
	 *  @param	User	$userobj			User object
1352
	 *  @param	string	$morewherefilter	Add more filter into where SQL request (must start with ' AND ...')
1353
	 *  @return int							<0 if KO, array of time spent if OK
1354
	 */
1355
	public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1356
	{
1357
		global $langs;
1358
1359
		$arrayres=array();
1360
1361
		$sql = "SELECT";
1362
		$sql.= " s.rowid as socid,";
1363
		$sql.= " s.nom as thirdparty_name,";
1364
		$sql.= " s.email as thirdparty_email,";
1365
		$sql.= " ptt.rowid,";
1366
		$sql.= " ptt.fk_task,";
1367
		$sql.= " ptt.task_date,";
1368
		$sql.= " ptt.task_datehour,";
1369
		$sql.= " ptt.task_date_withhour,";
1370
		$sql.= " ptt.task_duration,";
1371
		$sql.= " ptt.fk_user,";
1372
		$sql.= " ptt.note,";
1373
		$sql.= " pt.rowid as task_id,";
1374
		$sql.= " pt.ref as task_ref,";
1375
		$sql.= " pt.label as task_label,";
1376
		$sql.= " p.rowid as project_id,";
1377
		$sql.= " p.ref as project_ref,";
1378
		$sql.= " p.title as project_label,";
1379
		$sql.= " p.public as public";
1380
		$sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1381
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1382
		$sql.= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
1383
		$sql.= " AND ptt.fk_user = ".$userobj->id;
1384
		$sql.= " AND pt.entity IN (".getEntity('project').")";
1385
		if ($morewherefilter) $sql.=$morewherefilter;
1386
1387
		dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1388
		$resql=$this->db->query($sql);
1389
		if ($resql)
1390
		{
1391
			$num = $this->db->num_rows($resql);
1392
1393
			$i=0;
1394
			while ($i < $num)
1395
			{
1396
				$obj = $this->db->fetch_object($resql);
1397
1398
				$newobj = new stdClass();
1399
1400
				$newobj->socid              = $obj->socid;
1401
				$newobj->thirdparty_name    = $obj->thirdparty_name;
1402
				$newobj->thirdparty_email   = $obj->thirdparty_email;
1403
1404
				$newobj->fk_project			= $obj->project_id;
1405
				$newobj->project_ref		= $obj->project_ref;
1406
				$newobj->project_label		= $obj->project_label;
1407
				$newobj->public				= $obj->project_public;
1408
1409
				$newobj->fk_task			= $obj->task_id;
1410
				$newobj->task_ref			= $obj->task_ref;
1411
				$newobj->task_label			= $obj->task_label;
1412
1413
				$newobj->timespent_id		= $obj->rowid;
1414
				$newobj->timespent_date		= $this->db->jdate($obj->task_date);
1415
				$newobj->timespent_datehour	= $this->db->jdate($obj->task_datehour);
1416
				$newobj->timespent_withhour = $obj->task_date_withhour;
1417
				$newobj->timespent_duration = $obj->task_duration;
1418
				$newobj->timespent_fk_user	= $obj->fk_user;
1419
				$newobj->timespent_note		= $obj->note;
1420
1421
				$arrayres[] = $newobj;
1422
1423
				$i++;
1424
			}
1425
1426
			$this->db->free($resql);
1427
		}
1428
		else
1429
		{
1430
			dol_print_error($this->db);
1431
			$this->error="Error ".$this->db->lasterror();
1432
			return -1;
1433
		}
1434
1435
		return $arrayres;
1436
	}
1437
1438
	/**
1439
	 *	Update time spent
1440
	 *
1441
	 *  @param	User	$user           User id
1442
	 *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
1443
	 *  @return	int						<0 if KO, >0 if OK
1444
	 */
1445
	public function updateTimeSpent($user, $notrigger = 0)
1446
	{
1447
		global $conf,$langs;
1448
1449
		$ret = 0;
1450
1451
		// Check parameters
1452
		if ($this->timespent_date == '')
1453
		{
1454
		    $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1455
		    return -1;
1456
		}
1457
		if (! ($this->timespent_fk_user > 0))
1458
		{
1459
		    $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1460
		    return -1;
1461
		}
1462
1463
		// Clean parameters
1464
		if (empty($this->timespent_datehour)) $this->timespent_datehour = $this->timespent_date;
1465
		if (isset($this->timespent_note)) $this->timespent_note = trim($this->timespent_note);
1466
1467
		$this->db->begin();
1468
1469
		$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time SET";
1470
		$sql.= " task_date = '".$this->db->idate($this->timespent_date)."',";
1471
		$sql.= " task_datehour = '".$this->db->idate($this->timespent_datehour)."',";
1472
		$sql.= " task_date_withhour = ".(empty($this->timespent_withhour)?0:1).",";
1473
		$sql.= " task_duration = ".$this->timespent_duration.",";
1474
		$sql.= " fk_user = ".$this->timespent_fk_user.",";
1475
		$sql.= " note = ".(isset($this->timespent_note)?"'".$this->db->escape($this->timespent_note)."'":"null");
1476
		$sql.= " WHERE rowid = ".$this->timespent_id;
1477
1478
		dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1479
		if ($this->db->query($sql) )
1480
		{
1481
			if (! $notrigger)
1482
			{
1483
				// Call trigger
1484
				$result=$this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1485
				if ($result < 0)
1486
				{
1487
					$this->db->rollback();
1488
					$ret = -1;
1489
				}
1490
				else $ret = 1;
1491
				// End call triggers
1492
			}
1493
			else $ret = 1;
1494
		}
1495
		else
1496
		{
1497
			$this->error=$this->db->lasterror();
1498
			$this->db->rollback();
1499
			$ret = -1;
1500
		}
1501
1502
		if ($ret == 1 && ($this->timespent_old_duration != $this->timespent_duration))
1503
		{
1504
			$newDuration = $this->timespent_duration - $this->timespent_old_duration;
1505
1506
			$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1507
			$sql.= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".$this->db->escape($this->id).")";
1508
			$sql.= " WHERE rowid = ".$this->id;
1509
1510
			dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1511
			if (! $this->db->query($sql) )
1512
			{
1513
				$this->error=$this->db->lasterror();
1514
				$this->db->rollback();
1515
				$ret = -2;
1516
			}
1517
		}
1518
1519
		if ($ret >= 0) $this->db->commit();
1520
		return $ret;
1521
	}
1522
1523
	/**
1524
	 *  Delete time spent
1525
	 *
1526
	 *  @param	User	$user        	User that delete
1527
	 *  @param  int		$notrigger	    0=launch triggers after, 1=disable triggers
1528
	 *  @return	int						<0 if KO, >0 if OK
1529
	 */
1530
	public function delTimeSpent($user, $notrigger = 0)
1531
	{
1532
		global $conf, $langs;
1533
1534
		$error=0;
1535
1536
		$this->db->begin();
1537
1538
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
1539
		$sql.= " WHERE rowid = ".$this->timespent_id;
1540
1541
		dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1542
		$resql = $this->db->query($sql);
1543
		if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
1544
1545
		if (! $error)
1546
		{
1547
			if (! $notrigger)
1548
			{
1549
				// Call trigger
1550
				$result=$this->call_trigger('TASK_TIMESPENT_DELETE', $user);
1551
				if ($result < 0) { $error++; }
1552
				// End call triggers
1553
			}
1554
		}
1555
1556
		if (! $error)
1557
		{
1558
			$sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1559
			$sql.= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration?$this->timespent_duration:0);
1560
			$sql.= " WHERE rowid = ".$this->id;
1561
1562
			dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1563
			if ($this->db->query($sql) )
1564
			{
1565
				$result = 0;
1566
			}
1567
			else
1568
			{
1569
				$this->error=$this->db->lasterror();
1570
				$result = -2;
1571
			}
1572
		}
1573
1574
		// Commit or rollback
1575
		if ($error)
1576
		{
1577
			foreach($this->errors as $errmsg)
1578
			{
1579
				dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
1580
				$this->error.=($this->error?', '.$errmsg:$errmsg);
1581
			}
1582
			$this->db->rollback();
1583
			return -1*$error;
1584
		}
1585
		else
1586
		{
1587
			$this->db->commit();
1588
			return 1;
1589
		}
1590
	}
1591
1592
    /**	Load an object from its id and create a new one in database
1593
     *
1594
	 *  @param	User	$user		            User making the clone
1595
	 *  @param	int		$fromid     			Id of object to clone
1596
	 *  @param	int		$project_id				Id of project to attach clone task
1597
	 *  @param	int		$parent_task_id			Id of task to attach clone task
1598
	 *  @param	bool	$clone_change_dt		recalculate date of task regarding new project start date
1599
	 *  @param	bool	$clone_affectation		clone affectation of project
1600
	 *  @param	bool	$clone_time				clone time of project
1601
	 *  @param	bool	$clone_file				clone file of project
1602
	 *  @param	bool	$clone_note				clone note of project
1603
	 *  @param	bool	$clone_prog				clone progress of project
1604
	 *  @return	int								New id of clone
1605
     */
1606
	public function createFromClone(User $user, $fromid, $project_id, $parent_task_id, $clone_change_dt = false, $clone_affectation = false, $clone_time = false, $clone_file = false, $clone_note = false, $clone_prog = false)
1607
	{
1608
		global $langs,$conf;
1609
1610
		$error=0;
1611
1612
		//Use 00:00 of today if time is use on task.
1613
		$now=dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
1614
1615
		$datec = $now;
1616
1617
		$clone_task=new Task($this->db);
1618
		$origin_task=new Task($this->db);
1619
1620
		$clone_task->context['createfromclone']='createfromclone';
1621
1622
		$this->db->begin();
1623
1624
		// Load source object
1625
		$clone_task->fetch($fromid);
1626
		$clone_task->fetch_optionals();
1627
		//var_dump($clone_task->array_options);exit;
1628
1629
		$origin_task->fetch($fromid);
1630
1631
		$defaultref='';
1632
		$obj = empty($conf->global->PROJECT_TASK_ADDON)?'mod_task_simple':$conf->global->PROJECT_TASK_ADDON;
1633
		if (! empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT ."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.".php"))
1634
		{
1635
			require_once DOL_DOCUMENT_ROOT ."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.'.php';
1636
			$modTask = new $obj;
1637
			$defaultref = $modTask->getNextValue(0, $clone_task);
1638
		}
1639
1640
		$ori_project_id					= $clone_task->fk_project;
1641
1642
		$clone_task->id					= 0;
1643
		$clone_task->ref				= $defaultref;
1644
		$clone_task->fk_project			= $project_id;
1645
		$clone_task->fk_task_parent		= $parent_task_id;
1646
		$clone_task->date_c				= $datec;
1647
		$clone_task->planned_workload	= $origin_task->planned_workload;
1648
		$clone_task->rang				= $origin_task->rang;
1649
1650
		//Manage Task Date
1651
		if ($clone_change_dt)
1652
		{
1653
			$projectstatic=new Project($this->db);
1654
			$projectstatic->fetch($ori_project_id);
1655
1656
			//Origin project strat date
1657
			$orign_project_dt_start = $projectstatic->date_start;
1658
1659
			//Calcultate new task start date with difference between origin proj start date and origin task start date
1660
			if (!empty($clone_task->date_start))
1661
			{
1662
				$clone_task->date_start			= $now + $clone_task->date_start - $orign_project_dt_start;
1663
			}
1664
1665
			//Calcultate new task end date with difference between origin proj end date and origin task end date
1666
			if (!empty($clone_task->date_end))
1667
			{
1668
				$clone_task->date_end			= $now + $clone_task->date_end - $orign_project_dt_start;
1669
			}
1670
		}
1671
1672
		if (!$clone_prog)
1673
		{
1674
				$clone_task->progress=0;
1675
		}
1676
1677
		// Create clone
1678
		$result=$clone_task->create($user);
1679
1680
		// Other options
1681
		if ($result < 0)
1682
		{
1683
			$this->error=$clone_task->error;
1684
			$error++;
1685
		}
1686
1687
		// End
1688
		if (! $error)
1689
		{
1690
			$clone_task_id=$clone_task->id;
1691
			$clone_task_ref = $clone_task->ref;
1692
1693
	   		//Note Update
1694
			if (!$clone_note)
1695
	   		{
1696
				$clone_task->note_private='';
1697
				$clone_task->note_public='';
1698
			}
1699
			else
1700
			{
1701
				$this->db->begin();
1702
				$res=$clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES), '_public');
1703
				if ($res < 0)
1704
				{
1705
					$this->error.=$clone_task->error;
1706
					$error++;
1707
					$this->db->rollback();
1708
				}
1709
				else
1710
				{
1711
					$this->db->commit();
1712
				}
1713
1714
				$this->db->begin();
1715
				$res=$clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES), '_private');
1716
				if ($res < 0)
1717
				{
1718
					$this->error.=$clone_task->error;
1719
					$error++;
1720
					$this->db->rollback();
1721
				}
1722
				else
1723
				{
1724
					$this->db->commit();
1725
				}
1726
			}
1727
1728
			//Duplicate file
1729
			if ($clone_file)
1730
			{
1731
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1732
1733
				//retreive project origin ref to know folder to copy
1734
				$projectstatic=new Project($this->db);
1735
				$projectstatic->fetch($ori_project_id);
1736
				$ori_project_ref=$projectstatic->ref;
1737
1738
				if ($ori_project_id!=$project_id)
1739
				{
1740
					$projectstatic->fetch($project_id);
1741
					$clone_project_ref=$projectstatic->ref;
1742
				}
1743
				else
1744
				{
1745
					$clone_project_ref=$ori_project_ref;
1746
				}
1747
1748
				$clone_task_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($clone_project_ref). "/" . dol_sanitizeFileName($clone_task_ref);
1749
				$ori_task_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($ori_project_ref). "/" . dol_sanitizeFileName($fromid);
1750
1751
				$filearray=dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1752
				foreach($filearray as $key => $file)
1753
				{
1754
					if (!file_exists($clone_task_dir))
1755
					{
1756
						if (dol_mkdir($clone_task_dir) < 0)
1757
						{
1758
							$this->error.=$langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1759
							$error++;
1760
						}
1761
					}
1762
1763
					$rescopy = dol_copy($ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name'], 0, 1);
1764
					if (is_numeric($rescopy) && $rescopy < 0)
1765
					{
1766
						$this->error.=$langs->trans("ErrorFailToCopyFile", $ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name']);
1767
						$error++;
1768
					}
1769
				}
1770
			}
1771
1772
			// clone affectation
1773
			if ($clone_affectation)
1774
			{
1775
				$origin_task = new Task($this->db);
1776
				$origin_task->fetch($fromid);
1777
1778
				foreach(array('internal','external') as $source)
1779
				{
1780
					$tab = $origin_task->liste_contact(-1, $source);
1781
					$num=count($tab);
1782
					$i = 0;
1783
					while ($i < $num)
1784
					{
1785
						$clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
1786
						if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS')
1787
						{
1788
							$langs->load("errors");
1789
							$this->error.=$langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1790
							$error++;
1791
						}
1792
						else
1793
						{
1794
							if ($clone_task->error!='')
1795
							{
1796
								$this->error.=$clone_task->error;
1797
								$error++;
1798
							}
1799
						}
1800
						$i++;
1801
					}
1802
				}
1803
			}
1804
1805
			if($clone_time)
1806
			{
1807
				//TODO clone time of affectation
1808
			}
1809
		}
1810
1811
		unset($clone_task->context['createfromclone']);
1812
1813
		if (! $error)
1814
		{
1815
			$this->db->commit();
1816
			return $clone_task_id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $clone_task_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
1817
		}
1818
		else
1819
		{
1820
			$this->db->rollback();
1821
			dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : " . $this->error, LOG_ERR);
1822
			return -1;
1823
		}
1824
	}
1825
1826
1827
	/**
1828
	 *	Return status label of object
1829
	 *
1830
	 *	@param	integer	$mode		0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
1831
	 * 	@return	string	  			Label
1832
	 */
1833
	public function getLibStatut($mode = 0)
1834
	{
1835
		return $this->LibStatut($this->fk_statut, $mode);
1836
	}
1837
1838
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1839
	/**
1840
	 *  Return status label for an object
1841
	 *
1842
	 *  @param	int			$statut	  	Id statut
1843
	 *  @param	integer		$mode		0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
1844
	 *  @return	string	  				Label
1845
	 */
1846
	public function LibStatut($statut, $mode = 0)
1847
	{
1848
        // phpcs:enable
1849
		// list of Statut of the task
1850
		$this->statuts[0]='Draft';
1851
		$this->statuts[1]='ToDo';
1852
		$this->statuts[2]='Running';
1853
		$this->statuts[3]='Finish';
1854
		$this->statuts[4]='Transfered';
1855
		$this->statuts_short[0]='Draft';
1856
		$this->statuts_short[1]='ToDo';
1857
		$this->statuts_short[2]='Running';
1858
		$this->statuts_short[3]='Completed';
1859
		$this->statuts_short[4]='Transfered';
1860
1861
		global $langs;
1862
1863
		if ($mode == 0)
1864
		{
1865
			return $langs->trans($this->statuts[$statut]);
1866
		}
1867
		elseif ($mode == 1)
1868
		{
1869
			return $langs->trans($this->statuts_short[$statut]);
1870
		}
1871
		elseif ($mode == 2)
1872
		{
1873
			if ($statut==0) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0').' '.$langs->trans($this->statuts_short[$statut]);
1874
			elseif ($statut==1) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1').' '.$langs->trans($this->statuts_short[$statut]);
1875
			elseif ($statut==2) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3').' '.$langs->trans($this->statuts_short[$statut]);
1876
			elseif ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6').' '.$langs->trans($this->statuts_short[$statut]);
1877
			elseif ($statut==4) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6').' '.$langs->trans($this->statuts_short[$statut]);
1878
			elseif ($statut==5) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5').' '.$langs->trans($this->statuts_short[$statut]);
1879
		}
1880
		elseif ($mode == 3)
1881
		{
1882
			if ($statut==0) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0');
1883
			elseif ($statut==1) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1');
1884
			elseif ($statut==2) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3');
1885
			elseif ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6');
1886
			elseif ($statut==4) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6');
1887
			elseif ($statut==5) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5');
1888
		}
1889
		elseif ($mode == 4)
1890
		{
1891
			if ($statut==0) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0').' '.$langs->trans($this->statuts[$statut]);
1892
			elseif ($statut==1) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1').' '.$langs->trans($this->statuts[$statut]);
1893
			elseif ($statut==2) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3').' '.$langs->trans($this->statuts[$statut]);
1894
			elseif ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6').' '.$langs->trans($this->statuts[$statut]);
1895
			elseif ($statut==4) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6').' '.$langs->trans($this->statuts[$statut]);
1896
			elseif ($statut==5) return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5').' '.$langs->trans($this->statuts[$statut]);
1897
		}
1898
		elseif ($mode == 5)
1899
		{
1900
			/*if ($statut==0) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut0');
1901
			elseif ($statut==1) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut1');
1902
			elseif ($statut==2) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut3');
1903
			elseif ($statut==3) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6');
1904
			elseif ($statut==4) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6');
1905
			elseif ($statut==5) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut5');
1906
			*/
1907
			//else return $this->progress.' %';
1908
			return '&nbsp;';
1909
		}
1910
		elseif ($mode == 6)
1911
		{
1912
			/*if ($statut==0) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut0');
1913
			elseif ($statut==1) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut1');
1914
			elseif ($statut==2) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut3');
1915
			elseif ($statut==3) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6');
1916
			elseif ($statut==4) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6');
1917
			elseif ($statut==5) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut5');
1918
			*/
1919
			//else return $this->progress.' %';
1920
			return '&nbsp;';
1921
		}
1922
	}
1923
1924
	/**
1925
	 *  Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF
1926
	 *
1927
	 *  @param	string		$modele			force le modele a utiliser ('' par defaut)
1928
	 *  @param	Translate	$outputlangs	objet lang a utiliser pour traduction
1929
	 *  @param  int			$hidedetails    Hide details of lines
1930
	 *  @param  int			$hidedesc       Hide description
1931
	 *  @param  int			$hideref        Hide ref
1932
	 *  @return int         				0 if KO, 1 if OK
1933
	 */
1934
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1935
	{
1936
		global $conf,$langs;
1937
1938
		$langs->load("projects");
1939
1940
		if (! dol_strlen($modele)) {
1941
1942
			$modele = 'nodefault';
1943
1944
			if ($this->modelpdf) {
1945
				$modele = $this->modelpdf;
1946
			} elseif (! empty($conf->global->PROJECT_TASK_ADDON_PDF)) {
1947
				$modele = $conf->global->PROJECT_TASK_ADDON_PDF;
1948
			}
1949
		}
1950
1951
		$modelpath = "core/modules/project/task/doc/";
1952
1953
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1954
	}
1955
1956
1957
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1958
	/**
1959
	 * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
1960
	 *
1961
	 * @param	User	$user   Objet user
1962
	 * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
1963
	 */
1964
	public function load_board($user)
1965
	{
1966
        // phpcs:enable
1967
		global $conf, $langs;
1968
1969
		// For external user, no check is done on company because readability is managed by public status of project and assignement.
1970
		//$socid=$user->societe_id;
1971
1972
		$projectstatic = new Project($this->db);
1973
		$projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $socid seems to be never defined.
Loading history...
1974
1975
		// List of tasks (does not care about permissions. Filtering will be done later)
1976
		$sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
1977
		$sql.= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
1978
		$sql.= " t.dateo as date_start, t.datee as datee";
1979
		$sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
1980
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
1981
		//if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
1982
		$sql.= ", ".MAIN_DB_PREFIX."projet_task as t";
1983
		$sql.= " WHERE p.entity IN (".getEntity('project', 0).')';
1984
		$sql.= " AND p.fk_statut = 1";
1985
		$sql.= " AND t.fk_projet = p.rowid";
1986
		$sql.= " AND t.progress < 100";         // tasks to do
1987
		if (! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
1 ignored issue
show
Bug introduced by
Are you sure $projectsListId of type array can be used in concatenation? ( Ignorable by Annotation )

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

1987
		if (! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN ("./** @scrutinizer ignore-type */ $projectsListId.")";
Loading history...
1988
		// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
1989
		//if ($socid || ! $user->rights->societe->client->voir)	$sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1990
		if ($socid) $sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1991
		// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
1992
		// if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))";
1993
1994
		//print $sql;
1995
		$resql=$this->db->query($sql);
1996
		if ($resql)
1997
		{
1998
			$task_static = new Task($this->db);
1999
2000
			$response = new WorkboardResponse();
2001
			$response->warning_delay = $conf->projet->task->warning_delay/60/60/24;
2002
			$response->label = $langs->trans("OpenedTasks");
2003
			if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2004
			else $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2005
			$response->img = img_object('', "task");
2006
2007
			// This assignment in condition is not a bug. It allows walking the results.
2008
			while ($obj=$this->db->fetch_object($resql))
2009
			{
2010
				$response->nbtodo++;
2011
2012
				$task_static->projectstatus = $obj->projectstatus;
2013
				$task_static->progress = $obj->progress;
2014
				$task_static->fk_statut = $obj->status;
2015
				$task_static->date_end = $this->db->jdate($obj->datee);
2016
2017
				if ($task_static->hasDelay()) {
2018
					$response->nbtodolate++;
2019
				}
2020
			}
2021
2022
			return $response;
2023
		}
2024
		else
2025
		{
2026
			$this->error=$this->db->error();
2027
			return -1;
2028
		}
2029
	}
2030
2031
2032
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2033
	/**
2034
	 *      Charge indicateurs this->nb de tableau de bord
2035
	 *
2036
	 *      @return     int         <0 if ko, >0 if ok
2037
	 */
2038
	public function load_state_board()
2039
	{
2040
        // phpcs:enable
2041
		global $user;
2042
2043
		$mine=0; $socid=$user->societe_id;
2044
2045
		$projectstatic = new Project($this->db);
2046
		$projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2047
2048
		// List of tasks (does not care about permissions. Filtering will be done later)
2049
		$sql = "SELECT count(p.rowid) as nb";
2050
		$sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
2051
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2052
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2053
		$sql.= ", ".MAIN_DB_PREFIX."projet_task as t";
2054
		$sql.= " WHERE p.entity IN (".getEntity('project', 0).')';
2055
		$sql.= " AND t.fk_projet = p.rowid";         // tasks to do
2056
		if ($mine || ! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
1 ignored issue
show
Bug introduced by
Are you sure $projectsListId of type array can be used in concatenation? ( Ignorable by Annotation )

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

2056
		if ($mine || ! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN ("./** @scrutinizer ignore-type */ $projectsListId.")";
Loading history...
2057
		// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2058
		//if ($socid || ! $user->rights->societe->client->voir)	$sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
2059
		if ($socid) $sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
2060
		if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))";
2061
2062
		$resql=$this->db->query($sql);
2063
		if ($resql)
2064
		{
2065
2066
			// This assignment in condition is not a bug. It allows walking the results.
2067
			while ($obj=$this->db->fetch_object($resql))
2068
			{
2069
				$this->nb["tasks"]=$obj->nb;
2070
			}
2071
			$this->db->free($resql);
2072
			return 1;
2073
		}
2074
		else
2075
		{
2076
			dol_print_error($this->db);
2077
			$this->error=$this->db->error();
2078
			return -1;
2079
		}
2080
	}
2081
2082
	/**
2083
	 * Is the task delayed?
2084
	 *
2085
	 * @return bool
2086
	 */
2087
	public function hasDelay()
2088
	{
2089
		global $conf;
2090
2091
		if (! ($this->progress >= 0 && $this->progress < 100)) {
2092
			return false;
2093
		}
2094
2095
		$now = dol_now();
2096
2097
		$datetouse = ($this->date_end > 0) ? $this->date_end : ($this->datee > 0 ? $this->datee : 0);
0 ignored issues
show
Bug introduced by
The property datee does not exist on Task. Did you mean date?
Loading history...
2098
2099
		return ($datetouse > 0 && ($datetouse < ($now - $conf->projet->task->warning_delay)));
2100
	}
2101
}
2102