dolibarr 20.0.0
task.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2008-2014 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2010-2012 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
5 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2020 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
8 * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
9 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
31require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
32require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
33require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
34require_once DOL_DOCUMENT_ROOT.'/core/class/timespent.class.php';
35
36
41{
45 public $element = 'project_task';
46
50 public $table_element = 'projet_task';
51
55 public $fk_element = 'fk_element';
56
60 public $picto = 'projecttask';
61
65 protected $childtables = array(
66 'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task')
67 );
68
72 public $fk_task_parent = 0;
73
77 public $label;
78
82 public $description;
83
84 public $duration_effective; // total of time spent on this task
85 public $planned_workload;
86 public $date_c;
87 public $progress;
88
92 public $dateo;
93
94 public $date_start;
95
99 public $datee;
100
101 public $date_end;
102
107 public $fk_statut;
108
112 public $status;
113
114 public $priority;
115
119 public $fk_user_creat;
120
124 public $fk_user_valid;
125
126 public $rang;
127
128 public $timespent_min_date;
129 public $timespent_max_date;
130 public $timespent_total_duration;
131 public $timespent_total_amount;
132 public $timespent_nblinesnull;
133 public $timespent_nblines;
134 // For detail of lines of timespent record, there is the property ->lines in common
135
136 // Var used to call method addTimeSpent(). Bad practice.
137 public $timespent_id;
138 public $timespent_duration;
139 public $timespent_old_duration;
140 public $timespent_date;
141 public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
142 public $timespent_withhour; // 1 = we entered also start hours for timesheet line
143 public $timespent_fk_user;
144 public $timespent_thm;
145 public $timespent_note;
146 public $timespent_fk_product;
147 public $timespent_invoiceid;
148 public $timespent_invoicelineid;
149
150 public $comments = array();
151
152 // Properties calculated from sum of llx_element_time linked to task
153 public $tobill;
154 public $billed;
155
156 // Properties to store project information
157 public $projectref;
158 public $projectstatus;
159 public $projectlabel;
160 public $opp_amount;
161 public $opp_percent;
162 public $fk_opp_status;
163 public $usage_bill_time;
164 public $public;
165 public $array_options_project;
166
167 // Properties to store thirdparty of project information
168 public $socid;
169 public $thirdparty_id;
170 public $thirdparty_name;
171 public $thirdparty_email;
172
173 // store parent ref and position
174 public $task_parent_ref;
175 public $task_parent_position;
176
177
178
182 public $budget_amount;
183
187 public $project_budget_amount;
188
192 const STATUS_DRAFT = 0;
193
198
202 const STATUS_CLOSED = 3;
203
208
213
214
220 public function __construct($db)
221 {
222 $this->db = $db;
223 }
224
225
233 public function create($user, $notrigger = 0)
234 {
235 global $conf, $langs;
236
237 //For the date
238 $now = dol_now();
239
240 $error = 0;
241
242 // Clean parameters
243 $this->label = trim($this->label);
244 $this->description = trim($this->description);
245 $this->note_public = trim($this->note_public);
246 $this->note_private = trim($this->note_private);
247
248 if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
249 $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
250 return -1;
251 }
252
253 // Insert request
254 $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
255 $sql .= "entity";
256 $sql .= ", fk_projet";
257 $sql .= ", ref";
258 $sql .= ", fk_task_parent";
259 $sql .= ", label";
260 $sql .= ", description";
261 $sql .= ", note_public";
262 $sql .= ", note_private";
263 $sql .= ", datec";
264 $sql .= ", fk_user_creat";
265 $sql .= ", dateo";
266 $sql .= ", datee";
267 $sql .= ", planned_workload";
268 $sql .= ", progress";
269 $sql .= ", budget_amount";
270 $sql .= ", priority";
271 $sql .= ") VALUES (";
272 $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
273 $sql .= ", ".((int) $this->fk_project);
274 $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
275 $sql .= ", ".((int) $this->fk_task_parent);
276 $sql .= ", '".$this->db->escape($this->label)."'";
277 $sql .= ", '".$this->db->escape($this->description)."'";
278 $sql .= ", '".$this->db->escape($this->note_public)."'";
279 $sql .= ", '".$this->db->escape($this->note_private)."'";
280 $sql .= ", '".$this->db->idate($now)."'";
281 $sql .= ", ".((int) $user->id);
282 $sql .= ", ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : 'null');
283 $sql .= ", ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : 'null');
284 $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
285 $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
286 $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
287 $sql .= ", ".(($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null');
288 $sql .= ")";
289
290 $this->db->begin();
291
292 dol_syslog(get_class($this)."::create", LOG_DEBUG);
293 $resql = $this->db->query($sql);
294 if (!$resql) {
295 $error++;
296 $this->errors[] = "Error ".$this->db->lasterror();
297 }
298
299 if (!$error) {
300 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
301 // Update extrafield
302 $result = $this->insertExtraFields();
303 if ($result < 0) {
304 $error++;
305 }
306 }
307
308 if (!$error) {
309 if (!$notrigger) {
310 // Call trigger
311 $result = $this->call_trigger('TASK_CREATE', $user);
312 if ($result < 0) {
313 $error++;
314 }
315 // End call triggers
316 }
317 }
318
319 // Commit or rollback
320 if ($error) {
321 foreach ($this->errors as $errmsg) {
322 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
323 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
324 }
325 $this->db->rollback();
326 return -1 * $error;
327 } else {
328 $this->db->commit();
329 return $this->id;
330 }
331 }
332
333
342 public function fetch($id, $ref = '', $loadparentdata = 0)
343 {
344 $sql = "SELECT";
345 $sql .= " t.rowid,";
346 $sql .= " t.ref,";
347 $sql .= " t.entity,";
348 $sql .= " t.fk_projet as fk_project,";
349 $sql .= " t.fk_task_parent,";
350 $sql .= " t.label,";
351 $sql .= " t.description,";
352 $sql .= " t.duration_effective,";
353 $sql .= " t.planned_workload,";
354 $sql .= " t.datec,";
355 $sql .= " t.dateo as date_start,";
356 $sql .= " t.datee as date_end,";
357 $sql .= " t.fk_user_creat,";
358 $sql .= " t.fk_user_valid,";
359 $sql .= " t.fk_statut as status,";
360 $sql .= " t.progress,";
361 $sql .= " t.budget_amount,";
362 $sql .= " t.priority,";
363 $sql .= " t.note_private,";
364 $sql .= " t.note_public,";
365 $sql .= " t.rang";
366 if (!empty($loadparentdata)) {
367 $sql .= ", t2.ref as task_parent_ref";
368 $sql .= ", t2.rang as task_parent_position";
369 }
370 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
371 if (!empty($loadparentdata)) {
372 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
373 }
374 $sql .= " WHERE ";
375 if (!empty($ref)) {
376 $sql .= "entity IN (".getEntity('project').")";
377 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
378 } else {
379 $sql .= "t.rowid = ".((int) $id);
380 }
381
382 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
383 $resql = $this->db->query($sql);
384 if ($resql) {
385 $num_rows = $this->db->num_rows($resql);
386
387 if ($num_rows) {
388 $obj = $this->db->fetch_object($resql);
389
390 $this->id = $obj->rowid;
391 $this->ref = $obj->ref;
392 $this->entity = $obj->entity;
393 $this->fk_project = $obj->fk_project;
394 $this->fk_task_parent = $obj->fk_task_parent;
395 $this->label = $obj->label;
396 $this->description = $obj->description;
397 $this->duration_effective = $obj->duration_effective;
398 $this->planned_workload = $obj->planned_workload;
399 $this->date_c = $this->db->jdate($obj->datec);
400 $this->date_start = $this->db->jdate($obj->date_start);
401 $this->date_end = $this->db->jdate($obj->date_end);
402 $this->fk_user_creat = $obj->fk_user_creat;
403 $this->fk_user_valid = $obj->fk_user_valid;
404 $this->fk_statut = $obj->status;
405 $this->status = $obj->status;
406 $this->progress = $obj->progress;
407 $this->budget_amount = $obj->budget_amount;
408 $this->priority = $obj->priority;
409 $this->note_private = $obj->note_private;
410 $this->note_public = $obj->note_public;
411 $this->rang = $obj->rang;
412
413 if (!empty($loadparentdata)) {
414 $this->task_parent_ref = $obj->task_parent_ref;
415 $this->task_parent_position = $obj->task_parent_position;
416 }
417
418 // Retrieve all extrafield
419 $this->fetch_optionals();
420 }
421
422 $this->db->free($resql);
423
424 if ($num_rows) {
425 return 1;
426 } else {
427 return 0;
428 }
429 } else {
430 $this->error = "Error ".$this->db->lasterror();
431 return -1;
432 }
433 }
434
435
443 public function update($user = null, $notrigger = 0)
444 {
445 global $conf, $langs;
446 $error = 0;
447
448 // Clean parameters
449 if (isset($this->fk_project)) {
450 $this->fk_project = (int) $this->fk_project;
451 }
452 if (isset($this->ref)) {
453 $this->ref = trim($this->ref);
454 }
455 if (isset($this->fk_task_parent)) {
456 $this->fk_task_parent = (int) $this->fk_task_parent;
457 }
458 if (isset($this->label)) {
459 $this->label = trim($this->label);
460 }
461 if (isset($this->description)) {
462 $this->description = trim($this->description);
463 }
464 if (isset($this->note_public)) {
465 $this->note_public = trim($this->note_public);
466 }
467 if (isset($this->note_private)) {
468 $this->note_private = trim($this->note_private);
469 }
470 if (isset($this->duration_effective)) {
471 $this->duration_effective = trim($this->duration_effective);
472 }
473 if (isset($this->planned_workload)) {
474 $this->planned_workload = trim($this->planned_workload);
475 }
476 if (isset($this->budget_amount)) {
477 $this->budget_amount = (float) $this->budget_amount;
478 }
479
480 if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
481 $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
482 return -1;
483 }
484
485 // Check parameters
486 // Put here code to add control on parameters values
487
488 // Update request
489 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
490 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
491 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
492 $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
493 $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
494 $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
495 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
496 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
497 $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
498 $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
499 $sql .= " dateo=".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
500 $sql .= " datee=".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
501 $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
502 $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
503 $sql .= " rang=".((!empty($this->rang)) ? ((int) $this->rang) : "0").",";
504 $sql .= " priority=".((!empty($this->priority)) ? ((int) $this->priority) : "0");
505 $sql .= " WHERE rowid=".((int) $this->id);
506
507 $this->db->begin();
508
509 dol_syslog(get_class($this)."::update", LOG_DEBUG);
510 $resql = $this->db->query($sql);
511 if (!$resql) {
512 $error++;
513 $this->errors[] = "Error ".$this->db->lasterror();
514 }
515
516 // Update extrafield
517 if (!$error) {
518 $result = $this->insertExtraFields();
519 if ($result < 0) {
520 $error++;
521 }
522 }
523
524 if (!$error && getDolGlobalString('PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE')) {
525 // Close the parent project if it is open (validated) and its tasks are 100% completed
526 $project = new Project($this->db);
527 if ($project->fetch($this->fk_project) > 0) {
528 if ($project->statut == Project::STATUS_VALIDATED) {
529 $project->getLinesArray(null); // this method does not return <= 0 if fails
530 $projectCompleted = array_reduce(
531 $project->lines,
537 static function ($allTasksCompleted, $task) {
538 return $allTasksCompleted && $task->progress >= 100;
539 },
540 1
541 );
542 if ($projectCompleted) {
543 if ($project->setClose($user) <= 0) {
544 $error++;
545 }
546 }
547 }
548 } else {
549 $error++;
550 }
551 if ($error) {
552 $this->errors[] = $project->error;
553 }
554 }
555
556 if (!$error) {
557 if (!$notrigger) {
558 // Call trigger
559 $result = $this->call_trigger('TASK_MODIFY', $user);
560 if ($result < 0) {
561 $error++;
562 }
563 // End call triggers
564 }
565 }
566
567 if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
568 // We remove directory
569 if ($conf->project->dir_output) {
570 $project = new Project($this->db);
571 $project->fetch($this->fk_project);
572
573 $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
574 $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
575 if (file_exists($olddir)) {
576 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
577 $res = dol_move_dir($olddir, $newdir);
578 if (!$res) {
579 $langs->load("errors");
580 $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
581 $error++;
582 }
583 }
584 }
585 }
586
587 // Commit or rollback
588 if ($error) {
589 foreach ($this->errors as $errmsg) {
590 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
591 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
592 }
593 $this->db->rollback();
594 return -1 * $error;
595 } else {
596 $this->db->commit();
597 return 1;
598 }
599 }
600
601
609 public function delete($user, $notrigger = 0)
610 {
611 global $conf;
612 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
613
614 $error = 0;
615
616 $this->db->begin();
617
618 if ($this->hasChildren() > 0) {
619 dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
620 $this->error = 'ErrorRecordHasSubTasks';
621 $this->db->rollback();
622 return 0;
623 }
624
625 $objectisused = $this->isObjectUsed($this->id);
626 if (!empty($objectisused)) {
627 dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
628 $this->error = 'ErrorRecordHasChildren';
629 $this->db->rollback();
630 return 0;
631 }
632
633 if (!$error) {
634 // Delete linked contacts
635 $res = $this->delete_linked_contact();
636 if ($res < 0) {
637 $this->error = 'ErrorFailToDeleteLinkedContact';
638 //$error++;
639 $this->db->rollback();
640 return 0;
641 }
642 }
643
644 if (!$error) {
645 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_time";
646 $sql .= " WHERE fk_element = ".((int) $this->id)." AND elementtype = 'task'";
647
648 $resql = $this->db->query($sql);
649 if (!$resql) {
650 $error++;
651 $this->errors[] = "Error ".$this->db->lasterror();
652 }
653 }
654
655 if (!$error) {
656 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
657 $sql .= " WHERE fk_object = ".((int) $this->id);
658
659 $resql = $this->db->query($sql);
660 if (!$resql) {
661 $error++;
662 $this->errors[] = "Error ".$this->db->lasterror();
663 }
664 }
665
666 if (!$error) {
667 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
668 $sql .= " WHERE rowid=".((int) $this->id);
669
670 $resql = $this->db->query($sql);
671 if (!$resql) {
672 $error++;
673 $this->errors[] = "Error ".$this->db->lasterror();
674 }
675 }
676
677 if (!$error) {
678 if (!$notrigger) {
679 // Call trigger
680 $result = $this->call_trigger('TASK_DELETE', $user);
681 if ($result < 0) {
682 $error++;
683 }
684 // End call triggers
685 }
686 }
687
688 // Commit or rollback
689 if ($error) {
690 foreach ($this->errors as $errmsg) {
691 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
692 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
693 }
694 $this->db->rollback();
695 return -1 * $error;
696 } else {
697 //Delete associated link file
698 if ($conf->project->dir_output) {
699 $projectstatic = new Project($this->db);
700 $projectstatic->fetch($this->fk_project);
701
702 $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
703 dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
704 if (file_exists($dir)) {
705 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
706 $res = @dol_delete_dir_recursive($dir);
707 if (!$res) {
708 $this->error = 'ErrorFailToDeleteDir';
709 $this->db->rollback();
710 return 0;
711 }
712 }
713 }
714
715 $this->db->commit();
716
717 return 1;
718 }
719 }
720
726 public function hasChildren()
727 {
728 $error = 0;
729 $ret = 0;
730
731 $sql = "SELECT COUNT(*) as nb";
732 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
733 $sql .= " WHERE fk_task_parent = ".((int) $this->id);
734
735 dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
736 $resql = $this->db->query($sql);
737 if (!$resql) {
738 $error++;
739 $this->errors[] = "Error ".$this->db->lasterror();
740 } else {
741 $obj = $this->db->fetch_object($resql);
742 if ($obj) {
743 $ret = $obj->nb;
744 }
745 $this->db->free($resql);
746 }
747
748 if (!$error) {
749 return $ret;
750 } else {
751 return -1;
752 }
753 }
754
760 public function hasTimeSpent()
761 {
762 $error = 0;
763 $ret = 0;
764
765 $sql = "SELECT COUNT(*) as nb";
766 $sql .= " FROM ".MAIN_DB_PREFIX."element_time";
767 $sql .= " WHERE fk_element = ".((int) $this->id);
768 $sql .= " AND elementtype = 'task'";
769
770 dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
771 $resql = $this->db->query($sql);
772 if (!$resql) {
773 $error++;
774 $this->errors[] = "Error ".$this->db->lasterror();
775 } else {
776 $obj = $this->db->fetch_object($resql);
777 if ($obj) {
778 $ret = $obj->nb;
779 }
780 $this->db->free($resql);
781 }
782
783 if (!$error) {
784 return $ret;
785 } else {
786 return -1;
787 }
788 }
789
790
798 public function getTooltipContentArray($params)
799 {
800 global $langs;
801
802 $langs->load('projects');
803
804 $datas = [];
805 $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
806 if (!empty($this->ref)) {
807 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
808 }
809 if (!empty($this->label)) {
810 $datas['label'] = '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
811 }
812 if ($this->date_start || $this->date_end) {
813 $datas['range'] = "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
814 }
815
816 return $datas;
817 }
818
831 public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
832 {
833 global $action, $conf, $hookmanager, $langs;
834
835 if (!empty($conf->dol_no_mouse_hover)) {
836 $notooltip = 1; // Force disable tooltips
837 }
838
839 $result = '';
840 $params = [
841 'id' => $this->id,
842 'objecttype' => $this->element,
843 ];
844 $classfortooltip = 'classfortooltip';
845 $dataparams = '';
846 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
847 $classfortooltip = 'classforajaxtooltip';
848 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
849 $label = '';
850 } else {
851 $label = implode($this->getTooltipContentArray($params));
852 }
853
854 $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
855 // Add param to save lastsearch_values or not
856 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
857 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
858 $add_save_lastsearch_values = 1;
859 }
860 if ($add_save_lastsearch_values) {
861 $url .= '&save_lastsearch_values=1';
862 }
863
864 $linkclose = '';
865 if (empty($notooltip)) {
866 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
867 $label = $langs->trans("ShowTask");
868 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
869 }
870 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
871 $linkclose .= $dataparams.' class="'.$classfortooltip.' nowraponall"';
872 } else {
873 $linkclose .= ' class="nowraponall"';
874 }
875
876 $linkstart = '<a href="'.$url.'"';
877 $linkstart .= $linkclose.'>';
878 $linkend = '</a>';
879
880 $picto = 'projecttask';
881
882 $result .= $linkstart;
883 if ($withpicto) {
884 $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
885 }
886 if ($withpicto != 2) {
887 $result .= $this->ref;
888 }
889 $result .= $linkend;
890 if ($withpicto != 2) {
891 $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
892 }
893
894 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
895 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
896 if ($reshook > 0) {
897 $result = $hookmanager->resPrint;
898 } else {
899 $result .= $hookmanager->resPrint;
900 }
901
902 return $result;
903 }
904
912 public function initAsSpecimen()
913 {
914 global $user;
915
916 $this->id = 0;
917
918 $this->fk_project = 0;
919 $this->ref = 'TK01';
920 $this->fk_task_parent = 0;
921 $this->label = 'Specimen task TK01';
922 $this->duration_effective = '';
923 $this->fk_user_creat = $user->id;
924 $this->progress = '25';
925 $this->status = 0;
926 $this->priority = 0;
927 $this->note_private = 'This is a specimen private note';
928 $this->note_public = 'This is a specimen public note';
929
930 return 1;
931 }
932
956 public function getTasksArray($usert = null, $userp = null, $projectid = 0, $socid = 0, $mode = 0, $filteronproj = '', $filteronprojstatus = '-1', $morewherefilter = '', $filteronprojuser = 0, $filterontaskuser = 0, $extrafields = null, $includebilltime = 0, $search_array_options = array(), $loadextras = 0, $loadRoleMode = 1, $sortfield = '', $sortorder = '')
957 {
958 global $hookmanager;
959
960 $tasks = array();
961
962 //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
963
964 // List of tasks (does not care about permissions. Filtering will be done later)
965 $sql = "SELECT ";
966 if ($filteronprojuser > 0 || $filterontaskuser > 0) {
967 $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
968 }
969 $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
970 $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,";
971 $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
972 $sql .= " t.budget_amount,";
973 $sql .= " t.note_public, t.note_private,";
974 $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
975 $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
976 if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
977 if (!empty($extrafields->attributes['projet']['label'])) {
978 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
979 $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
980 }
981 }
982 if (!empty($extrafields->attributes['projet_task']['label'])) {
983 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
984 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
985 }
986 }
987 }
988 if ($includebilltime) {
989 $sql .= ", SUM(tt.element_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.element_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed";
990 }
991
992 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
993 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
994 if ($loadextras) {
995 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
996 }
997
998 if ($mode == 0) {
999 if ($filteronprojuser > 0) {
1000 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1001 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1002 }
1003 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1004 if ($loadextras) {
1005 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
1006 }
1007 if ($includebilltime) {
1008 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1009 }
1010 if ($filterontaskuser > 0) {
1011 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1012 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1013 }
1014 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1015 $sql .= " AND t.fk_projet = p.rowid";
1016 } elseif ($mode == 1) {
1017 if ($filteronprojuser > 0) {
1018 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1019 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1020 }
1021 if ($filterontaskuser > 0) {
1022 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1023 if ($includebilltime) {
1024 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1025 }
1026 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1027 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1028 } else {
1029 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
1030 if ($includebilltime) {
1031 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
1032 }
1033 }
1034 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1035 } else {
1036 return 'BadValueForParameterMode';
1037 }
1038
1039 if ($filteronprojuser > 0) {
1040 $sql .= " AND p.rowid = ec.element_id";
1041 $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1042 $sql .= " AND ctc.element = 'project'";
1043 $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
1044 $sql .= " AND ec.statut = 4";
1045 $sql .= " AND ctc.source = 'internal'";
1046 }
1047 if ($filterontaskuser > 0) {
1048 $sql .= " AND t.fk_projet = p.rowid";
1049 $sql .= " AND p.rowid = ec2.element_id";
1050 $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
1051 $sql .= " AND ctc2.element = 'project_task'";
1052 $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
1053 $sql .= " AND ec2.statut = 4";
1054 $sql .= " AND ctc2.source = 'internal'";
1055 }
1056 if ($socid) {
1057 $sql .= " AND p.fk_soc = ".((int) $socid);
1058 }
1059 if ($projectid) {
1060 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1061 }
1062 if ($filteronproj) {
1063 $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
1064 }
1065 if ($filteronprojstatus && (int) $filteronprojstatus != '-1') {
1066 $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
1067 }
1068 if ($morewherefilter) {
1069 $sql .= $morewherefilter;
1070 }
1071
1072 // Add where from extra fields
1073 $extrafieldsobjectkey = 'projet_task';
1074 $extrafieldsobjectprefix = 'efpt.';
1075 global $db, $conf; // needed for extrafields_list_search_sql.tpl
1076 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
1077
1078 // Add where from hooks
1079 $parameters = array();
1080 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
1081 $sql .= $hookmanager->resPrint;
1082 if ($includebilltime) {
1083 $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
1084 $sql .= " t.datec, t.dateo, t.datee, t.tms,";
1085 $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
1086 $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
1087 $sql .= " t.budget_amount,";
1088 $sql .= " t.note_public, t.note_private,";
1089 $sql .= " s.rowid, s.nom, s.email,";
1090 $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
1091 if ($loadextras) {
1092 if (!empty($extrafields->attributes['projet']['label'])) {
1093 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1094 $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
1095 }
1096 }
1097 if (!empty($extrafields->attributes['projet_task']['label'])) {
1098 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1099 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
1100 }
1101 }
1102 }
1103 }
1104
1105 if ($sortfield && $sortorder) {
1106 $sql .= $this->db->order($sortfield, $sortorder);
1107 } else {
1108 $sql .= " ORDER BY p.ref, t.rang, t.dateo";
1109 }
1110
1111 //print $sql;exit;
1112 dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
1113 $resql = $this->db->query($sql);
1114 if ($resql) {
1115 $num = $this->db->num_rows($resql);
1116 $i = 0;
1117 // Loop on each record found, so each couple (project id, task id)
1118 while ($i < $num) {
1119 $error = 0;
1120
1121 $obj = $this->db->fetch_object($resql);
1122
1123 if ($loadRoleMode) {
1124 if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
1125 if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
1126 $error++;
1127 }
1128 }
1129 if (is_object($usert)) { // If we ask a filter on a user affected to a task
1130 if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
1131 $error++;
1132 }
1133 }
1134 }
1135
1136 if (!$error) {
1137 $tasks[$i] = new Task($this->db);
1138 $tasks[$i]->id = $obj->taskid;
1139 $tasks[$i]->ref = $obj->taskref;
1140 $tasks[$i]->fk_project = $obj->projectid;
1141
1142 // Data from project
1143 $tasks[$i]->projectref = $obj->ref;
1144 $tasks[$i]->projectlabel = $obj->plabel;
1145 $tasks[$i]->projectstatus = $obj->projectstatus;
1146 $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1147 $tasks[$i]->opp_amount = $obj->opp_amount;
1148 $tasks[$i]->opp_percent = $obj->opp_percent;
1149 $tasks[$i]->budget_amount = $obj->budget_amount;
1150 $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1151 $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1152
1153 $tasks[$i]->label = $obj->label;
1154 $tasks[$i]->description = $obj->description;
1155
1156 $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1157 $tasks[$i]->note_public = $obj->note_public;
1158 $tasks[$i]->note_private = $obj->note_private;
1159 $tasks[$i]->duration_effective = $obj->duration_effective;
1160 $tasks[$i]->planned_workload = $obj->planned_workload;
1161
1162 if ($includebilltime) {
1163 // Data summed from element_time linked to task
1164 $tasks[$i]->tobill = $obj->tobill;
1165 $tasks[$i]->billed = $obj->billed;
1166 }
1167
1168 $tasks[$i]->progress = $obj->progress;
1169 $tasks[$i]->fk_statut = $obj->status;
1170 $tasks[$i]->status = $obj->status;
1171 $tasks[$i]->public = $obj->public;
1172 $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1173 $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1174 $tasks[$i]->rang = $obj->rang;
1175 $tasks[$i]->priority = $obj->priority;
1176
1177 $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1178 $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1179 $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1180 $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1181
1182 if ($loadextras) {
1183 if (!empty($extrafields->attributes['projet']['label'])) {
1184 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1185 if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1186 $tmpvar = 'options_'.$key;
1187 $tasks[$i]->array_options_project['options_'.$key] = $obj->$tmpvar;
1188 }
1189 }
1190 }
1191 }
1192
1193 if ($loadextras) {
1194 $tasks[$i]->fetch_optionals();
1195 }
1196 }
1197
1198 $i++;
1199 }
1200 $this->db->free($resql);
1201 } else {
1202 dol_print_error($this->db);
1203 }
1204
1205 return $tasks;
1206 }
1207
1218 public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1219 {
1220 $arrayroles = array();
1221
1222 dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".json_encode(is_object($userp))." usert=".json_encode(is_object($usert))." projectid=".$projectid." taskid=".$taskid);
1223
1224 // We want role of user for a projet or role of user for a task. Both are not possible.
1225 if (empty($userp) && empty($usert)) {
1226 $this->error = "CallWithWrongParameters";
1227 return -1;
1228 }
1229 if (!empty($userp) && !empty($usert)) {
1230 $this->error = "CallWithWrongParameters";
1231 return -1;
1232 }
1233
1234 /* Liste des taches et role sur les projects ou taches */
1235 $sql = "SELECT ";
1236 if ($userp) {
1237 $sql .= " p.rowid as pid,";
1238 } else {
1239 $sql .= " pt.rowid as pid,";
1240 }
1241 $sql .= " ec.element_id, ctc.code, ctc.source";
1242 if ($userp) {
1243 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1244 }
1245 if ($usert && $filteronprojstatus > -1) {
1246 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1247 }
1248 if ($usert && $filteronprojstatus <= -1) {
1249 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1250 }
1251 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1252 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1253 if ($userp) {
1254 $sql .= " WHERE p.rowid = ec.element_id";
1255 } else {
1256 $sql .= " WHERE pt.rowid = ec.element_id";
1257 }
1258 if ($userp && $filteronprojstatus > -1) {
1259 $sql .= " AND p.fk_statut = ".((int) $filteronprojstatus);
1260 }
1261 if ($usert && $filteronprojstatus > -1) {
1262 $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1263 }
1264 if ($userp) {
1265 $sql .= " AND ctc.element = 'project'";
1266 }
1267 if ($usert) {
1268 $sql .= " AND ctc.element = 'project_task'";
1269 }
1270 $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1271 if ($userp) {
1272 $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1273 }
1274 if ($usert) {
1275 $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1276 }
1277 $sql .= " AND ec.statut = 4";
1278 $sql .= " AND ctc.source = 'internal'";
1279 if ($projectid) {
1280 if ($userp) {
1281 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1282 }
1283 if ($usert) {
1284 $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1285 }
1286 }
1287 if ($taskid) {
1288 if ($userp) {
1289 $sql .= " ERROR SHOULD NOT HAPPENS";
1290 }
1291 if ($usert) {
1292 $sql .= " AND pt.rowid = ".((int) $taskid);
1293 }
1294 }
1295 //print $sql;
1296
1297 dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1298 $resql = $this->db->query($sql);
1299 if ($resql) {
1300 $num = $this->db->num_rows($resql);
1301 $i = 0;
1302 while ($i < $num) {
1303 $obj = $this->db->fetch_object($resql);
1304 if (empty($arrayroles[$obj->pid])) {
1305 $arrayroles[$obj->pid] = $obj->code;
1306 } else {
1307 $arrayroles[$obj->pid] .= ','.$obj->code;
1308 }
1309 $i++;
1310 }
1311 $this->db->free($resql);
1312 } else {
1313 dol_print_error($this->db);
1314 }
1315
1316 return $arrayroles;
1317 }
1318
1319
1326 public function getListContactId($source = 'internal')
1327 {
1328 $contactAlreadySelected = array();
1329 $tab = $this->liste_contact(-1, $source);
1330 //var_dump($tab);
1331 $num = count($tab);
1332 $i = 0;
1333 while ($i < $num) {
1334 if ($source == 'thirdparty') {
1335 $contactAlreadySelected[$i] = $tab[$i]['socid'];
1336 } else {
1337 $contactAlreadySelected[$i] = $tab[$i]['id'];
1338 }
1339 $i++;
1340 }
1341 return $contactAlreadySelected;
1342 }
1343
1351 public function mergeContactTask($origin_id, $dest_id)
1352 {
1353 $error = 0;
1354 $origintask = new Task($this->db);
1355 $result = $origintask->fetch($origin_id);
1356 if ($result <= 0) {
1357 return false;
1358 }
1359
1360 //Get list of origin contacts
1361 $arraycontactorigin = array_merge($origintask->liste_contact(-1, 'internal'), $origintask->liste_contact(-1, 'external'));
1362 if (is_array($arraycontactorigin)) {
1363 foreach ($arraycontactorigin as $key => $contact) {
1364 $result = $this->add_contact($contact["id"], $contact["fk_c_type_contact"], $contact["source"]);
1365 if ($result < 0) {
1366 return false;
1367 }
1368 }
1369 }
1370 return true;
1371 }
1372
1380 public function mergeTimeSpentTask($origin_id, $dest_id)
1381 {
1382 $ret = true;
1383
1384 $this->db->begin();
1385
1386 $sql = "UPDATE ".MAIN_DB_PREFIX."element_time as et";
1387 $sql .= " SET et.fk_element = ".((int) $dest_id);
1388 $sql .= " WHERE et.elementtype = 'task'";
1389 $sql .= " AND et.fk_element = ".((int) $origin_id);
1390
1391 dol_syslog(get_class($this)."::mergeTimeSpentTask", LOG_DEBUG);
1392 if (!$this->db->query($sql)) {
1393 $this->error = $this->db->lasterror();
1394 $ret = false;
1395 }
1396
1397 if ($ret) {
1398 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1399 $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM ".MAIN_DB_PREFIX."element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = ".((int) $dest_id).")";
1400 $sql .= " WHERE rowid = ".((int) $dest_id);
1401
1402 dol_syslog(get_class($this)."::mergeTimeSpentTask update project_task", LOG_DEBUG);
1403 if (!$this->db->query($sql)) {
1404 $this->error = $this->db->lasterror();
1405 $ret = false;
1406 }
1407 }
1408
1409 if ($ret == true) {
1410 $this->db->commit();
1411 } else {
1412 $this->db->rollback();
1413 }
1414 return $ret;
1415 }
1416
1424 public function addTimeSpent($user, $notrigger = 0)
1425 {
1426 global $langs;
1427
1428 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1429
1430 $ret = 0;
1431 $now = dol_now();
1432
1433 // Check parameters
1434 if (!is_object($user)) {
1435 dol_print_error(null, "Method addTimeSpent was called with wrong parameter user");
1436 return -1;
1437 }
1438
1439 // Clean parameters
1440 if (isset($this->timespent_note)) {
1441 $this->timespent_note = trim($this->timespent_note);
1442 }
1443 if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1444 $this->timespent_datehour = $this->timespent_date;
1445 }
1446
1447 if (getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1448 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1449 $restrictBefore = dol_time_plus_duree(dol_now(), - getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'), 'm');
1450
1451 if ($this->timespent_date < $restrictBefore) {
1452 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1453 $this->errors[] = $this->error;
1454 return -1;
1455 }
1456 }
1457
1458 $this->db->begin();
1459
1460 $timespent = new TimeSpent($this->db);
1461 $timespent->fk_element = $this->id;
1462 $timespent->elementtype = 'task';
1463 $timespent->element_date = $this->timespent_date;
1464 $timespent->element_datehour = $this->timespent_datehour;
1465 $timespent->element_date_withhour = $this->timespent_withhour;
1466 $timespent->element_duration = $this->timespent_duration;
1467 $timespent->fk_user = $this->timespent_fk_user;
1468 $timespent->fk_product = $this->timespent_fk_product;
1469 $timespent->note = $this->timespent_note;
1470 $timespent->datec = $this->db->idate($now);
1471
1472 $result = $timespent->create($user);
1473
1474 if ($result > 0) {
1475 $ret = $result;
1476 $this->timespent_id = $result;
1477
1478 if (!$notrigger) {
1479 // Call trigger
1480 $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1481 if ($result < 0) {
1482 $ret = -1;
1483 }
1484 // End call triggers
1485 }
1486 } else {
1487 $this->error = $this->db->lasterror();
1488 $ret = -1;
1489 }
1490
1491 if ($ret > 0) {
1492 // Recalculate amount of time spent for task and update denormalized field
1493 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1494 $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM ".MAIN_DB_PREFIX."element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = ".((int) $this->id).")";
1495 if (isset($this->progress)) {
1496 $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1497 }
1498 $sql .= " WHERE rowid = ".((int) $this->id);
1499
1500 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1501 if (!$this->db->query($sql)) {
1502 $this->error = $this->db->lasterror();
1503 $ret = -2;
1504 }
1505
1506 // Update hourly rate of this time spent entry
1507 $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1508 if (!empty($resql_thm_user)) {
1509 $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1510 $timespent->thm = $obj_thm_user->thm;
1511 }
1512 $res_update = $timespent->update($user);
1513
1514 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1515 if ($res_update <= 0) {
1516 $this->error = $this->db->lasterror();
1517 $ret = -2;
1518 }
1519 }
1520
1521 if ($ret > 0) {
1522 $this->db->commit();
1523 } else {
1524 $this->db->rollback();
1525 }
1526 return $ret;
1527 }
1528
1535 public function fetchTimeSpentOnTask($morewherefilter = '')
1536 {
1537 $arrayres = array();
1538
1539 $sql = "SELECT";
1540 $sql .= " s.rowid as socid,";
1541 $sql .= " s.nom as thirdparty_name,";
1542 $sql .= " s.email as thirdparty_email,";
1543 $sql .= " ptt.rowid,";
1544 $sql .= " ptt.ref_ext,";
1545 $sql .= " ptt.fk_element as fk_task,";
1546 $sql .= " ptt.element_date as task_date,";
1547 $sql .= " ptt.element_datehour as task_datehour,";
1548 $sql .= " ptt.element_date_withhour as task_date_withhour,";
1549 $sql .= " ptt.element_duration as task_duration,";
1550 $sql .= " ptt.fk_user,";
1551 $sql .= " ptt.note,";
1552 $sql .= " ptt.thm,";
1553 $sql .= " pt.rowid as task_id,";
1554 $sql .= " pt.ref as task_ref,";
1555 $sql .= " pt.label as task_label,";
1556 $sql .= " p.rowid as project_id,";
1557 $sql .= " p.ref as project_ref,";
1558 $sql .= " p.title as project_label,";
1559 $sql .= " p.public as public";
1560 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1561 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1562 $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1563 $sql .= " AND ptt.elementtype = 'task'";
1564 $sql .= " AND pt.rowid = ".((int) $this->id);
1565 $sql .= " AND pt.entity IN (".getEntity('project').")";
1566 if ($morewherefilter) {
1567 $sql .= $morewherefilter;
1568 }
1569
1570 dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1571 $resql = $this->db->query($sql);
1572 if ($resql) {
1573 $num = $this->db->num_rows($resql);
1574
1575 $i = 0;
1576 while ($i < $num) {
1577 $obj = $this->db->fetch_object($resql);
1578
1579 $newobj = new stdClass();
1580
1581 $newobj->socid = $obj->socid;
1582 $newobj->thirdparty_name = $obj->thirdparty_name;
1583 $newobj->thirdparty_email = $obj->thirdparty_email;
1584
1585 $newobj->fk_project = $obj->project_id;
1586 $newobj->project_ref = $obj->project_ref;
1587 $newobj->project_label = $obj->project_label;
1588 $newobj->public = $obj->project_public;
1589
1590 $newobj->fk_task = $obj->task_id;
1591 $newobj->task_ref = $obj->task_ref;
1592 $newobj->task_label = $obj->task_label;
1593
1594 $newobj->timespent_line_id = $obj->rowid;
1595 $newobj->timespent_line_ref_ext = $obj->ref_ext;
1596 $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1597 $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1598 $newobj->timespent_line_withhour = $obj->task_date_withhour;
1599 $newobj->timespent_line_duration = $obj->task_duration;
1600 $newobj->timespent_line_fk_user = $obj->fk_user;
1601 $newobj->timespent_line_thm = $obj->thm; // hourly rate
1602 $newobj->timespent_line_note = $obj->note;
1603
1604 $arrayres[] = $newobj;
1605
1606 $i++;
1607 }
1608
1609 $this->db->free($resql);
1610
1611 $this->lines = $arrayres;
1612 return 1;
1613 } else {
1614 dol_print_error($this->db);
1615 $this->error = "Error ".$this->db->lasterror();
1616 return -1;
1617 }
1618 }
1619
1620
1628 public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1629 {
1630 if (is_object($userobj)) {
1631 $userid = $userobj->id;
1632 } else {
1633 $userid = $userobj; // old method
1634 }
1635
1636 $id = $this->id;
1637 if (empty($id) && empty($userid)) {
1638 dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1639 return -1;
1640 }
1641
1642 $result = array();
1643
1644 $sql = "SELECT";
1645 $sql .= " MIN(t.element_datehour) as min_date,";
1646 $sql .= " MAX(t.element_datehour) as max_date,";
1647 $sql .= " SUM(t.element_duration) as total_duration,";
1648 $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1649 $sql .= " COUNT(t.rowid) as nblines,";
1650 $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1651 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1652 $sql .= " WHERE t.elementtype='task'";
1653 if ($morewherefilter) {
1654 $sql .= $morewherefilter;
1655 }
1656 if ($id > 0) {
1657 $sql .= " AND t.fk_element = ".((int) $id);
1658 }
1659 if ($userid > 0) {
1660 $sql .= " AND t.fk_user = ".((int) $userid);
1661 }
1662
1663 dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1664 $resql = $this->db->query($sql);
1665 if ($resql) {
1666 $obj = $this->db->fetch_object($resql);
1667
1668 $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1669 $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1670 $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1671
1672 $this->timespent_min_date = $this->db->jdate($obj->min_date);
1673 $this->timespent_max_date = $this->db->jdate($obj->max_date);
1674 $this->timespent_total_duration = $obj->total_duration;
1675 $this->timespent_total_amount = $obj->total_amount;
1676 $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1677 $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1678
1679 $this->db->free($resql);
1680 } else {
1681 dol_print_error($this->db);
1682 }
1683 return $result;
1684 }
1685
1694 public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1695 {
1696 $id = $this->id;
1697
1698 $result = array();
1699
1700 $sql = "SELECT";
1701 $sql .= " SUM(t.element_duration) as nbseconds,";
1702 $sql .= " SUM(t.element_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";
1703 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1704 $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id);
1705 if (is_object($fuser) && $fuser->id > 0) {
1706 $sql .= " AND fk_user = ".((int) $fuser->id);
1707 }
1708 if ($dates > 0) {
1709 $datefieldname = "element_datehour";
1710 $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1711 }
1712 if ($datee > 0) {
1713 $datefieldname = "element_datehour";
1714 $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1715 }
1716 //print $sql;
1717
1718 dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1719 $resql = $this->db->query($sql);
1720 if ($resql) {
1721 $obj = $this->db->fetch_object($resql);
1722
1723 $result['amount'] = $obj->amount;
1724 $result['nbseconds'] = $obj->nbseconds;
1725 $result['nblinesnull'] = $obj->nblinesnull;
1726
1727 $this->db->free($resql);
1728 return $result;
1729 } else {
1730 dol_print_error($this->db);
1731 return $result;
1732 }
1733 }
1734
1741 public function fetchTimeSpent($id)
1742 {
1743 $timespent = new TimeSpent($this->db);
1744 $timespent->fetch($id);
1745
1746 dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1747
1748 if ($timespent->id > 0) {
1749 $this->timespent_id = $timespent->id;
1750 $this->id = $timespent->fk_element;
1751 $this->timespent_date = $timespent->element_date;
1752 $this->timespent_datehour = $timespent->element_datehour;
1753 $this->timespent_withhour = $timespent->element_date_withhour;
1754 $this->timespent_duration = $timespent->element_duration;
1755 $this->timespent_fk_user = $timespent->fk_user;
1756 $this->timespent_fk_product = $timespent->fk_product;
1757 $this->timespent_thm = $timespent->thm; // hourly rate
1758 $this->timespent_note = $timespent->note;
1759
1760 return 1;
1761 }
1762
1763 return 0;
1764 }
1765
1773 public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1774 {
1775 $arrayres = array();
1776
1777 $sql = "SELECT";
1778 $sql .= " s.rowid as socid,";
1779 $sql .= " s.nom as thirdparty_name,";
1780 $sql .= " s.email as thirdparty_email,";
1781 $sql .= " ptt.rowid,";
1782 $sql .= " ptt.fk_element as fk_task,";
1783 $sql .= " ptt.element_date as task_date,";
1784 $sql .= " ptt.element_datehour as task_datehour,";
1785 $sql .= " ptt.element_date_withhour as task_date_withhour,";
1786 $sql .= " ptt.element_duration as task_duration,";
1787 $sql .= " ptt.fk_user,";
1788 $sql .= " ptt.note,";
1789 $sql .= " ptt.thm,";
1790 $sql .= " pt.rowid as task_id,";
1791 $sql .= " pt.ref as task_ref,";
1792 $sql .= " pt.label as task_label,";
1793 $sql .= " p.rowid as project_id,";
1794 $sql .= " p.ref as project_ref,";
1795 $sql .= " p.title as project_label,";
1796 $sql .= " p.public as public";
1797 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1798 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1799 $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1800 $sql .= " AND ptt.elementtype = 'task'";
1801 $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1802 $sql .= " AND pt.entity IN (".getEntity('project').")";
1803 if ($morewherefilter) {
1804 $sql .= $morewherefilter;
1805 }
1806
1807 dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1808 $resql = $this->db->query($sql);
1809 if ($resql) {
1810 $num = $this->db->num_rows($resql);
1811
1812 $i = 0;
1813 while ($i < $num) {
1814 $obj = $this->db->fetch_object($resql);
1815
1816 $newobj = new stdClass();
1817
1818 $newobj->socid = $obj->socid;
1819 $newobj->thirdparty_name = $obj->thirdparty_name;
1820 $newobj->thirdparty_email = $obj->thirdparty_email;
1821
1822 $newobj->fk_project = $obj->project_id;
1823 $newobj->project_ref = $obj->project_ref;
1824 $newobj->project_label = $obj->project_label;
1825 $newobj->public = $obj->project_public;
1826
1827 $newobj->fk_task = $obj->task_id;
1828 $newobj->task_ref = $obj->task_ref;
1829 $newobj->task_label = $obj->task_label;
1830
1831 $newobj->timespent_id = $obj->rowid;
1832 $newobj->timespent_date = $this->db->jdate($obj->task_date);
1833 $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1834 $newobj->timespent_withhour = $obj->task_date_withhour;
1835 $newobj->timespent_duration = $obj->task_duration;
1836 $newobj->timespent_fk_user = $obj->fk_user;
1837 $newobj->timespent_thm = $obj->thm; // hourly rate
1838 $newobj->timespent_note = $obj->note;
1839
1840 $arrayres[] = $newobj;
1841
1842 $i++;
1843 }
1844
1845 $this->db->free($resql);
1846 } else {
1847 dol_print_error($this->db);
1848 $this->error = "Error ".$this->db->lasterror();
1849 return -1;
1850 }
1851
1852 return $arrayres;
1853 }
1854
1862 public function updateTimeSpent($user, $notrigger = 0)
1863 {
1864 global $conf, $langs;
1865
1866 $ret = 0;
1867
1868 // Check parameters
1869 if ($this->timespent_date == '') {
1870 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1871 return -1;
1872 }
1873 if (!($this->timespent_fk_user > 0)) {
1874 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1875 return -1;
1876 }
1877
1878 // Clean parameters
1879 if (empty($this->timespent_datehour)) {
1880 $this->timespent_datehour = $this->timespent_date;
1881 }
1882 if (isset($this->timespent_note)) {
1883 $this->timespent_note = trim($this->timespent_note);
1884 }
1885
1886 if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1887 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1888 $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1889
1890 if ($this->timespent_date < $restrictBefore) {
1891 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1892 $this->errors[] = $this->error;
1893 return -1;
1894 }
1895 }
1896
1897 $this->db->begin();
1898
1899 $timespent = new TimeSpent($this->db);
1900 $timespent->fetch($this->timespent_id);
1901 $timespent->element_date = $this->timespent_date;
1902 $timespent->element_datehour = $this->timespent_datehour;
1903 $timespent->element_date_withhour = $this->timespent_withhour;
1904 $timespent->element_duration = $this->timespent_duration;
1905 $timespent->fk_user = $this->timespent_fk_user;
1906 $timespent->fk_product = $this->timespent_fk_product;
1907 $timespent->note = $this->timespent_note;
1908 $timespent->invoice_id = $this->timespent_invoiceid;
1909 $timespent->invoice_line_id = $this->timespent_invoicelineid;
1910
1911 dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1912 if ($timespent->update($user) > 0) {
1913 if (!$notrigger) {
1914 // Call trigger
1915 $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1916 if ($result < 0) {
1917 $this->db->rollback();
1918 $ret = -1;
1919 } else {
1920 $ret = 1;
1921 }
1922 // End call triggers
1923 } else {
1924 $ret = 1;
1925 }
1926 } else {
1927 $this->error = $this->db->lasterror();
1928 $this->db->rollback();
1929 $ret = -1;
1930 }
1931
1932 if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) {
1933 if ($this->timespent_old_duration != $this->timespent_duration) {
1934 // Recalculate amount of time spent for task and update denormalized field
1935 $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1936 $sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM " . MAIN_DB_PREFIX . "element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = " . ((int) $this->id) . ")";
1937 if (isset($this->progress)) {
1938 $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1939 }
1940 $sql .= " WHERE rowid = " . ((int) $this->id);
1941
1942 dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1943 if (!$this->db->query($sql)) {
1944 $this->error = $this->db->lasterror();
1945 $this->db->rollback();
1946 $ret = -2;
1947 }
1948 }
1949
1950 // Update hourly rate of this time spent entry, but only if it was not set initially
1951 $res_update = 1;
1952 if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) {
1953 $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1954 if (!empty($resql_thm_user)) {
1955 $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1956 $timespent->thm = $obj_thm_user->thm;
1957 }
1958 $res_update = $timespent->update($user);
1959 }
1960
1961 dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1962 if ($res_update <= 0) {
1963 $this->error = $this->db->lasterror();
1964 $ret = -2;
1965 }
1966 }
1967
1968 if ($ret >= 0) {
1969 $this->db->commit();
1970 }
1971 return $ret;
1972 }
1973
1981 public function delTimeSpent($user, $notrigger = 0)
1982 {
1983 global $conf, $langs;
1984
1985 $error = 0;
1986
1987 if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1988 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1989 $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1990
1991 if ($this->timespent_date < $restrictBefore) {
1992 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1993 $this->errors[] = $this->error;
1994 return -1;
1995 }
1996 }
1997
1998 $this->db->begin();
1999
2000 if (!$notrigger) {
2001 // Call trigger
2002 $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
2003 if ($result < 0) {
2004 $error++;
2005 }
2006 // End call triggers
2007 }
2008
2009 if (!$error) {
2010 $timespent = new TimeSpent($this->db);
2011 $timespent->fetch($this->timespent_id);
2012
2013 $res_del = $timespent->delete($user);
2014
2015 if ($res_del < 0) {
2016 $error++;
2017 $this->errors[] = "Error ".$this->db->lasterror();
2018 }
2019 }
2020
2021 if (!$error) {
2022 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
2023 $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
2024 $sql .= " WHERE rowid = ".((int) $this->id);
2025
2026 dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
2027 if ($this->db->query($sql)) {
2028 $result = 0;
2029 } else {
2030 $this->error = $this->db->lasterror();
2031 $result = -2;
2032 }
2033 }
2034
2035 // Commit or rollback
2036 if ($error) {
2037 foreach ($this->errors as $errmsg) {
2038 dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
2039 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2040 }
2041 $this->db->rollback();
2042 return -1 * $error;
2043 } else {
2044 $this->db->commit();
2045 return 1;
2046 }
2047 }
2048
2063 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)
2064 {
2065 global $langs, $conf;
2066
2067 $error = 0;
2068
2069 //Use 00:00 of today if time is use on task.
2070 $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
2071
2072 $datec = $now;
2073
2074 $clone_task = new Task($this->db);
2075 $origin_task = new Task($this->db);
2076
2077 $clone_task->context['createfromclone'] = 'createfromclone';
2078
2079 $this->db->begin();
2080
2081 // Load source object
2082 $clone_task->fetch($fromid);
2083 $clone_task->fetch_optionals();
2084 //var_dump($clone_task->array_options);exit;
2085
2086 $origin_task->fetch($fromid);
2087
2088 $defaultref = '';
2089 $obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
2090 if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').".php")) {
2091 require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').'.php';
2092 $modTask = new $obj();
2093 $defaultref = $modTask->getNextValue(0, $clone_task);
2094 }
2095
2096 $ori_project_id = $clone_task->fk_project;
2097
2098 $clone_task->id = 0;
2099 $clone_task->ref = $defaultref;
2100 $clone_task->fk_project = $project_id;
2101 $clone_task->fk_task_parent = $parent_task_id;
2102 $clone_task->date_c = $datec;
2103 $clone_task->planned_workload = $origin_task->planned_workload;
2104 $clone_task->rang = $origin_task->rang;
2105 $clone_task->priority = $origin_task->priority;
2106
2107 //Manage Task Date
2108 if ($clone_change_dt) {
2109 $projectstatic = new Project($this->db);
2110 $projectstatic->fetch($ori_project_id);
2111
2112 //Origin project start date
2113 $orign_project_dt_start = $projectstatic->date_start;
2114
2115 //Calculate new task start date with difference between origin proj start date and origin task start date
2116 if (!empty($clone_task->date_start)) {
2117 $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
2118 }
2119
2120 //Calculate new task end date with difference between origin proj end date and origin task end date
2121 if (!empty($clone_task->date_end)) {
2122 $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
2123 }
2124 }
2125
2126 if (!$clone_prog) {
2127 $clone_task->progress = 0;
2128 }
2129
2130 // Create clone
2131 $result = $clone_task->create($user);
2132
2133 // Other options
2134 if ($result < 0) {
2135 $this->error = $clone_task->error;
2136 $error++;
2137 }
2138 // End
2139 if ($error) {
2140 $clone_task_id = 0; // For static tool check
2141 } else {
2142 $clone_task_id = $clone_task->id;
2143 $clone_task_ref = $clone_task->ref;
2144
2145 //Note Update
2146 if (!$clone_note) {
2147 $clone_task->note_private = '';
2148 $clone_task->note_public = '';
2149 } else {
2150 $this->db->begin();
2151 $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
2152 if ($res < 0) {
2153 $this->error .= $clone_task->error;
2154 $error++;
2155 $this->db->rollback();
2156 } else {
2157 $this->db->commit();
2158 }
2159
2160 $this->db->begin();
2161 $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
2162 if ($res < 0) {
2163 $this->error .= $clone_task->error;
2164 $error++;
2165 $this->db->rollback();
2166 } else {
2167 $this->db->commit();
2168 }
2169 }
2170
2171 //Duplicate file
2172 if ($clone_file) {
2173 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2174
2175 //retrieve project origin ref to know folder to copy
2176 $projectstatic = new Project($this->db);
2177 $projectstatic->fetch($ori_project_id);
2178 $ori_project_ref = $projectstatic->ref;
2179
2180 if ($ori_project_id != $project_id) {
2181 $projectstatic->fetch($project_id);
2182 $clone_project_ref = $projectstatic->ref;
2183 } else {
2184 $clone_project_ref = $ori_project_ref;
2185 }
2186
2187 $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
2188 $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
2189
2190 $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
2191 foreach ($filearray as $key => $file) {
2192 if (!file_exists($clone_task_dir)) {
2193 if (dol_mkdir($clone_task_dir) < 0) {
2194 $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
2195 $error++;
2196 }
2197 }
2198
2199 $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
2200 if (is_numeric($rescopy) && $rescopy < 0) {
2201 $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
2202 $error++;
2203 }
2204 }
2205 }
2206
2207 // clone affectation
2208 if ($clone_affectation) {
2209 $origin_task = new Task($this->db);
2210 $origin_task->fetch($fromid);
2211
2212 foreach (array('internal', 'external') as $source) {
2213 $tab = $origin_task->liste_contact(-1, $source);
2214 $num = count($tab);
2215 $i = 0;
2216 while ($i < $num) {
2217 $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2218 if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2219 $langs->load("errors");
2220 $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2221 $error++;
2222 } else {
2223 if ($clone_task->error != '') {
2224 $this->error .= $clone_task->error;
2225 $error++;
2226 }
2227 }
2228 $i++;
2229 }
2230 }
2231 }
2232
2233 if ($clone_time) {
2234 //TODO clone time of affectation
2235 }
2236 }
2237
2238 unset($clone_task->context['createfromclone']);
2239
2240 if (!$error) {
2241 $this->db->commit();
2242 return $clone_task_id;
2243 } else {
2244 $this->db->rollback();
2245 dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
2246 return -1;
2247 }
2248 }
2249
2250
2257 public function getLibStatut($mode = 0)
2258 {
2259 return $this->LibStatut($this->status, $mode);
2260 }
2261
2262 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2270 public function LibStatut($status, $mode = 0)
2271 {
2272 // phpcs:enable
2273 global $langs;
2274
2275 // list of Statut of the task
2276 $this->labelStatus[0] = 'Draft';
2277 $this->labelStatus[1] = 'ToDo';
2278 $this->labelStatus[2] = 'Running';
2279 $this->labelStatus[3] = 'Finish';
2280 $this->labelStatus[4] = 'Transfered';
2281 $this->labelStatusShort[0] = 'Draft';
2282 $this->labelStatusShort[1] = 'ToDo';
2283 $this->labelStatusShort[2] = 'Running';
2284 $this->labelStatusShort[3] = 'Completed';
2285 $this->labelStatusShort[4] = 'Transfered';
2286
2287 if ($mode == 0) {
2288 return $langs->trans($this->labelStatus[$status]);
2289 } elseif ($mode == 1) {
2290 return $langs->trans($this->labelStatusShort[$status]);
2291 } elseif ($mode == 2) {
2292 if ($status == 0) {
2293 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0').' '.$langs->trans($this->labelStatusShort[$status]);
2294 } elseif ($status == 1) {
2295 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1').' '.$langs->trans($this->labelStatusShort[$status]);
2296 } elseif ($status == 2) {
2297 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3').' '.$langs->trans($this->labelStatusShort[$status]);
2298 } elseif ($status == 3) {
2299 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatusShort[$status]);
2300 } elseif ($status == 4) {
2301 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatusShort[$status]);
2302 } elseif ($status == 5) {
2303 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5').' '.$langs->trans($this->labelStatusShort[$status]);
2304 }
2305 } elseif ($mode == 3) {
2306 if ($status == 0) {
2307 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0');
2308 } elseif ($status == 1) {
2309 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1');
2310 } elseif ($status == 2) {
2311 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3');
2312 } elseif ($status == 3) {
2313 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2314 } elseif ($status == 4) {
2315 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2316 } elseif ($status == 5) {
2317 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5');
2318 }
2319 } elseif ($mode == 4) {
2320 if ($status == 0) {
2321 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0').' '.$langs->trans($this->labelStatus[$status]);
2322 } elseif ($status == 1) {
2323 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1').' '.$langs->trans($this->labelStatus[$status]);
2324 } elseif ($status == 2) {
2325 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3').' '.$langs->trans($this->labelStatus[$status]);
2326 } elseif ($status == 3) {
2327 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatus[$status]);
2328 } elseif ($status == 4) {
2329 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatus[$status]);
2330 } elseif ($status == 5) {
2331 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5').' '.$langs->trans($this->labelStatus[$status]);
2332 }
2333 } elseif ($mode == 5) {
2334 /*if ($status==0) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2335 elseif ($status==1) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2336 elseif ($status==2) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2337 elseif ($status==3) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2338 elseif ($status==4) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2339 elseif ($status==5) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2340 */
2341 //else return $this->progress.' %';
2342 return '&nbsp;';
2343 } elseif ($mode == 6) {
2344 /*if ($status==0) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2345 elseif ($status==1) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2346 elseif ($status==2) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2347 elseif ($status==3) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2348 elseif ($status==4) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2349 elseif ($status==5) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2350 */
2351 //else return $this->progress.' %';
2352 return '&nbsp;';
2353 }
2354 return "";
2355 }
2356
2367 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2368 {
2369 $outputlangs->load("projects");
2370
2371 if (!dol_strlen($modele)) {
2372 $modele = 'nodefault';
2373
2374 if (!empty($this->model_pdf)) {
2375 $modele = $this->model_pdf;
2376 } elseif (getDolGlobalString('PROJECT_TASK_ADDON_PDF')) {
2377 $modele = getDolGlobalString('PROJECT_TASK_ADDON_PDF');
2378 }
2379 }
2380
2381 $modelpath = "core/modules/project/task/doc/";
2382
2383 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2384 }
2385
2386
2387 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2394 public function load_board($user)
2395 {
2396 // phpcs:enable
2397 global $conf, $langs;
2398
2399 // For external user, no check is done on company because readability is managed by public status of project and assignment.
2400 //$socid = $user->socid;
2401 $socid = 0;
2402
2403 $projectstatic = new Project($this->db);
2404 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2405
2406 // List of tasks (does not care about permissions. Filtering will be done later)
2407 $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2408 $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2409 $sql .= " t.dateo as date_start, t.datee as date_end";
2410 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2411 //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2412 //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2413 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2414 $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2415 $sql .= " AND p.fk_statut = 1";
2416 $sql .= " AND t.fk_projet = p.rowid";
2417 $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2418 if (!$user->hasRight('projet', 'all', 'lire')) {
2419 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2420 }
2421 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2422 //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2423 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2424 // if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2425
2426 //print $sql;
2427 $resql = $this->db->query($sql);
2428 if ($resql) {
2429 $task_static = new Task($this->db);
2430
2431 $response = new WorkboardResponse();
2432 $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2433 $response->label = $langs->trans("OpenedTasks");
2434 if ($user->hasRight("projet", "all", "lire")) {
2435 $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2436 } else {
2437 $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2438 }
2439 $response->img = img_object('', "task");
2440
2441 // This assignment in condition is not a bug. It allows walking the results.
2442 while ($obj = $this->db->fetch_object($resql)) {
2443 $response->nbtodo++;
2444
2445 $task_static->projectstatus = $obj->projectstatus;
2446 $task_static->progress = $obj->progress;
2447 $task_static->fk_statut = $obj->status;
2448 $task_static->status = $obj->status;
2449 $task_static->date_start = $this->db->jdate($obj->date_start);
2450 $task_static->date_end = $this->db->jdate($obj->date_end);
2451
2452 if ($task_static->hasDelay()) {
2453 $response->nbtodolate++;
2454 }
2455 }
2456
2457 return $response;
2458 } else {
2459 $this->error = $this->db->error();
2460 return -1;
2461 }
2462 }
2463
2464
2470 public function loadStateBoard()
2471 {
2472 global $user;
2473
2474 $mine = 0;
2475 $socid = $user->socid;
2476
2477 $projectstatic = new Project($this->db);
2478 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2479
2480 // List of tasks (does not care about permissions. Filtering will be done later)
2481 $sql = "SELECT count(p.rowid) as nb";
2482 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2483 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2484 if (!$user->hasRight('societe', 'client', 'voir')) {
2485 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2486 }
2487 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2488 $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2489 $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2490 if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
2491 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2492 }
2493 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2494 //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2495 if ($socid) {
2496 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2497 }
2498 if (!$user->hasRight('societe', 'client', 'voir')) {
2499 $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2500 }
2501
2502 $resql = $this->db->query($sql);
2503 if ($resql) {
2504 // This assignment in condition is not a bug. It allows walking the results.
2505 while ($obj = $this->db->fetch_object($resql)) {
2506 $this->nb["tasks"] = $obj->nb;
2507 }
2508 $this->db->free($resql);
2509 return 1;
2510 } else {
2511 dol_print_error($this->db);
2512 $this->error = $this->db->error();
2513 return -1;
2514 }
2515 }
2516
2522 public function hasDelay()
2523 {
2524 global $conf;
2525
2526 if (!($this->progress >= 0 && $this->progress < 100)) {
2527 return false;
2528 }
2529
2530 $now = dol_now();
2531
2532 $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
2533
2534 return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2535 }
2536
2544 public function getKanbanView($option = '', $arraydata = null)
2545 {
2546 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2547
2548 $return = '<div class="box-flex-item box-flex-grow-zero">';
2549 $return .= '<div class="info-box info-box-sm info-box-kanban">';
2550 $return .= '<span class="info-box-icon bg-infobox-action">';
2551 $return .= img_picto('', $this->picto);
2552 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2553 $return .= '</span>';
2554 $return .= '<div class="info-box-content">';
2555 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2556 if ($selected >= 0) {
2557 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2558 }
2559 if (!empty($arraydata['projectlink'])) {
2560 //$tmpproject = $arraydata['project'];
2561 //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
2562 $return .= '<br><span class="info-box-status ">'.$arraydata['projectlink'].'</span>';
2563 }
2564 if (property_exists($this, 'budget_amount')) {
2565 //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
2566 }
2567 if (property_exists($this, 'duration_effective')) {
2568 $return .= '<br><div class="info-box-label progressinkanban paddingtop">'.getTaskProgressView($this, false, true).'</div>';
2569 }
2570 $return .= '</div>';
2571 $return .= '</div>';
2572 $return .= '</div>';
2573
2574 return $return;
2575 }
2576
2584 public function mergeTask($task_origin_id)
2585 {
2586 global $langs, $hookmanager, $user, $action;
2587
2588 $error = 0;
2589 $task_origin = new Task($this->db); // The thirdparty that we will delete
2590
2591 dol_syslog("mergeTask merge task id=".$task_origin_id." (will be deleted) into the task id=".$this->id);
2592
2593 $langs->load('error');
2594
2595 if (!$error && $task_origin->fetch($task_origin_id) < 1) {
2596 $this->error = $langs->trans('ErrorRecordNotFound');
2597 $error++;
2598 }
2599
2600 if (!$error) {
2601 $this->db->begin();
2602
2603 // Recopy some data
2604 $listofproperties = array(
2605 'label', 'description', 'duration_effective', 'planned_workload', 'datec', 'date_start',
2606 'date_end', 'fk_user_creat', 'fk_user_valid', 'fk_statut', 'progress', 'budget_amount',
2607 'priority', 'rang', 'fk_projet', 'fk_task_parent'
2608 );
2609 foreach ($listofproperties as $property) {
2610 if (empty($this->$property)) {
2611 $this->$property = $task_origin->$property;
2612 }
2613 }
2614
2615 // Concat some data
2616 $listofproperties = array(
2617 'note_public', 'note_private'
2618 );
2619 foreach ($listofproperties as $property) {
2620 $this->$property = dol_concatdesc($this->$property, $task_origin->$property);
2621 }
2622
2623 // Merge extrafields
2624 if (is_array($task_origin->array_options)) {
2625 foreach ($task_origin->array_options as $key => $val) {
2626 if (empty($this->array_options[$key])) {
2627 $this->array_options[$key] = $val;
2628 }
2629 }
2630 }
2631
2632 // Update
2633 $result = $this->update($user);
2634
2635 if ($result < 0) {
2636 $error++;
2637 }
2638
2639 // Merge time spent
2640 if (!$error) {
2641 $result = $this->mergeTimeSpentTask($task_origin_id, $this->id);
2642 if ($result != true) {
2643 $error++;
2644 }
2645 }
2646
2647 // Merge contacts
2648 if (!$error) {
2649 $result = $this->mergeContactTask($task_origin_id, $this->id);
2650 if ($result != true) {
2651 $error++;
2652 }
2653 }
2654
2655 // External modules should update their ones too
2656 if (!$error) {
2657 $parameters = array('task_origin' => $task_origin->id, 'task_dest' => $this->id);
2658 $reshook = $hookmanager->executeHooks('replaceThirdparty', $parameters, $this, $action);
2659
2660 if ($reshook < 0) {
2661 $this->error = $hookmanager->error;
2662 $this->errors = $hookmanager->errors;
2663 $error++;
2664 }
2665 }
2666
2667
2668 if (!$error) {
2669 $this->context = array('merge' => 1, 'mergefromid' => $task_origin->id, 'mergefromref' => $task_origin->ref);
2670
2671 // Call trigger
2672 $result = $this->call_trigger('TASK_MODIFY', $user);
2673 if ($result < 0) {
2674 $error++;
2675 }
2676 // End call triggers
2677 }
2678
2679 if (!$error) {
2680 // We finally remove the old task
2681 if ($task_origin->delete($user) < 1) {
2682 $this->error = $task_origin->error;
2683 $this->errors = $task_origin->errors;
2684 $error++;
2685 }
2686 }
2687
2688 if (!$error) {
2689 $this->db->commit();
2690 return 0;
2691 } else {
2692 $langs->load("errors");
2693 $this->error = $langs->trans('ErrorsTaskMerge');
2694 $this->db->rollback();
2695 return -1;
2696 }
2697 }
2698
2699 return -1;
2700 }
2701}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:636
$object ref
Definition info.php:79
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others (by children).
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage projects.
const STATUS_VALIDATED
Open/Validated status.
Class to manage tasks.
create($user, $notrigger=0)
Create into database.
fetch($id, $ref='', $loadparentdata=0)
Load object in memory from database.
getLibStatut($mode=0)
Return status label of object.
mergeContactTask($origin_id, $dest_id)
Merge contact of tasks.
const STATUS_TRANSFERRED
Transferred status.
getSumOfAmount($fuser='', $dates='', $datee='')
Calculate quantity and value of time consumed using the thm (hourly amount value of work for user ent...
mergeTask($task_origin_id)
Merge a task with another one, deleting the given task.
__construct($db)
Constructor.
fetchTimeSpentOnTask($morewherefilter='')
Fetch records of time spent of this task.
const STATUS_VALIDATED
Validated status (To do).
hasTimeSpent()
Return nb of time spent.
getNomUrl($withpicto=0, $option='', $mode='task', $addlabel=0, $sep=' - ', $notooltip=0, $save_lastsearch_value=-1)
Return clicable name (with picto eventually)
getListContactId($source='internal')
Return list of id of contacts of task.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
delTimeSpent($user, $notrigger=0)
Delete time spent.
mergeTimeSpentTask($origin_id, $dest_id)
Merge time spent of tasks.
const STATUS_DRAFT
Draft status.
fetchAllTimeSpent(User $userobj, $morewherefilter='')
Load all records of time spent.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getUserRolesForProjectsOrTasks($userp, $usert, $projectid='', $taskid=0, $filteronprojstatus=-1)
Return list of roles for a user for each projects or each tasks (or a particular project or a particu...
update($user=null, $notrigger=0)
Update database.
getSummaryOfTimeSpent($userobj=null, $morewherefilter='')
Calculate total of time spent for task.
getTooltipContentArray($params)
getTooltipContentArray
const STATUS_CANCELED
status canceled
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF.
fetchTimeSpent($id)
Load properties of timespent of a task from the time spent ID.
updateTimeSpent($user, $notrigger=0)
Update time spent.
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)
Load an object from its id and create a new one in database.
loadStateBoard()
Load indicators this->nb for state board.
initAsSpecimen()
Initialise an instance with random values.
getTasksArray($usert=null, $userp=null, $projectid=0, $socid=0, $mode=0, $filteronproj='', $filteronprojstatus='-1', $morewherefilter='', $filteronprojuser=0, $filterontaskuser=0, $extrafields=null, $includebilltime=0, $search_array_options=array(), $loadextras=0, $loadRoleMode=1, $sortfield='', $sortorder='')
Return list of tasks for all projects or for one particular project Sort order is on project,...
addTimeSpent($user, $notrigger=0)
Add time spent.
hasDelay()
Is the task delayed?
LibStatut($status, $mode=0)
Return status label for an object.
const STATUS_CLOSED
Finished status.
hasChildren()
Return nb of children.
Class for TimeSpent.
Class to manage Dolibarr users.
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:124
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
dol_move_dir($srcdir, $destdir, $overwriteifexists=1, $indexdatabase=1, $renamedircontent=1)
Move a directory into another name.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)