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