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

Task::delete()   F

Complexity

Conditions 20
Paths 1515

Size

Total Lines 117

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
nc 1515
nop 2
dl 0
loc 117
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

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