dolibarr 21.0.0-alpha
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 * Copyright (C) 2024 Vincent de Grandpré <vincent@de-grandpre.quebec>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
32require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
33require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
34require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
35require_once DOL_DOCUMENT_ROOT.'/core/class/timespent.class.php';
36
37
42{
46 public $element = 'project_task';
47
51 public $table_element = 'projet_task';
52
56 public $fk_element = 'fk_element';
57
61 public $picto = 'projecttask';
62
66 protected $childtables = array(
67 'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task')
68 );
69
73 public $fk_task_parent = 0;
74
78 public $label;
79
83 public $description;
84
88 public $duration_effective;
89
93 public $planned_workload;
94
99 public $date_c;
100
104 public $progress;
105
111 public $dateo;
112
117 public $date_start;
118
124 public $datee;
125
130 public $date_end;
131
137 public $fk_statut;
138
142 public $status;
143
147 public $priority;
148
152 public $fk_user_creat;
153
157 public $fk_user_valid;
158
162 public $rang;
163
167 public $timespent_min_date;
171 public $timespent_max_date;
175 public $timespent_total_duration;
179 public $timespent_total_amount;
183 public $timespent_nblinesnull;
187 public $timespent_nblines;
188 // For detail of lines of timespent record, there is the property ->lines in common
189
190 // Var used to call method addTimeSpent(). Bad practice.
194 public $timespent_id;
198 public $timespent_duration;
202 public $timespent_old_duration;
206 public $timespent_date;
210 public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
214 public $timespent_withhour; // 1 = we entered also start hours for timesheet line
218 public $timespent_fk_user;
222 public $timespent_thm;
226 public $timespent_note;
230 public $timespent_fk_product;
234 public $timespent_invoiceid;
238 public $timespent_invoicelineid;
239
240 public $comments = array();
241
242 // Properties calculated from sum of llx_element_time linked to task
246 public $tobill;
247
251 public $billed;
252
253 // Properties to store project information
257 public $projectref;
258
262 public $projectstatus;
263
267 public $projectlabel;
268
272 public $opp_amount;
273
277 public $opp_percent;
278
282 public $fk_opp_status;
283
287 public $usage_bill_time;
288
292 public $public;
293
297 public $array_options_project;
298
299 // Properties to store thirdparty of project information
300
306 public $socid;
307
311 public $thirdparty_id;
312
316 public $thirdparty_name;
317
321 public $thirdparty_email;
322
323 // store parent ref and position
327 public $task_parent_ref;
328
332 public $task_parent_position;
333
338 public $billable = 1;
339
343 public $budget_amount;
344
348 public $project_budget_amount;
349
353 const STATUS_DRAFT = 0;
354
359
363 const STATUS_CLOSED = 3;
364
369
374
375
381 public function __construct($db)
382 {
383 $this->db = $db;
384 }
385
386
394 public function create($user, $notrigger = 0)
395 {
396 global $conf, $langs;
397
398 //For the date
399 $now = dol_now();
400
401 $error = 0;
402
403 // Clean parameters
404 $this->label = trim($this->label);
405 $this->description = trim($this->description);
406 $this->note_public = trim($this->note_public);
407 $this->note_private = trim($this->note_private);
408
409 if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
410 $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
411 return -1;
412 }
413
414 // Insert request
415 $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
416 $sql .= "entity";
417 $sql .= ", fk_projet";
418 $sql .= ", ref";
419 $sql .= ", fk_task_parent";
420 $sql .= ", label";
421 $sql .= ", description";
422 $sql .= ", note_public";
423 $sql .= ", note_private";
424 $sql .= ", datec";
425 $sql .= ", fk_user_creat";
426 $sql .= ", dateo";
427 $sql .= ", datee";
428 $sql .= ", planned_workload";
429 $sql .= ", progress";
430 $sql .= ", budget_amount";
431 $sql .= ", priority";
432 $sql .= ", billable";
433 $sql .= ") VALUES (";
434 $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
435 $sql .= ", ".((int) $this->fk_project);
436 $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
437 $sql .= ", ".((int) $this->fk_task_parent);
438 $sql .= ", '".$this->db->escape($this->label)."'";
439 $sql .= ", '".$this->db->escape($this->description)."'";
440 $sql .= ", '".$this->db->escape($this->note_public)."'";
441 $sql .= ", '".$this->db->escape($this->note_private)."'";
442 $sql .= ", '".$this->db->idate($now)."'";
443 $sql .= ", ".((int) $user->id);
444 $sql .= ", ".(isDolTms($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : 'null');
445 $sql .= ", ".(isDolTms($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : 'null');
446 $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
447 $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
448 $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
449 $sql .= ", ".(($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null');
450 $sql .= ", ".((int) $this->billable);
451 $sql .= ")";
452
453 $this->db->begin();
454
455 dol_syslog(get_class($this)."::create", LOG_DEBUG);
456 $resql = $this->db->query($sql);
457 if (!$resql) {
458 $error++;
459 $this->errors[] = "Error ".$this->db->lasterror();
460 }
461
462 if (!$error) {
463 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
464 // Update extrafield
465 $result = $this->insertExtraFields();
466 if ($result < 0) {
467 $error++;
468 }
469 }
470
471 if (!$error) {
472 if (!$notrigger) {
473 // Call trigger
474 $result = $this->call_trigger('TASK_CREATE', $user);
475 if ($result < 0) {
476 $error++;
477 }
478 // End call triggers
479 }
480 }
481
482 // Commit or rollback
483 if ($error) {
484 foreach ($this->errors as $errmsg) {
485 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
486 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
487 }
488 $this->db->rollback();
489 return -1 * $error;
490 } else {
491 $this->db->commit();
492 return $this->id;
493 }
494 }
495
496
505 public function fetch($id, $ref = '', $loadparentdata = 0)
506 {
507 $sql = "SELECT";
508 $sql .= " t.rowid,";
509 $sql .= " t.ref,";
510 $sql .= " t.entity,";
511 $sql .= " t.fk_projet as fk_project,";
512 $sql .= " t.fk_task_parent,";
513 $sql .= " t.label,";
514 $sql .= " t.description,";
515 $sql .= " t.duration_effective,";
516 $sql .= " t.planned_workload,";
517 $sql .= " t.datec,";
518 $sql .= " t.dateo as date_start,";
519 $sql .= " t.datee as date_end,";
520 $sql .= " t.fk_user_creat,";
521 $sql .= " t.fk_user_valid,";
522 $sql .= " t.fk_statut as status,";
523 $sql .= " t.progress,";
524 $sql .= " t.budget_amount,";
525 $sql .= " t.priority,";
526 $sql .= " t.note_private,";
527 $sql .= " t.note_public,";
528 $sql .= " t.rang,";
529 $sql .= " t.billable";
530 if (!empty($loadparentdata)) {
531 $sql .= ", t2.ref as task_parent_ref";
532 $sql .= ", t2.rang as task_parent_position";
533 }
534 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
535 if (!empty($loadparentdata)) {
536 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
537 }
538 $sql .= " WHERE ";
539 if (!empty($ref)) {
540 $sql .= "entity IN (".getEntity('project').")";
541 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
542 } else {
543 $sql .= "t.rowid = ".((int) $id);
544 }
545
546 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
547 $resql = $this->db->query($sql);
548 if ($resql) {
549 $num_rows = $this->db->num_rows($resql);
550
551 if ($num_rows) {
552 $obj = $this->db->fetch_object($resql);
553
554 $this->id = $obj->rowid;
555 $this->ref = $obj->ref;
556 $this->entity = $obj->entity;
557 $this->fk_project = $obj->fk_project;
558 $this->fk_task_parent = $obj->fk_task_parent;
559 $this->label = $obj->label;
560 $this->description = $obj->description;
561 $this->duration_effective = $obj->duration_effective;
562 $this->planned_workload = $obj->planned_workload;
563 $this->date_c = $this->db->jdate($obj->datec);
564 $this->date_start = $this->db->jdate($obj->date_start);
565 $this->date_end = $this->db->jdate($obj->date_end);
566 $this->fk_user_creat = $obj->fk_user_creat;
567 $this->fk_user_valid = $obj->fk_user_valid;
568 $this->fk_statut = $obj->status;
569 $this->status = $obj->status;
570 $this->progress = $obj->progress;
571 $this->budget_amount = $obj->budget_amount;
572 $this->priority = $obj->priority;
573 $this->note_private = $obj->note_private;
574 $this->note_public = $obj->note_public;
575 $this->rang = $obj->rang;
576
577 if (!empty($loadparentdata)) {
578 $this->task_parent_ref = $obj->task_parent_ref;
579 $this->task_parent_position = $obj->task_parent_position;
580 }
581 $this->billable = $obj->billable;
582
583 // Retrieve all extrafield
584 $this->fetch_optionals();
585 }
586
587 $this->db->free($resql);
588
589 if ($num_rows) {
590 return 1;
591 } else {
592 return 0;
593 }
594 } else {
595 $this->error = "Error ".$this->db->lasterror();
596 return -1;
597 }
598 }
599
600
608 public function update($user = null, $notrigger = 0)
609 {
610 global $conf, $langs;
611 $error = 0;
612
613 // Clean parameters
614 if (isset($this->fk_project)) {
615 $this->fk_project = (int) $this->fk_project;
616 }
617 if (isset($this->ref)) {
618 $this->ref = trim($this->ref);
619 }
620 if (isset($this->fk_task_parent)) {
621 $this->fk_task_parent = (int) $this->fk_task_parent;
622 }
623 if (isset($this->label)) {
624 $this->label = trim($this->label);
625 }
626 if (isset($this->description)) {
627 $this->description = trim($this->description);
628 }
629 if (isset($this->note_public)) {
630 $this->note_public = trim($this->note_public);
631 }
632 if (isset($this->note_private)) {
633 $this->note_private = trim($this->note_private);
634 }
635 if (isset($this->duration_effective)) {
636 $this->duration_effective = trim($this->duration_effective);
637 }
638 if (isset($this->planned_workload)) {
639 $this->planned_workload = trim($this->planned_workload);
640 }
641 if (isset($this->budget_amount)) {
642 $this->budget_amount = (float) $this->budget_amount;
643 }
644
645 if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
646 $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
647 return -1;
648 }
649
650 // Check parameters
651 // Put here code to add control on parameters values
652
653 // Update request
654 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
655 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
656 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
657 $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
658 $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
659 $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
660 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
661 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
662 $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
663 $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
664 $sql .= " dateo=".(isDolTms($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
665 $sql .= " datee=".(isDolTms($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
666 $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
667 $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
668 $sql .= " rang=".((!empty($this->rang)) ? ((int) $this->rang) : "0").",";
669 $sql .= " priority=".((!empty($this->priority)) ? ((int) $this->priority) : "0").",";
670 $sql .= " billable=".((int) $this->billable);
671 $sql .= " WHERE rowid=".((int) $this->id);
672
673 $this->db->begin();
674
675 dol_syslog(get_class($this)."::update", LOG_DEBUG);
676 $resql = $this->db->query($sql);
677 if (!$resql) {
678 $error++;
679 $this->errors[] = "Error ".$this->db->lasterror();
680 }
681
682 // Update extrafield
683 if (!$error) {
684 $result = $this->insertExtraFields();
685 if ($result < 0) {
686 $error++;
687 }
688 }
689
690 if (!$error && getDolGlobalString('PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE')) {
691 // Close the parent project if it is open (validated) and its tasks are 100% completed
692 $project = new Project($this->db);
693 if ($project->fetch($this->fk_project) > 0) {
694 if ($project->statut == Project::STATUS_VALIDATED) {
695 $project->getLinesArray(null); // this method does not return <= 0 if fails
696 $projectCompleted = array_reduce(
697 $project->lines,
703 static function ($allTasksCompleted, $task) {
704 return $allTasksCompleted && $task->progress >= 100;
705 },
706 1
707 );
708 if ($projectCompleted) {
709 if ($project->setClose($user) <= 0) {
710 $error++;
711 }
712 }
713 }
714 } else {
715 $error++;
716 }
717 if ($error) {
718 $this->errors[] = $project->error;
719 }
720 }
721
722 if (!$error) {
723 if (!$notrigger) {
724 // Call trigger
725 $result = $this->call_trigger('TASK_MODIFY', $user);
726 if ($result < 0) {
727 $error++;
728 }
729 // End call triggers
730 }
731 }
732
733 if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
734 // We remove directory
735 if ($conf->project->dir_output) {
736 $project = new Project($this->db);
737 $project->fetch($this->fk_project);
738
739 $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
740 $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
741 if (file_exists($olddir)) {
742 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
743 $res = dol_move_dir($olddir, $newdir);
744 if (!$res) {
745 $langs->load("errors");
746 $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
747 $error++;
748 }
749 }
750 }
751 }
752
753 // Commit or rollback
754 if ($error) {
755 foreach ($this->errors as $errmsg) {
756 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
757 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
758 }
759 $this->db->rollback();
760 return -1 * $error;
761 } else {
762 $this->db->commit();
763 return 1;
764 }
765 }
766
767
775 public function delete($user, $notrigger = 0)
776 {
777 global $conf;
778 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
779
780 $error = 0;
781
782 $this->db->begin();
783
784 if ($this->hasChildren() > 0) {
785 dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
786 $this->error = 'ErrorRecordHasSubTasks';
787 $this->db->rollback();
788 return 0;
789 }
790
791 $objectisused = $this->isObjectUsed($this->id);
792 if (!empty($objectisused)) {
793 dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
794 $this->error = 'ErrorRecordHasChildren';
795 $this->db->rollback();
796 return 0;
797 }
798
799 if (!$error) {
800 // Delete linked contacts
801 $res = $this->delete_linked_contact();
802 if ($res < 0) {
803 $this->error = 'ErrorFailToDeleteLinkedContact';
804 //$error++;
805 $this->db->rollback();
806 return 0;
807 }
808 }
809
810 if (!$error) {
811 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_time";
812 $sql .= " WHERE fk_element = ".((int) $this->id)." AND elementtype = 'task'";
813
814 $resql = $this->db->query($sql);
815 if (!$resql) {
816 $error++;
817 $this->errors[] = "Error ".$this->db->lasterror();
818 }
819 }
820
821 if (!$error) {
822 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
823 $sql .= " WHERE fk_object = ".((int) $this->id);
824
825 $resql = $this->db->query($sql);
826 if (!$resql) {
827 $error++;
828 $this->errors[] = "Error ".$this->db->lasterror();
829 }
830 }
831
832 if (!$error) {
833 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
834 $sql .= " WHERE rowid=".((int) $this->id);
835
836 $resql = $this->db->query($sql);
837 if (!$resql) {
838 $error++;
839 $this->errors[] = "Error ".$this->db->lasterror();
840 }
841 }
842
843 if (!$error) {
844 if (!$notrigger) {
845 // Call trigger
846 $result = $this->call_trigger('TASK_DELETE', $user);
847 if ($result < 0) {
848 $error++;
849 }
850 // End call triggers
851 }
852 }
853
854 // Commit or rollback
855 if ($error) {
856 foreach ($this->errors as $errmsg) {
857 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
858 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
859 }
860 $this->db->rollback();
861 return -1 * $error;
862 } else {
863 //Delete associated link file
864 if ($conf->project->dir_output) {
865 $projectstatic = new Project($this->db);
866 $projectstatic->fetch($this->fk_project);
867
868 $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName((string) $this->id);
869 dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
870 if (file_exists($dir)) {
871 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
872 $res = @dol_delete_dir_recursive($dir);
873 if (!$res) {
874 $this->error = 'ErrorFailToDeleteDir';
875 $this->db->rollback();
876 return 0;
877 }
878 }
879 }
880
881 $this->db->commit();
882
883 return 1;
884 }
885 }
886
892 public function hasChildren()
893 {
894 $error = 0;
895 $ret = 0;
896
897 $sql = "SELECT COUNT(*) as nb";
898 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
899 $sql .= " WHERE fk_task_parent = ".((int) $this->id);
900
901 dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
902 $resql = $this->db->query($sql);
903 if (!$resql) {
904 $error++;
905 $this->errors[] = "Error ".$this->db->lasterror();
906 } else {
907 $obj = $this->db->fetch_object($resql);
908 if ($obj) {
909 $ret = $obj->nb;
910 }
911 $this->db->free($resql);
912 }
913
914 if (!$error) {
915 return $ret;
916 } else {
917 return -1;
918 }
919 }
920
926 public function hasTimeSpent()
927 {
928 $error = 0;
929 $ret = 0;
930
931 $sql = "SELECT COUNT(*) as nb";
932 $sql .= " FROM ".MAIN_DB_PREFIX."element_time";
933 $sql .= " WHERE fk_element = ".((int) $this->id);
934 $sql .= " AND elementtype = 'task'";
935
936 dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
937 $resql = $this->db->query($sql);
938 if (!$resql) {
939 $error++;
940 $this->errors[] = "Error ".$this->db->lasterror();
941 } else {
942 $obj = $this->db->fetch_object($resql);
943 if ($obj) {
944 $ret = $obj->nb;
945 }
946 $this->db->free($resql);
947 }
948
949 if (!$error) {
950 return $ret;
951 } else {
952 return -1;
953 }
954 }
955
956
963 public function getTooltipContentArray($params)
964 {
965 global $langs;
966
967 $langs->load('projects');
968
969 $datas = [];
970 $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
971 if (!empty($this->ref)) {
972 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
973 }
974 if (!empty($this->label)) {
975 $datas['label'] = '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
976 }
977 if ($this->date_start || $this->date_end) {
978 $datas['range'] = "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
979 }
980
981 return $datas;
982 }
983
996 public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
997 {
998 global $action, $conf, $hookmanager, $langs;
999
1000 if (!empty($conf->dol_no_mouse_hover)) {
1001 $notooltip = 1; // Force disable tooltips
1002 }
1003
1004 $result = '';
1005 $params = [
1006 'id' => $this->id,
1007 'objecttype' => $this->element,
1008 ];
1009 $classfortooltip = 'classfortooltip';
1010 $dataparams = '';
1011 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1012 $classfortooltip = 'classforajaxtooltip';
1013 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1014 $label = '';
1015 } else {
1016 $label = implode($this->getTooltipContentArray($params));
1017 }
1018
1019 $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
1020 // Add param to save lastsearch_values or not
1021 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1022 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1023 $add_save_lastsearch_values = 1;
1024 }
1025 if ($add_save_lastsearch_values) {
1026 $url .= '&save_lastsearch_values=1';
1027 }
1028
1029 $linkclose = '';
1030 if (empty($notooltip)) {
1031 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1032 $label = $langs->trans("ShowTask");
1033 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1034 }
1035 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1036 $linkclose .= $dataparams.' class="'.$classfortooltip.' nowraponall"';
1037 } else {
1038 $linkclose .= ' class="nowraponall"';
1039 }
1040
1041 $linkstart = '<a href="'.$url.'"';
1042 $linkstart .= $linkclose.'>';
1043 $linkend = '</a>';
1044
1045 $picto = 'projecttask';
1046
1047 $result .= $linkstart;
1048 if ($withpicto) {
1049 $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
1050 }
1051 if ($withpicto != 2) {
1052 $result .= $this->ref;
1053 }
1054 $result .= $linkend;
1055 if ($withpicto != 2) {
1056 $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1057 }
1058
1059 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1060 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1061 if ($reshook > 0) {
1062 $result = $hookmanager->resPrint;
1063 } else {
1064 $result .= $hookmanager->resPrint;
1065 }
1066
1067 return $result;
1068 }
1069
1077 public function initAsSpecimen()
1078 {
1079 global $user;
1080
1081 $this->id = 0;
1082
1083 $this->fk_project = 0;
1084 $this->ref = 'TK01';
1085 $this->fk_task_parent = 0;
1086 $this->label = 'Specimen task TK01';
1087 $this->duration_effective = '';
1088 $this->fk_user_creat = $user->id;
1089 $this->progress = 25;
1090 $this->status = 0;
1091 $this->priority = 0;
1092 $this->note_private = 'This is a specimen private note';
1093 $this->note_public = 'This is a specimen public note';
1094 $this->billable = 1;
1095
1096 return 1;
1097 }
1098
1122 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 = '')
1123 {
1124 global $hookmanager;
1125
1126 $tasks = array();
1127
1128 //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
1129
1130 // List of tasks (does not care about permissions. Filtering will be done later)
1131 $sql = "SELECT ";
1132 if ($filteronprojuser > 0 || $filterontaskuser > 0) {
1133 $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
1134 }
1135 $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
1136 $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,";
1137 $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
1138 $sql .= " t.budget_amount, t.billable,";
1139 $sql .= " t.note_public, t.note_private,";
1140 $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
1141 $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
1142 if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
1143 if (!empty($extrafields->attributes['projet']['label'])) {
1144 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1145 $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
1146 }
1147 }
1148 if (!empty($extrafields->attributes['projet_task']['label'])) {
1149 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1150 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
1151 }
1152 }
1153 }
1154 if ($includebilltime) {
1155 $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";
1156 }
1157
1158 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1159 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1160 if ($loadextras) {
1161 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
1162 }
1163
1164 if ($mode == 0) {
1165 if ($filteronprojuser > 0) {
1166 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1167 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1168 }
1169 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1170 if ($loadextras) {
1171 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
1172 }
1173 if ($includebilltime) {
1174 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1175 }
1176 if ($filterontaskuser > 0) {
1177 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1178 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1179 }
1180 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1181 $sql .= " AND t.fk_projet = p.rowid";
1182 } elseif ($mode == 1) {
1183 if ($filteronprojuser > 0) {
1184 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1185 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1186 }
1187 if ($filterontaskuser > 0) {
1188 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1189 if ($includebilltime) {
1190 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1191 }
1192 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1193 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1194 } else {
1195 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
1196 if ($includebilltime) {
1197 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
1198 }
1199 }
1200 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1201 } else {
1202 return 'BadValueForParameterMode';
1203 }
1204
1205 if ($filteronprojuser > 0) {
1206 $sql .= " AND p.rowid = ec.element_id";
1207 $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1208 $sql .= " AND ctc.element = 'project'";
1209 $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
1210 $sql .= " AND ec.statut = 4";
1211 $sql .= " AND ctc.source = 'internal'";
1212 }
1213 if ($filterontaskuser > 0) {
1214 $sql .= " AND t.fk_projet = p.rowid";
1215 $sql .= " AND p.rowid = ec2.element_id";
1216 $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
1217 $sql .= " AND ctc2.element = 'project_task'";
1218 $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
1219 $sql .= " AND ec2.statut = 4";
1220 $sql .= " AND ctc2.source = 'internal'";
1221 }
1222 if ($socid) {
1223 $sql .= " AND p.fk_soc = ".((int) $socid);
1224 }
1225 if ($projectid) {
1226 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1227 }
1228 if ($filteronproj) {
1229 $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
1230 }
1231 if ($filteronprojstatus && (int) $filteronprojstatus != '-1') {
1232 $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
1233 }
1234 if ($morewherefilter) {
1235 $sql .= $morewherefilter;
1236 }
1237
1238 // Add where from extra fields
1239 $extrafieldsobjectkey = 'projet_task';
1240 $extrafieldsobjectprefix = 'efpt.';
1241 global $db, $conf; // needed for extrafields_list_search_sql.tpl
1242 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
1243
1244 // Add where from hooks
1245 $parameters = array();
1246 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
1247 $sql .= $hookmanager->resPrint;
1248 if ($includebilltime) {
1249 $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
1250 $sql .= " t.datec, t.dateo, t.datee, t.tms,";
1251 $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
1252 $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
1253 $sql .= " t.budget_amount, t.billable,";
1254 $sql .= " t.note_public, t.note_private,";
1255 $sql .= " s.rowid, s.nom, s.email,";
1256 $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
1257 if ($loadextras) {
1258 if (!empty($extrafields->attributes['projet']['label'])) {
1259 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1260 $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
1261 }
1262 }
1263 if (!empty($extrafields->attributes['projet_task']['label'])) {
1264 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1265 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
1266 }
1267 }
1268 }
1269 }
1270
1271 if ($sortfield && $sortorder) {
1272 $sql .= $this->db->order($sortfield, $sortorder);
1273 } else {
1274 $sql .= " ORDER BY p.ref, t.rang, t.dateo";
1275 }
1276
1277 //print $sql;exit;
1278 dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
1279 $resql = $this->db->query($sql);
1280 if ($resql) {
1281 $num = $this->db->num_rows($resql);
1282 $i = 0;
1283 // Loop on each record found, so each couple (project id, task id)
1284 while ($i < $num) {
1285 $error = 0;
1286
1287 $obj = $this->db->fetch_object($resql);
1288
1289 if ($loadRoleMode) {
1290 if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
1291 if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
1292 $error++;
1293 }
1294 }
1295 if (is_object($usert)) { // If we ask a filter on a user affected to a task
1296 if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
1297 $error++;
1298 }
1299 }
1300 }
1301
1302 if (!$error) {
1303 $tasks[$i] = new Task($this->db);
1304 $tasks[$i]->id = $obj->taskid;
1305 $tasks[$i]->ref = $obj->taskref;
1306 $tasks[$i]->fk_project = $obj->projectid;
1307
1308 // Data from project
1309 $tasks[$i]->projectref = $obj->ref;
1310 $tasks[$i]->projectlabel = $obj->plabel;
1311 $tasks[$i]->projectstatus = $obj->projectstatus;
1312 $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1313 $tasks[$i]->opp_amount = $obj->opp_amount;
1314 $tasks[$i]->opp_percent = $obj->opp_percent;
1315 $tasks[$i]->budget_amount = $obj->budget_amount;
1316 $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1317 $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1318
1319 $tasks[$i]->label = $obj->label;
1320 $tasks[$i]->description = $obj->description;
1321
1322 $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1323 $tasks[$i]->note_public = $obj->note_public;
1324 $tasks[$i]->note_private = $obj->note_private;
1325 $tasks[$i]->duration_effective = $obj->duration_effective;
1326 $tasks[$i]->planned_workload = $obj->planned_workload;
1327
1328 if ($includebilltime) {
1329 // Data summed from element_time linked to task
1330 $tasks[$i]->tobill = $obj->tobill;
1331 $tasks[$i]->billed = $obj->billed;
1332 }
1333
1334 $tasks[$i]->progress = $obj->progress;
1335 $tasks[$i]->fk_statut = $obj->status;
1336 $tasks[$i]->status = $obj->status;
1337 $tasks[$i]->public = $obj->public;
1338 $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1339 $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1340 $tasks[$i]->rang = $obj->rang;
1341 $tasks[$i]->priority = $obj->priority;
1342
1343 $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1344 $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1345 $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1346 $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1347
1348 $tasks[$i]->billable = $obj->billable;
1349
1350 if ($loadextras) {
1351 if (!empty($extrafields->attributes['projet']['label'])) {
1352 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1353 if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1354 $tmpvar = 'options_'.$key;
1355 $tasks[$i]->array_options_project['options_'.$key] = $obj->$tmpvar;
1356 }
1357 }
1358 }
1359 }
1360
1361 if ($loadextras) {
1362 $tasks[$i]->fetch_optionals();
1363 }
1364 }
1365
1366 $i++;
1367 }
1368 $this->db->free($resql);
1369 } else {
1370 dol_print_error($this->db);
1371 }
1372
1373 return $tasks;
1374 }
1375
1386 public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1387 {
1388 $arrayroles = array();
1389
1390 dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".json_encode(is_object($userp))." usert=".json_encode(is_object($usert))." projectid=".$projectid." taskid=".$taskid);
1391
1392 // We want role of user for a projet or role of user for a task. Both are not possible.
1393 if (empty($userp) && empty($usert)) {
1394 $this->error = "CallWithWrongParameters";
1395 return -1;
1396 }
1397 if (!empty($userp) && !empty($usert)) {
1398 $this->error = "CallWithWrongParameters";
1399 return -1;
1400 }
1401
1402 /* Liste des taches et role sur les projects ou taches */
1403 $sql = "SELECT ";
1404 if ($userp) {
1405 $sql .= " p.rowid as pid,";
1406 } else {
1407 $sql .= " pt.rowid as pid,";
1408 }
1409 $sql .= " ec.element_id, ctc.code, ctc.source";
1410 if ($userp) {
1411 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1412 }
1413 if ($usert && $filteronprojstatus > -1) {
1414 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1415 }
1416 if ($usert && $filteronprojstatus <= -1) {
1417 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1418 }
1419 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1420 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1421 if ($userp) {
1422 $sql .= " WHERE p.rowid = ec.element_id";
1423 } else {
1424 $sql .= " WHERE pt.rowid = ec.element_id";
1425 }
1426 if ($userp && $filteronprojstatus > -1) {
1427 $sql .= " AND p.fk_statut = ".((int) $filteronprojstatus);
1428 }
1429 if ($usert && $filteronprojstatus > -1) {
1430 $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1431 }
1432 if ($userp) {
1433 $sql .= " AND ctc.element = 'project'";
1434 }
1435 if ($usert) {
1436 $sql .= " AND ctc.element = 'project_task'";
1437 }
1438 $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1439 if ($userp) {
1440 $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1441 }
1442 if ($usert) {
1443 $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1444 }
1445 $sql .= " AND ec.statut = 4";
1446 $sql .= " AND ctc.source = 'internal'";
1447 if ($projectid) {
1448 if ($userp) {
1449 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1450 }
1451 if ($usert) {
1452 $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1453 }
1454 }
1455 if ($taskid) {
1456 if ($userp) {
1457 $sql .= " ERROR SHOULD NOT HAPPENS";
1458 }
1459 if ($usert) {
1460 $sql .= " AND pt.rowid = ".((int) $taskid);
1461 }
1462 }
1463 //print $sql;
1464
1465 dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1466 $resql = $this->db->query($sql);
1467 if ($resql) {
1468 $num = $this->db->num_rows($resql);
1469 $i = 0;
1470 while ($i < $num) {
1471 $obj = $this->db->fetch_object($resql);
1472 if (empty($arrayroles[$obj->pid])) {
1473 $arrayroles[$obj->pid] = $obj->code;
1474 } else {
1475 $arrayroles[$obj->pid] .= ','.$obj->code;
1476 }
1477 $i++;
1478 }
1479 $this->db->free($resql);
1480 } else {
1481 dol_print_error($this->db);
1482 }
1483
1484 return $arrayroles;
1485 }
1486
1487
1494 public function getListContactId($source = 'internal')
1495 {
1496 $contactAlreadySelected = array();
1497 $tab = $this->liste_contact(-1, $source);
1498 //var_dump($tab);
1499 $num = count($tab);
1500 $i = 0;
1501 while ($i < $num) {
1502 if ($source == 'thirdparty') {
1503 $contactAlreadySelected[$i] = $tab[$i]['socid'];
1504 } else {
1505 $contactAlreadySelected[$i] = $tab[$i]['id'];
1506 }
1507 $i++;
1508 }
1509 return $contactAlreadySelected;
1510 }
1511
1519 public function mergeContactTask($origin_id, $dest_id)
1520 {
1521 $error = 0;
1522 $origintask = new Task($this->db);
1523 $result = $origintask->fetch($origin_id);
1524 if ($result <= 0) {
1525 return false;
1526 }
1527
1528 //Get list of origin contacts
1529 $arraycontactorigin = array_merge($origintask->liste_contact(-1, 'internal'), $origintask->liste_contact(-1, 'external'));
1530 if (is_array($arraycontactorigin)) {
1531 foreach ($arraycontactorigin as $key => $contact) {
1532 $result = $this->add_contact($contact["id"], $contact["fk_c_type_contact"], $contact["source"]);
1533 if ($result < 0) {
1534 return false;
1535 }
1536 }
1537 }
1538 return true;
1539 }
1540
1548 public function mergeTimeSpentTask($origin_id, $dest_id)
1549 {
1550 $ret = true;
1551
1552 $this->db->begin();
1553
1554 $sql = "UPDATE ".MAIN_DB_PREFIX."element_time as et";
1555 $sql .= " SET et.fk_element = ".((int) $dest_id);
1556 $sql .= " WHERE et.elementtype = 'task'";
1557 $sql .= " AND et.fk_element = ".((int) $origin_id);
1558
1559 dol_syslog(get_class($this)."::mergeTimeSpentTask", LOG_DEBUG);
1560 if (!$this->db->query($sql)) {
1561 $this->error = $this->db->lasterror();
1562 $ret = false;
1563 }
1564
1565 if ($ret) {
1566 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1567 $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).")";
1568 $sql .= " WHERE rowid = ".((int) $dest_id);
1569
1570 dol_syslog(get_class($this)."::mergeTimeSpentTask update project_task", LOG_DEBUG);
1571 if (!$this->db->query($sql)) {
1572 $this->error = $this->db->lasterror();
1573 $ret = false;
1574 }
1575 }
1576
1577 if ($ret) {
1578 $this->db->commit();
1579 } else {
1580 $this->db->rollback();
1581 }
1582 return $ret;
1583 }
1584
1592 public function addTimeSpent($user, $notrigger = 0)
1593 {
1594 global $langs;
1595
1596 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1597
1598 $ret = 0;
1599 $now = dol_now();
1600
1601 // Check parameters
1602 if (!is_object($user)) {
1603 dol_print_error(null, "Method addTimeSpent was called with wrong parameter user");
1604 return -1;
1605 }
1606
1607 // Clean parameters
1608 if (isset($this->timespent_note)) {
1609 $this->timespent_note = trim($this->timespent_note);
1610 }
1611 if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1612 $this->timespent_datehour = $this->timespent_date;
1613 }
1614
1615 if (getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1616 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1617 $restrictBefore = dol_time_plus_duree(dol_now(), - getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'), 'm');
1618
1619 if ($this->timespent_date < $restrictBefore) {
1620 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1621 $this->errors[] = $this->error;
1622 return -1;
1623 }
1624 }
1625
1626 $this->db->begin();
1627
1628 $timespent = new TimeSpent($this->db);
1629 $timespent->fk_element = $this->id;
1630 $timespent->elementtype = 'task';
1631 $timespent->element_date = $this->timespent_date;
1632 $timespent->element_datehour = $this->timespent_datehour;
1633 $timespent->element_date_withhour = $this->timespent_withhour;
1634 $timespent->element_duration = $this->timespent_duration;
1635 $timespent->fk_user = $this->timespent_fk_user;
1636 $timespent->fk_product = $this->timespent_fk_product;
1637 $timespent->note = $this->timespent_note;
1638 $timespent->datec = $this->db->idate($now);
1639
1640 $result = $timespent->create($user);
1641
1642 if ($result > 0) {
1643 $ret = $result;
1644 $this->timespent_id = $result;
1645
1646 if (!$notrigger) {
1647 // Call trigger
1648 $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1649 if ($result < 0) {
1650 $ret = -1;
1651 }
1652 // End call triggers
1653 }
1654 } else {
1655 $this->error = $this->db->lasterror();
1656 $ret = -1;
1657 }
1658
1659 if ($ret > 0) {
1660 // Recalculate amount of time spent for task and update denormalized field
1661 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1662 $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).")";
1663 if (isset($this->progress)) {
1664 $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1665 }
1666 $sql .= " WHERE rowid = ".((int) $this->id);
1667
1668 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1669 if (!$this->db->query($sql)) {
1670 $this->error = $this->db->lasterror();
1671 $ret = -2;
1672 }
1673
1674 // Update hourly rate of this time spent entry
1675 $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1676 if (!empty($resql_thm_user)) {
1677 $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1678 $timespent->thm = $obj_thm_user->thm;
1679 }
1680 $res_update = $timespent->update($user);
1681
1682 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1683 if ($res_update <= 0) {
1684 $this->error = $this->db->lasterror();
1685 $ret = -2;
1686 }
1687 }
1688
1689 if ($ret > 0) {
1690 $this->db->commit();
1691 } else {
1692 $this->db->rollback();
1693 }
1694 return $ret;
1695 }
1696
1703 public function fetchTimeSpentOnTask($morewherefilter = '')
1704 {
1705 $arrayres = array();
1706
1707 $sql = "SELECT";
1708 $sql .= " s.rowid as socid,";
1709 $sql .= " s.nom as thirdparty_name,";
1710 $sql .= " s.email as thirdparty_email,";
1711 $sql .= " ptt.rowid,";
1712 $sql .= " ptt.ref_ext,";
1713 $sql .= " ptt.fk_element as fk_task,";
1714 $sql .= " ptt.element_date as task_date,";
1715 $sql .= " ptt.element_datehour as task_datehour,";
1716 $sql .= " ptt.element_date_withhour as task_date_withhour,";
1717 $sql .= " ptt.element_duration as task_duration,";
1718 $sql .= " ptt.fk_user,";
1719 $sql .= " ptt.note,";
1720 $sql .= " ptt.thm,";
1721 $sql .= " pt.rowid as task_id,";
1722 $sql .= " pt.ref as task_ref,";
1723 $sql .= " pt.label as task_label,";
1724 $sql .= " p.rowid as project_id,";
1725 $sql .= " p.ref as project_ref,";
1726 $sql .= " p.title as project_label,";
1727 $sql .= " p.public as public";
1728 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1729 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1730 $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1731 $sql .= " AND ptt.elementtype = 'task'";
1732 $sql .= " AND pt.rowid = ".((int) $this->id);
1733 $sql .= " AND pt.entity IN (".getEntity('project').")";
1734 if ($morewherefilter) {
1735 $sql .= $morewherefilter;
1736 }
1737
1738 dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1739 $resql = $this->db->query($sql);
1740 if ($resql) {
1741 $num = $this->db->num_rows($resql);
1742
1743 $i = 0;
1744 while ($i < $num) {
1745 $obj = $this->db->fetch_object($resql);
1746
1747 $newobj = new stdClass();
1748
1749 $newobj->socid = $obj->socid;
1750 $newobj->thirdparty_name = $obj->thirdparty_name;
1751 $newobj->thirdparty_email = $obj->thirdparty_email;
1752
1753 $newobj->fk_project = $obj->project_id;
1754 $newobj->project_ref = $obj->project_ref;
1755 $newobj->project_label = $obj->project_label;
1756 $newobj->public = $obj->project_public;
1757
1758 $newobj->fk_task = $obj->task_id;
1759 $newobj->task_ref = $obj->task_ref;
1760 $newobj->task_label = $obj->task_label;
1761
1762 $newobj->timespent_line_id = $obj->rowid;
1763 $newobj->timespent_line_ref_ext = $obj->ref_ext;
1764 $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1765 $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1766 $newobj->timespent_line_withhour = $obj->task_date_withhour;
1767 $newobj->timespent_line_duration = $obj->task_duration;
1768 $newobj->timespent_line_fk_user = $obj->fk_user;
1769 $newobj->timespent_line_thm = $obj->thm; // hourly rate
1770 $newobj->timespent_line_note = $obj->note;
1771
1772 $arrayres[] = $newobj;
1773
1774 $i++;
1775 }
1776
1777 $this->db->free($resql);
1778
1779 $this->lines = $arrayres;
1780 return 1;
1781 } else {
1782 dol_print_error($this->db);
1783 $this->error = "Error ".$this->db->lasterror();
1784 return -1;
1785 }
1786 }
1787
1788
1796 public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1797 {
1798 if (is_object($userobj)) {
1799 $userid = $userobj->id;
1800 } else {
1801 $userid = $userobj; // old method
1802 }
1803
1804 $id = $this->id;
1805 if (empty($id) && empty($userid)) {
1806 dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1807 return -1;
1808 }
1809
1810 $result = array();
1811
1812 $sql = "SELECT";
1813 $sql .= " MIN(t.element_datehour) as min_date,";
1814 $sql .= " MAX(t.element_datehour) as max_date,";
1815 $sql .= " SUM(t.element_duration) as total_duration,";
1816 $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1817 $sql .= " COUNT(t.rowid) as nblines,";
1818 $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1819 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1820 $sql .= " WHERE t.elementtype='task'";
1821 if ($morewherefilter) {
1822 $sql .= $morewherefilter;
1823 }
1824 if ($id > 0) {
1825 $sql .= " AND t.fk_element = ".((int) $id);
1826 }
1827 if ($userid > 0) {
1828 $sql .= " AND t.fk_user = ".((int) $userid);
1829 }
1830
1831 dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1832 $resql = $this->db->query($sql);
1833 if ($resql) {
1834 $obj = $this->db->fetch_object($resql);
1835
1836 $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1837 $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1838 $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1839
1840 $this->timespent_min_date = $this->db->jdate($obj->min_date);
1841 $this->timespent_max_date = $this->db->jdate($obj->max_date);
1842 $this->timespent_total_duration = $obj->total_duration;
1843 $this->timespent_total_amount = $obj->total_amount;
1844 $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1845 $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1846
1847 $this->db->free($resql);
1848 } else {
1849 dol_print_error($this->db);
1850 }
1851 return $result;
1852 }
1853
1862 public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1863 {
1864 $id = $this->id;
1865
1866 $result = array();
1867
1868 $sql = "SELECT";
1869 $sql .= " SUM(t.element_duration) as nbseconds,";
1870 $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";
1871 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1872 $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id);
1873 if (is_object($fuser) && $fuser->id > 0) {
1874 $sql .= " AND fk_user = ".((int) $fuser->id);
1875 }
1876 if ($dates > 0) {
1877 $datefieldname = "element_datehour";
1878 $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1879 }
1880 if ($datee > 0) {
1881 $datefieldname = "element_datehour";
1882 $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1883 }
1884 //print $sql;
1885
1886 dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1887 $resql = $this->db->query($sql);
1888 if ($resql) {
1889 $obj = $this->db->fetch_object($resql);
1890
1891 $result['amount'] = $obj->amount;
1892 $result['nbseconds'] = $obj->nbseconds;
1893 $result['nblinesnull'] = $obj->nblinesnull;
1894
1895 $this->db->free($resql);
1896 return $result;
1897 } else {
1898 dol_print_error($this->db);
1899 return $result;
1900 }
1901 }
1902
1909 public function fetchTimeSpent($id)
1910 {
1911 $timespent = new TimeSpent($this->db);
1912 $timespent->fetch($id);
1913
1914 dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1915
1916 if ($timespent->id > 0) {
1917 $this->timespent_id = $timespent->id;
1918 $this->id = $timespent->fk_element;
1919 $this->timespent_date = $timespent->element_date;
1920 $this->timespent_datehour = $timespent->element_datehour;
1921 $this->timespent_withhour = $timespent->element_date_withhour;
1922 $this->timespent_duration = $timespent->element_duration;
1923 $this->timespent_fk_user = $timespent->fk_user;
1924 $this->timespent_fk_product = $timespent->fk_product;
1925 $this->timespent_thm = $timespent->thm; // hourly rate
1926 $this->timespent_note = $timespent->note;
1927
1928 return 1;
1929 }
1930
1931 return 0;
1932 }
1933
1941 public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1942 {
1943 $arrayres = array();
1944
1945 $sql = "SELECT";
1946 $sql .= " s.rowid as socid,";
1947 $sql .= " s.nom as thirdparty_name,";
1948 $sql .= " s.email as thirdparty_email,";
1949 $sql .= " ptt.rowid,";
1950 $sql .= " ptt.fk_element as fk_task,";
1951 $sql .= " ptt.element_date as task_date,";
1952 $sql .= " ptt.element_datehour as task_datehour,";
1953 $sql .= " ptt.element_date_withhour as task_date_withhour,";
1954 $sql .= " ptt.element_duration as task_duration,";
1955 $sql .= " ptt.fk_user,";
1956 $sql .= " ptt.note,";
1957 $sql .= " ptt.thm,";
1958 $sql .= " pt.rowid as task_id,";
1959 $sql .= " pt.ref as task_ref,";
1960 $sql .= " pt.label as task_label,";
1961 $sql .= " p.rowid as project_id,";
1962 $sql .= " p.ref as project_ref,";
1963 $sql .= " p.title as project_label,";
1964 $sql .= " p.public as public";
1965 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1966 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1967 $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1968 $sql .= " AND ptt.elementtype = 'task'";
1969 $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1970 $sql .= " AND pt.entity IN (".getEntity('project').")";
1971 if ($morewherefilter) {
1972 $sql .= $morewherefilter;
1973 }
1974
1975 dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1976 $resql = $this->db->query($sql);
1977 if ($resql) {
1978 $num = $this->db->num_rows($resql);
1979
1980 $i = 0;
1981 while ($i < $num) {
1982 $obj = $this->db->fetch_object($resql);
1983
1984 $newobj = new stdClass();
1985
1986 $newobj->socid = $obj->socid;
1987 $newobj->thirdparty_name = $obj->thirdparty_name;
1988 $newobj->thirdparty_email = $obj->thirdparty_email;
1989
1990 $newobj->fk_project = $obj->project_id;
1991 $newobj->project_ref = $obj->project_ref;
1992 $newobj->project_label = $obj->project_label;
1993 $newobj->public = $obj->project_public;
1994
1995 $newobj->fk_task = $obj->task_id;
1996 $newobj->task_ref = $obj->task_ref;
1997 $newobj->task_label = $obj->task_label;
1998
1999 $newobj->timespent_id = $obj->rowid;
2000 $newobj->timespent_date = $this->db->jdate($obj->task_date);
2001 $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
2002 $newobj->timespent_withhour = $obj->task_date_withhour;
2003 $newobj->timespent_duration = $obj->task_duration;
2004 $newobj->timespent_fk_user = $obj->fk_user;
2005 $newobj->timespent_thm = $obj->thm; // hourly rate
2006 $newobj->timespent_note = $obj->note;
2007
2008 $arrayres[] = $newobj;
2009
2010 $i++;
2011 }
2012
2013 $this->db->free($resql);
2014 } else {
2015 dol_print_error($this->db);
2016 $this->error = "Error ".$this->db->lasterror();
2017 return -1;
2018 }
2019
2020 return $arrayres;
2021 }
2022
2030 public function updateTimeSpent($user, $notrigger = 0)
2031 {
2032 global $conf, $langs;
2033
2034 $ret = 0;
2035
2036 // Check parameters
2037 if ($this->timespent_date == '') {
2038 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
2039 return -1;
2040 }
2041 if (!($this->timespent_fk_user > 0)) {
2042 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
2043 return -1;
2044 }
2045
2046 // Clean parameters
2047 if (empty($this->timespent_datehour)) {
2048 $this->timespent_datehour = $this->timespent_date;
2049 }
2050 if (isset($this->timespent_note)) {
2051 $this->timespent_note = trim($this->timespent_note);
2052 }
2053
2054 if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
2055 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2056 $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
2057
2058 if ($this->timespent_date < $restrictBefore) {
2059 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
2060 $this->errors[] = $this->error;
2061 return -1;
2062 }
2063 }
2064
2065 $this->db->begin();
2066
2067 $timespent = new TimeSpent($this->db);
2068 $timespent->fetch($this->timespent_id);
2069 $timespent->element_date = $this->timespent_date;
2070 $timespent->element_datehour = $this->timespent_datehour;
2071 $timespent->element_date_withhour = $this->timespent_withhour;
2072 $timespent->element_duration = $this->timespent_duration;
2073 $timespent->fk_user = $this->timespent_fk_user;
2074 $timespent->fk_product = $this->timespent_fk_product;
2075 $timespent->note = $this->timespent_note;
2076 $timespent->invoice_id = $this->timespent_invoiceid;
2077 $timespent->invoice_line_id = $this->timespent_invoicelineid;
2078
2079 dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
2080 if ($timespent->update($user) > 0) {
2081 if (!$notrigger) {
2082 // Call trigger
2083 $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
2084 if ($result < 0) {
2085 $this->db->rollback();
2086 $ret = -1;
2087 } else {
2088 $ret = 1;
2089 }
2090 // End call triggers
2091 } else {
2092 $ret = 1;
2093 }
2094 } else {
2095 $this->error = $this->db->lasterror();
2096 $this->db->rollback();
2097 $ret = -1;
2098 }
2099
2100 if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) {
2101 if ($this->timespent_old_duration != $this->timespent_duration) {
2102 // Recalculate amount of time spent for task and update denormalized field
2103 $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
2104 $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) . ")";
2105 if (isset($this->progress)) {
2106 $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
2107 }
2108 $sql .= " WHERE rowid = " . ((int) $this->id);
2109
2110 dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
2111 if (!$this->db->query($sql)) {
2112 $this->error = $this->db->lasterror();
2113 $this->db->rollback();
2114 $ret = -2;
2115 }
2116 }
2117
2118 // Update hourly rate of this time spent entry, but only if it was not set initially
2119 $res_update = 1;
2120 if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) {
2121 $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
2122 if (!empty($resql_thm_user)) {
2123 $obj_thm_user = $this->db->fetch_object($resql_thm_user);
2124 $timespent->thm = $obj_thm_user->thm;
2125 }
2126 $res_update = $timespent->update($user);
2127 }
2128
2129 dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
2130 if ($res_update <= 0) {
2131 $this->error = $this->db->lasterror();
2132 $ret = -2;
2133 }
2134 }
2135
2136 if ($ret >= 0) {
2137 $this->db->commit();
2138 }
2139 return $ret;
2140 }
2141
2149 public function delTimeSpent($user, $notrigger = 0)
2150 {
2151 global $conf, $langs;
2152
2153 $error = 0;
2154
2155 if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
2156 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2157 $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
2158
2159 if ($this->timespent_date < $restrictBefore) {
2160 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
2161 $this->errors[] = $this->error;
2162 return -1;
2163 }
2164 }
2165
2166 $this->db->begin();
2167
2168 if (!$notrigger) {
2169 // Call trigger
2170 $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
2171 if ($result < 0) {
2172 $error++;
2173 }
2174 // End call triggers
2175 }
2176
2177 if (!$error) {
2178 $timespent = new TimeSpent($this->db);
2179 $timespent->fetch($this->timespent_id);
2180
2181 $res_del = $timespent->delete($user);
2182
2183 if ($res_del < 0) {
2184 $error++;
2185 $this->errors[] = "Error ".$this->db->lasterror();
2186 }
2187 }
2188
2189 if (!$error) {
2190 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
2191 $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
2192 $sql .= " WHERE rowid = ".((int) $this->id);
2193
2194 dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
2195 if ($this->db->query($sql)) {
2196 $result = 0;
2197 } else {
2198 $this->error = $this->db->lasterror();
2199 $result = -2;
2200 }
2201 }
2202
2203 // Commit or rollback
2204 if ($error) {
2205 foreach ($this->errors as $errmsg) {
2206 dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
2207 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2208 }
2209 $this->db->rollback();
2210 return -1 * $error;
2211 } else {
2212 $this->db->commit();
2213 return 1;
2214 }
2215 }
2216
2231 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)
2232 {
2233 global $langs, $conf;
2234
2235 $error = 0;
2236
2237 //Use 00:00 of today if time is use on task.
2238 $now = dol_mktime(0, 0, 0, (int) dol_print_date(dol_now(), '%m'), (int) dol_print_date(dol_now(), '%d'), (int) dol_print_date(dol_now(), '%Y'));
2239
2240 $datec = $now;
2241
2242 $clone_task = new Task($this->db);
2243 $origin_task = new Task($this->db);
2244
2245 $clone_task->context['createfromclone'] = 'createfromclone';
2246
2247 $this->db->begin();
2248
2249 // Load source object
2250 $clone_task->fetch($fromid);
2251 $clone_task->fetch_optionals();
2252 //var_dump($clone_task->array_options);exit;
2253
2254 $origin_task->fetch($fromid);
2255
2256 $defaultref = '';
2257 $obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
2258 if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').".php")) {
2259 require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').'.php';
2260 $modTask = new $obj();
2261 '@phan-var-force ModeleNumRefTask $modTask';
2262 $defaultref = $modTask->getNextValue(0, $clone_task);
2263 }
2264
2265 $ori_project_id = $clone_task->fk_project;
2266
2267 $clone_task->id = 0;
2268 $clone_task->ref = $defaultref;
2269 $clone_task->fk_project = $project_id;
2270 $clone_task->fk_task_parent = $parent_task_id;
2271 $clone_task->date_c = $datec;
2272 $clone_task->planned_workload = $origin_task->planned_workload;
2273 $clone_task->rang = $origin_task->rang;
2274 $clone_task->priority = $origin_task->priority;
2275
2276 //Manage Task Date
2277 if ($clone_change_dt) {
2278 $projectstatic = new Project($this->db);
2279 $projectstatic->fetch($ori_project_id);
2280
2281 //Origin project start date
2282 $orign_project_dt_start = $projectstatic->date_start;
2283
2284 //Calculate new task start date with difference between origin proj start date and origin task start date
2285 if (!empty($clone_task->date_start)) {
2286 $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
2287 }
2288
2289 //Calculate new task end date with difference between origin proj end date and origin task end date
2290 if (!empty($clone_task->date_end)) {
2291 $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
2292 }
2293 }
2294
2295 if (!$clone_prog) {
2296 $clone_task->progress = 0;
2297 }
2298
2299 // Create clone
2300 $result = $clone_task->create($user);
2301
2302 // Other options
2303 if ($result < 0) {
2304 $this->error = $clone_task->error;
2305 $error++;
2306 }
2307 // End
2308 if ($error) {
2309 $clone_task_id = 0; // For static tool check
2310 } else {
2311 $clone_task_id = $clone_task->id;
2312 $clone_task_ref = $clone_task->ref;
2313
2314 //Note Update
2315 if (!$clone_note) {
2316 $clone_task->note_private = '';
2317 $clone_task->note_public = '';
2318 } else {
2319 $this->db->begin();
2320 $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
2321 if ($res < 0) {
2322 $this->error .= $clone_task->error;
2323 $error++;
2324 $this->db->rollback();
2325 } else {
2326 $this->db->commit();
2327 }
2328
2329 $this->db->begin();
2330 $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
2331 if ($res < 0) {
2332 $this->error .= $clone_task->error;
2333 $error++;
2334 $this->db->rollback();
2335 } else {
2336 $this->db->commit();
2337 }
2338 }
2339
2340 //Duplicate file
2341 if ($clone_file) {
2342 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2343
2344 //retrieve project origin ref to know folder to copy
2345 $projectstatic = new Project($this->db);
2346 $projectstatic->fetch($ori_project_id);
2347 $ori_project_ref = $projectstatic->ref;
2348
2349 if ($ori_project_id != $project_id) {
2350 $projectstatic->fetch($project_id);
2351 $clone_project_ref = $projectstatic->ref;
2352 } else {
2353 $clone_project_ref = $ori_project_ref;
2354 }
2355
2356 $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
2357 $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName((string) $fromid);
2358
2359 $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
2360 foreach ($filearray as $key => $file) {
2361 if (!file_exists($clone_task_dir)) {
2362 if (dol_mkdir($clone_task_dir) < 0) {
2363 $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
2364 $error++;
2365 }
2366 }
2367
2368 $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], '0', 1);
2369 if (is_numeric($rescopy) && $rescopy < 0) {
2370 $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
2371 $error++;
2372 }
2373 }
2374 }
2375
2376 // clone affectation
2377 if ($clone_affectation) {
2378 $origin_task = new Task($this->db);
2379 $origin_task->fetch($fromid);
2380
2381 foreach (array('internal', 'external') as $source) {
2382 $tab = $origin_task->liste_contact(-1, $source);
2383 $num = count($tab);
2384 $i = 0;
2385 while ($i < $num) {
2386 $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2387 if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2388 $langs->load("errors");
2389 $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2390 $error++;
2391 } else {
2392 if ($clone_task->error != '') {
2393 $this->error .= $clone_task->error;
2394 $error++;
2395 }
2396 }
2397 $i++;
2398 }
2399 }
2400 }
2401
2402 if ($clone_time) {
2403 //TODO clone time of affectation
2404 }
2405 }
2406
2407 unset($clone_task->context['createfromclone']);
2408
2409 if (!$error) {
2410 $this->db->commit();
2411 return $clone_task_id;
2412 } else {
2413 $this->db->rollback();
2414 dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
2415 return -1;
2416 }
2417 }
2418
2419
2426 public function getLibStatut($mode = 0)
2427 {
2428 return $this->LibStatut($this->status, $mode);
2429 }
2430
2431 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2439 public function LibStatut($status, $mode = 0)
2440 {
2441 // phpcs:enable
2442 global $langs;
2443
2444 // list of Statut of the task
2445 $this->labelStatus[0] = 'Draft';
2446 $this->labelStatus[1] = 'ToDo';
2447 $this->labelStatus[2] = 'Running';
2448 $this->labelStatus[3] = 'Finish';
2449 $this->labelStatus[4] = 'Transfered';
2450 $this->labelStatusShort[0] = 'Draft';
2451 $this->labelStatusShort[1] = 'ToDo';
2452 $this->labelStatusShort[2] = 'Running';
2453 $this->labelStatusShort[3] = 'Completed';
2454 $this->labelStatusShort[4] = 'Transfered';
2455
2456 if ($mode == 0) {
2457 return $langs->trans($this->labelStatus[$status]);
2458 } elseif ($mode == 1) {
2459 return $langs->trans($this->labelStatusShort[$status]);
2460 } elseif ($mode == 2) {
2461 if ($status == 0) {
2462 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0').' '.$langs->trans($this->labelStatusShort[$status]);
2463 } elseif ($status == 1) {
2464 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1').' '.$langs->trans($this->labelStatusShort[$status]);
2465 } elseif ($status == 2) {
2466 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3').' '.$langs->trans($this->labelStatusShort[$status]);
2467 } elseif ($status == 3) {
2468 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatusShort[$status]);
2469 } elseif ($status == 4) {
2470 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatusShort[$status]);
2471 } elseif ($status == 5) {
2472 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5').' '.$langs->trans($this->labelStatusShort[$status]);
2473 }
2474 } elseif ($mode == 3) {
2475 if ($status == 0) {
2476 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0');
2477 } elseif ($status == 1) {
2478 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1');
2479 } elseif ($status == 2) {
2480 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3');
2481 } elseif ($status == 3) {
2482 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2483 } elseif ($status == 4) {
2484 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2485 } elseif ($status == 5) {
2486 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5');
2487 }
2488 } elseif ($mode == 4) {
2489 if ($status == 0) {
2490 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0').' '.$langs->trans($this->labelStatus[$status]);
2491 } elseif ($status == 1) {
2492 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1').' '.$langs->trans($this->labelStatus[$status]);
2493 } elseif ($status == 2) {
2494 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3').' '.$langs->trans($this->labelStatus[$status]);
2495 } elseif ($status == 3) {
2496 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatus[$status]);
2497 } elseif ($status == 4) {
2498 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatus[$status]);
2499 } elseif ($status == 5) {
2500 return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5').' '.$langs->trans($this->labelStatus[$status]);
2501 }
2502 } elseif ($mode == 5) {
2503 /*if ($status==0) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2504 elseif ($status==1) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2505 elseif ($status==2) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2506 elseif ($status==3) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2507 elseif ($status==4) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2508 elseif ($status==5) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2509 */
2510 //else return $this->progress.' %';
2511 return '&nbsp;';
2512 } elseif ($mode == 6) {
2513 /*if ($status==0) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2514 elseif ($status==1) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2515 elseif ($status==2) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2516 elseif ($status==3) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2517 elseif ($status==4) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2518 elseif ($status==5) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2519 */
2520 //else return $this->progress.' %';
2521 return '&nbsp;';
2522 }
2523 return "";
2524 }
2525
2536 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2537 {
2538 $outputlangs->load("projects");
2539
2540 if (!dol_strlen($modele)) {
2541 $modele = 'nodefault';
2542
2543 if (!empty($this->model_pdf)) {
2544 $modele = $this->model_pdf;
2545 } elseif (getDolGlobalString('PROJECT_TASK_ADDON_PDF')) {
2546 $modele = getDolGlobalString('PROJECT_TASK_ADDON_PDF');
2547 }
2548 }
2549
2550 $modelpath = "core/modules/project/task/doc/";
2551
2552 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2553 }
2554
2555
2556 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2563 public function load_board($user)
2564 {
2565 // phpcs:enable
2566 global $conf, $langs;
2567
2568 // For external user, no check is done on company because readability is managed by public status of project and assignment.
2569 //$socid = $user->socid;
2570 $socid = 0;
2571
2572 $projectstatic = new Project($this->db);
2573 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2574
2575 // List of tasks (does not care about permissions. Filtering will be done later)
2576 $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2577 $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2578 $sql .= " t.dateo as date_start, t.datee as date_end";
2579 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2580 //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2581 //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2582 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2583 $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2584 $sql .= " AND p.fk_statut = 1";
2585 $sql .= " AND t.fk_projet = p.rowid";
2586 $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2587 if (!$user->hasRight('projet', 'all', 'lire')) {
2588 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2589 }
2590 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2591 //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).")";
2592 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2593 // 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))";
2594
2595 //print $sql;
2596 $resql = $this->db->query($sql);
2597 if ($resql) {
2598 $task_static = new Task($this->db);
2599
2600 $response = new WorkboardResponse();
2601 $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2602 $response->label = $langs->trans("OpenedTasks");
2603 if ($user->hasRight("projet", "all", "lire")) {
2604 $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2605 } else {
2606 $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2607 }
2608 $response->img = img_object('', "task");
2609
2610 // This assignment in condition is not a bug. It allows walking the results.
2611 while ($obj = $this->db->fetch_object($resql)) {
2612 $response->nbtodo++;
2613
2614 $task_static->projectstatus = $obj->projectstatus;
2615 $task_static->progress = $obj->progress;
2616 $task_static->fk_statut = $obj->status;
2617 $task_static->status = $obj->status;
2618 $task_static->date_start = $this->db->jdate($obj->date_start);
2619 $task_static->date_end = $this->db->jdate($obj->date_end);
2620
2621 if ($task_static->hasDelay()) {
2622 $response->nbtodolate++;
2623 }
2624 }
2625
2626 return $response;
2627 } else {
2628 $this->error = $this->db->error();
2629 return -1;
2630 }
2631 }
2632
2633
2639 public function loadStateBoard()
2640 {
2641 global $user;
2642
2643 $mine = 0;
2644 $socid = $user->socid;
2645
2646 $projectstatic = new Project($this->db);
2647 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2648
2649 // List of tasks (does not care about permissions. Filtering will be done later)
2650 $sql = "SELECT count(p.rowid) as nb";
2651 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2652 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2653 if (!$user->hasRight('societe', 'client', 'voir')) {
2654 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2655 }
2656 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2657 $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2658 $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2659 if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
2660 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2661 }
2662 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2663 //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).")";
2664 if ($socid) {
2665 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2666 }
2667 if (!$user->hasRight('societe', 'client', 'voir')) {
2668 $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2669 }
2670
2671 $resql = $this->db->query($sql);
2672 if ($resql) {
2673 // This assignment in condition is not a bug. It allows walking the results.
2674 while ($obj = $this->db->fetch_object($resql)) {
2675 $this->nb["tasks"] = $obj->nb;
2676 }
2677 $this->db->free($resql);
2678 return 1;
2679 } else {
2680 dol_print_error($this->db);
2681 $this->error = $this->db->error();
2682 return -1;
2683 }
2684 }
2685
2691 public function hasDelay()
2692 {
2693 global $conf;
2694
2695 if (!($this->progress >= 0 && $this->progress < 100)) {
2696 return false;
2697 }
2698
2699 $now = dol_now();
2700
2701 $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
2702
2703 return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2704 }
2705
2713 public function getKanbanView($option = '', $arraydata = null)
2714 {
2715 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2716
2717 $return = '<div class="box-flex-item box-flex-grow-zero">';
2718 $return .= '<div class="info-box info-box-sm info-box-kanban">';
2719 $return .= '<span class="info-box-icon bg-infobox-action">';
2720 $return .= img_picto('', $this->picto);
2721 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2722 $return .= '</span>';
2723 $return .= '<div class="info-box-content">';
2724 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2725 if ($selected >= 0) {
2726 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2727 }
2728 if (!empty($arraydata['projectlink'])) {
2729 //$tmpproject = $arraydata['project'];
2730 //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
2731 $return .= '<br><span class="info-box-status ">'.$arraydata['projectlink'].'</span>';
2732 }
2733 //if (property_exists($this, 'budget_amount')) {
2734 //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
2735 //}
2736 if (property_exists($this, 'duration_effective')) {
2737 $return .= '<br><div class="info-box-label progressinkanban paddingtop">'.getTaskProgressView($this, false, true).'</div>';
2738 }
2739 $return .= '</div>';
2740 $return .= '</div>';
2741 $return .= '</div>';
2742
2743 return $return;
2744 }
2745
2753 public function mergeTask($task_origin_id)
2754 {
2755 global $langs, $hookmanager, $user, $action;
2756
2757 $error = 0;
2758 $task_origin = new Task($this->db); // The thirdparty that we will delete
2759
2760 dol_syslog("mergeTask merge task id=".$task_origin_id." (will be deleted) into the task id=".$this->id);
2761
2762 $langs->load('error');
2763
2764 if (!$error && $task_origin->fetch($task_origin_id) < 1) {
2765 $this->error = $langs->trans('ErrorRecordNotFound');
2766 $error++;
2767 }
2768
2769 if (!$error) {
2770 $this->db->begin();
2771
2772 // Recopy some data
2773 $listofproperties = array(
2774 'label', 'description', 'duration_effective', 'planned_workload', 'datec', 'date_start',
2775 'date_end', 'fk_user_creat', 'fk_user_valid', 'fk_statut', 'progress', 'budget_amount',
2776 'priority', 'rang', 'fk_projet', 'fk_task_parent'
2777 );
2778 foreach ($listofproperties as $property) {
2779 if (empty($this->$property)) {
2780 $this->$property = $task_origin->$property;
2781 }
2782 }
2783
2784 // Concat some data
2785 $listofproperties = array(
2786 'note_public', 'note_private'
2787 );
2788 foreach ($listofproperties as $property) {
2789 $this->$property = dol_concatdesc($this->$property, $task_origin->$property);
2790 }
2791
2792 // Merge extrafields
2793 if (is_array($task_origin->array_options)) {
2794 foreach ($task_origin->array_options as $key => $val) {
2795 if (empty($this->array_options[$key])) {
2796 $this->array_options[$key] = $val;
2797 }
2798 }
2799 }
2800
2801 // Update
2802 $result = $this->update($user);
2803
2804 if ($result < 0) {
2805 $error++;
2806 }
2807
2808 // Merge time spent
2809 if (!$error) {
2810 $result = $this->mergeTimeSpentTask($task_origin_id, $this->id);
2811 if ($result != true) {
2812 $error++;
2813 }
2814 }
2815
2816 // Merge contacts
2817 if (!$error) {
2818 $result = $this->mergeContactTask($task_origin_id, $this->id);
2819 if ($result != true) {
2820 $error++;
2821 }
2822 }
2823
2824 // External modules should update their ones too
2825 if (!$error) {
2826 $parameters = array('task_origin' => $task_origin->id, 'task_dest' => $this->id);
2827 $reshook = $hookmanager->executeHooks('replaceThirdparty', $parameters, $this, $action);
2828
2829 if ($reshook < 0) {
2830 $this->error = $hookmanager->error;
2831 $this->errors = $hookmanager->errors;
2832 $error++;
2833 }
2834 }
2835
2836
2837 if (!$error) {
2838 $this->context = array('merge' => 1, 'mergefromid' => $task_origin->id, 'mergefromref' => $task_origin->ref);
2839
2840 // Call trigger
2841 $result = $this->call_trigger('TASK_MODIFY', $user);
2842 if ($result < 0) {
2843 $error++;
2844 }
2845 // End call triggers
2846 }
2847
2848 if (!$error) {
2849 // We finally remove the old task
2850 if ($task_origin->delete($user) < 1) {
2851 $this->error = $task_origin->error;
2852 $this->errors = $task_origin->errors;
2853 $error++;
2854 }
2855 }
2856
2857 if (!$error) {
2858 $this->db->commit();
2859 return 0;
2860 } else {
2861 $langs->load("errors");
2862 $this->error = $langs->trans('ErrorsTaskMerge');
2863 $this->db->rollback();
2864 return -1;
2865 }
2866 }
2867
2868 return -1;
2869 }
2870}
$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 clickable name (with picto eventually)
getListContactId($source='internal')
Return list of id of contacts of task.
getKanbanView($option='', $arraydata=null)
Return clickable 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.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:162
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:125
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 a 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)