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