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($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
904 public function getTooltipContentArray($params)
905 {
906 global $langs;
907
908 $langs->load('projects');
909
910 $datas = [];
911 $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
912 if (!empty($this->ref)) {
913 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
914 }
915 if (!empty($this->label)) {
916 $datas['label'] = '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
917 }
918 if ($this->date_start || $this->date_end) {
919 $datas['range'] = "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
920 }
921
922 return $datas;
923 }
924
937 public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
938 {
939 global $action, $conf, $hookmanager, $langs;
940
941 if (!empty($conf->dol_no_mouse_hover)) {
942 $notooltip = 1; // Force disable tooltips
943 }
944
945 $result = '';
946 $params = [
947 'id' => $this->id,
948 'objecttype' => $this->element,
949 ];
950 $classfortooltip = 'classfortooltip';
951 $dataparams = '';
952 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
953 $classfortooltip = 'classforajaxtooltip';
954 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
955 $label = '';
956 } else {
957 $label = implode($this->getTooltipContentArray($params));
958 }
959
960 $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
961 // Add param to save lastsearch_values or not
962 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
963 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
964 $add_save_lastsearch_values = 1;
965 }
966 if ($add_save_lastsearch_values) {
967 $url .= '&save_lastsearch_values=1';
968 }
969
970 $linkclose = '';
971 if (empty($notooltip)) {
972 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
973 $label = $langs->trans("ShowTask");
974 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
975 }
976 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
977 $linkclose .= $dataparams.' class="'.$classfortooltip.' nowraponall"';
978 } else {
979 $linkclose .= ' class="nowraponall"';
980 }
981
982 $linkstart = '<a href="'.$url.'"';
983 $linkstart .= $linkclose.'>';
984 $linkend = '</a>';
985
986 $picto = 'projecttask';
987
988 $result .= $linkstart;
989 if ($withpicto) {
990 $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
991 }
992 if ($withpicto != 2) {
993 $result .= $this->ref;
994 }
995 $result .= $linkend;
996 if ($withpicto != 2) {
997 $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
998 }
999
1000 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1001 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1002 if ($reshook > 0) {
1003 $result = $hookmanager->resPrint;
1004 } else {
1005 $result .= $hookmanager->resPrint;
1006 }
1007
1008 return $result;
1009 }
1010
1018 public function initAsSpecimen()
1019 {
1020 global $user;
1021
1022 $this->id = 0;
1023
1024 $this->fk_project = 0;
1025 $this->ref = 'TK01';
1026 $this->fk_task_parent = 0;
1027 $this->label = 'Specimen task TK01';
1028 $this->duration_effective = '';
1029 $this->fk_user_creat = $user->id;
1030 $this->progress = 25;
1031 $this->status = 0;
1032 $this->priority = 0;
1033 $this->note_private = 'This is a specimen private note';
1034 $this->note_public = 'This is a specimen public note';
1035 $this->billable = 1;
1036
1037 return 1;
1038 }
1039
1063 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 = '')
1064 {
1065 global $hookmanager;
1066
1067 $tasks = array();
1068
1069 //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
1070
1071 // List of tasks (does not care about permissions. Filtering will be done later)
1072 $sql = "SELECT ";
1073 if ($filteronprojuser > 0 || $filterontaskuser > 0) {
1074 $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
1075 }
1076 $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
1077 $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,";
1078 $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
1079 $sql .= " t.budget_amount, t.billable,";
1080 $sql .= " t.note_public, t.note_private,";
1081 $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
1082 $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
1083 if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
1084 if (!empty($extrafields->attributes['projet']['label'])) {
1085 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1086 $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
1087 }
1088 }
1089 if (!empty($extrafields->attributes['projet_task']['label'])) {
1090 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1091 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
1092 }
1093 }
1094 }
1095 if ($includebilltime) {
1096 $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";
1097 }
1098
1099 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1100 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1101 if ($loadextras) {
1102 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
1103 }
1104
1105 if ($mode == 0) {
1106 if ($filteronprojuser > 0) {
1107 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1108 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1109 }
1110 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1111 if ($loadextras) {
1112 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
1113 }
1114 if ($includebilltime) {
1115 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1116 }
1117 if ($filterontaskuser > 0) {
1118 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1119 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1120 }
1121 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1122 $sql .= " AND t.fk_projet = p.rowid";
1123 } elseif ($mode == 1) {
1124 if ($filteronprojuser > 0) {
1125 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1126 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1127 }
1128 if ($filterontaskuser > 0) {
1129 $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1130 if ($includebilltime) {
1131 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1132 }
1133 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1134 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1135 } else {
1136 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
1137 if ($includebilltime) {
1138 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
1139 }
1140 }
1141 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1142 } else {
1143 return 'BadValueForParameterMode';
1144 }
1145
1146 if ($filteronprojuser > 0) {
1147 $sql .= " AND p.rowid = ec.element_id";
1148 $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1149 $sql .= " AND ctc.element = 'project'";
1150 $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
1151 $sql .= " AND ec.statut = 4";
1152 $sql .= " AND ctc.source = 'internal'";
1153 }
1154 if ($filterontaskuser > 0) {
1155 $sql .= " AND t.fk_projet = p.rowid";
1156 $sql .= " AND p.rowid = ec2.element_id";
1157 $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
1158 $sql .= " AND ctc2.element = 'project_task'";
1159 $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
1160 $sql .= " AND ec2.statut = 4";
1161 $sql .= " AND ctc2.source = 'internal'";
1162 }
1163 if ($socid) {
1164 $sql .= " AND p.fk_soc = ".((int) $socid);
1165 }
1166 if ($projectid) {
1167 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1168 }
1169 if ($filteronproj) {
1170 $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
1171 }
1172 if ($filteronprojstatus && (int) $filteronprojstatus != '-1') {
1173 $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
1174 }
1175 if ($morewherefilter) {
1176 $sql .= $morewherefilter;
1177 }
1178
1179 // Add where from extra fields
1180 $extrafieldsobjectkey = 'projet_task';
1181 $extrafieldsobjectprefix = 'efpt.';
1182 global $db, $conf; // needed for extrafields_list_search_sql.tpl
1183 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
1184
1185 // Add where from hooks
1186 $parameters = array();
1187 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
1188 $sql .= $hookmanager->resPrint;
1189 if ($includebilltime) {
1190 $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
1191 $sql .= " t.datec, t.dateo, t.datee, t.tms,";
1192 $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
1193 $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
1194 $sql .= " t.budget_amount, t.billable,";
1195 $sql .= " t.note_public, t.note_private,";
1196 $sql .= " s.rowid, s.nom, s.email,";
1197 $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
1198 if ($loadextras) {
1199 if (!empty($extrafields->attributes['projet']['label'])) {
1200 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1201 $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
1202 }
1203 }
1204 if (!empty($extrafields->attributes['projet_task']['label'])) {
1205 foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1206 $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
1207 }
1208 }
1209 }
1210 }
1211
1212 if ($sortfield && $sortorder) {
1213 $sql .= $this->db->order($sortfield, $sortorder);
1214 } else {
1215 $sql .= " ORDER BY p.ref, t.rang, t.dateo";
1216 }
1217
1218 //print $sql;exit;
1219 dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
1220 $resql = $this->db->query($sql);
1221 if ($resql) {
1222 $num = $this->db->num_rows($resql);
1223 $i = 0;
1224 // Loop on each record found, so each couple (project id, task id)
1225 while ($i < $num) {
1226 $error = 0;
1227
1228 $obj = $this->db->fetch_object($resql);
1229
1230 if ($loadRoleMode) {
1231 if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
1232 if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
1233 $error++;
1234 }
1235 }
1236 if (is_object($usert)) { // If we ask a filter on a user affected to a task
1237 if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
1238 $error++;
1239 }
1240 }
1241 }
1242
1243 if (!$error) {
1244 $tasks[$i] = new Task($this->db);
1245 $tasks[$i]->id = $obj->taskid;
1246 $tasks[$i]->ref = $obj->taskref;
1247 $tasks[$i]->fk_project = $obj->projectid;
1248
1249 // Data from project
1250 $tasks[$i]->projectref = $obj->ref;
1251 $tasks[$i]->projectlabel = $obj->plabel;
1252 $tasks[$i]->projectstatus = $obj->projectstatus;
1253 $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1254 $tasks[$i]->opp_amount = $obj->opp_amount;
1255 $tasks[$i]->opp_percent = $obj->opp_percent;
1256 $tasks[$i]->budget_amount = $obj->budget_amount;
1257 $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1258 $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1259
1260 $tasks[$i]->label = $obj->label;
1261 $tasks[$i]->description = $obj->description;
1262
1263 $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1264 $tasks[$i]->note_public = $obj->note_public;
1265 $tasks[$i]->note_private = $obj->note_private;
1266 $tasks[$i]->duration_effective = $obj->duration_effective;
1267 $tasks[$i]->planned_workload = $obj->planned_workload;
1268
1269 if ($includebilltime) {
1270 // Data summed from element_time linked to task
1271 $tasks[$i]->tobill = $obj->tobill;
1272 $tasks[$i]->billed = $obj->billed;
1273 }
1274
1275 $tasks[$i]->progress = $obj->progress;
1276 $tasks[$i]->fk_statut = $obj->status;
1277 $tasks[$i]->status = $obj->status;
1278 $tasks[$i]->public = $obj->public;
1279 $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1280 $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1281 $tasks[$i]->rang = $obj->rang;
1282 $tasks[$i]->priority = $obj->priority;
1283
1284 $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1285 $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1286 $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1287 $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1288
1289 $tasks[$i]->billable = $obj->billable;
1290
1291 if ($loadextras) {
1292 if (!empty($extrafields->attributes['projet']['label'])) {
1293 foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1294 if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1295 $tmpvar = 'options_'.$key;
1296 $tasks[$i]->array_options_project['options_'.$key] = $obj->$tmpvar;
1297 }
1298 }
1299 }
1300 }
1301
1302 if ($loadextras) {
1303 $tasks[$i]->fetch_optionals();
1304 }
1305 }
1306
1307 $i++;
1308 }
1309 $this->db->free($resql);
1310 } else {
1311 dol_print_error($this->db);
1312 }
1313
1314 return $tasks;
1315 }
1316
1327 public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1328 {
1329 $arrayroles = array();
1330
1331 dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".json_encode(is_object($userp))." usert=".json_encode(is_object($usert))." projectid=".$projectid." taskid=".$taskid);
1332
1333 // We want role of user for a projet or role of user for a task. Both are not possible.
1334 if (empty($userp) && empty($usert)) {
1335 $this->error = "CallWithWrongParameters";
1336 return -1;
1337 }
1338 if (!empty($userp) && !empty($usert)) {
1339 $this->error = "CallWithWrongParameters";
1340 return -1;
1341 }
1342
1343 /* Liste des taches et role sur les projects ou taches */
1344 $sql = "SELECT ";
1345 if ($userp) {
1346 $sql .= " p.rowid as pid,";
1347 } else {
1348 $sql .= " pt.rowid as pid,";
1349 }
1350 $sql .= " ec.element_id, ctc.code, ctc.source";
1351 if ($userp) {
1352 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1353 }
1354 if ($usert && $filteronprojstatus > -1) {
1355 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1356 }
1357 if ($usert && $filteronprojstatus <= -1) {
1358 $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1359 }
1360 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1361 $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1362 if ($userp) {
1363 $sql .= " WHERE p.rowid = ec.element_id";
1364 } else {
1365 $sql .= " WHERE pt.rowid = ec.element_id";
1366 }
1367 if ($userp && $filteronprojstatus > -1) {
1368 $sql .= " AND p.fk_statut = ".((int) $filteronprojstatus);
1369 }
1370 if ($usert && $filteronprojstatus > -1) {
1371 $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1372 }
1373 if ($userp) {
1374 $sql .= " AND ctc.element = 'project'";
1375 }
1376 if ($usert) {
1377 $sql .= " AND ctc.element = 'project_task'";
1378 }
1379 $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1380 if ($userp) {
1381 $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1382 }
1383 if ($usert) {
1384 $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1385 }
1386 $sql .= " AND ec.statut = 4";
1387 $sql .= " AND ctc.source = 'internal'";
1388 if ($projectid) {
1389 if ($userp) {
1390 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1391 }
1392 if ($usert) {
1393 $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1394 }
1395 }
1396 if ($taskid) {
1397 if ($userp) {
1398 $sql .= " ERROR SHOULD NOT HAPPENS";
1399 }
1400 if ($usert) {
1401 $sql .= " AND pt.rowid = ".((int) $taskid);
1402 }
1403 }
1404 //print $sql;
1405
1406 dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1407 $resql = $this->db->query($sql);
1408 if ($resql) {
1409 $num = $this->db->num_rows($resql);
1410 $i = 0;
1411 while ($i < $num) {
1412 $obj = $this->db->fetch_object($resql);
1413 if (empty($arrayroles[$obj->pid])) {
1414 $arrayroles[$obj->pid] = $obj->code;
1415 } else {
1416 $arrayroles[$obj->pid] .= ','.$obj->code;
1417 }
1418 $i++;
1419 }
1420 $this->db->free($resql);
1421 } else {
1422 dol_print_error($this->db);
1423 }
1424
1425 return $arrayroles;
1426 }
1427
1428
1435 public function getListContactId($source = 'internal')
1436 {
1437 $contactAlreadySelected = array();
1438 $tab = $this->liste_contact(-1, $source);
1439 //var_dump($tab);
1440 $num = count($tab);
1441 $i = 0;
1442 while ($i < $num) {
1443 if ($source == 'thirdparty') {
1444 $contactAlreadySelected[$i] = $tab[$i]['socid'];
1445 } else {
1446 $contactAlreadySelected[$i] = $tab[$i]['id'];
1447 }
1448 $i++;
1449 }
1450 return $contactAlreadySelected;
1451 }
1452
1460 public function mergeContactTask($origin_id, $dest_id)
1461 {
1462 $error = 0;
1463 $origintask = new Task($this->db);
1464 $result = $origintask->fetch($origin_id);
1465 if ($result <= 0) {
1466 return false;
1467 }
1468
1469 //Get list of origin contacts
1470 $arraycontactorigin = array_merge($origintask->liste_contact(-1, 'internal'), $origintask->liste_contact(-1, 'external'));
1471 if (is_array($arraycontactorigin)) {
1472 foreach ($arraycontactorigin as $key => $contact) {
1473 $result = $this->add_contact($contact["id"], $contact["fk_c_type_contact"], $contact["source"]);
1474 if ($result < 0) {
1475 return false;
1476 }
1477 }
1478 }
1479 return true;
1480 }
1481
1489 public function mergeTimeSpentTask($origin_id, $dest_id)
1490 {
1491 $ret = true;
1492
1493 $this->db->begin();
1494
1495 $sql = "UPDATE ".MAIN_DB_PREFIX."element_time as et";
1496 $sql .= " SET et.fk_element = ".((int) $dest_id);
1497 $sql .= " WHERE et.elementtype = 'task'";
1498 $sql .= " AND et.fk_element = ".((int) $origin_id);
1499
1500 dol_syslog(get_class($this)."::mergeTimeSpentTask", LOG_DEBUG);
1501 if (!$this->db->query($sql)) {
1502 $this->error = $this->db->lasterror();
1503 $ret = false;
1504 }
1505
1506 if ($ret) {
1507 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1508 $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).")";
1509 $sql .= " WHERE rowid = ".((int) $dest_id);
1510
1511 dol_syslog(get_class($this)."::mergeTimeSpentTask update project_task", LOG_DEBUG);
1512 if (!$this->db->query($sql)) {
1513 $this->error = $this->db->lasterror();
1514 $ret = false;
1515 }
1516 }
1517
1518 if ($ret == true) {
1519 $this->db->commit();
1520 } else {
1521 $this->db->rollback();
1522 }
1523 return $ret;
1524 }
1525
1533 public function addTimeSpent($user, $notrigger = 0)
1534 {
1535 global $langs;
1536
1537 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1538
1539 $ret = 0;
1540 $now = dol_now();
1541
1542 // Check parameters
1543 if (!is_object($user)) {
1544 dol_print_error(null, "Method addTimeSpent was called with wrong parameter user");
1545 return -1;
1546 }
1547
1548 // Clean parameters
1549 if (isset($this->timespent_note)) {
1550 $this->timespent_note = trim($this->timespent_note);
1551 }
1552 if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1553 $this->timespent_datehour = $this->timespent_date;
1554 }
1555
1556 if (getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1557 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1558 $restrictBefore = dol_time_plus_duree(dol_now(), - getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'), 'm');
1559
1560 if ($this->timespent_date < $restrictBefore) {
1561 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1562 $this->errors[] = $this->error;
1563 return -1;
1564 }
1565 }
1566
1567 $this->db->begin();
1568
1569 $timespent = new TimeSpent($this->db);
1570 $timespent->fk_element = $this->id;
1571 $timespent->elementtype = 'task';
1572 $timespent->element_date = $this->timespent_date;
1573 $timespent->element_datehour = $this->timespent_datehour;
1574 $timespent->element_date_withhour = $this->timespent_withhour;
1575 $timespent->element_duration = $this->timespent_duration;
1576 $timespent->fk_user = $this->timespent_fk_user;
1577 $timespent->fk_product = $this->timespent_fk_product;
1578 $timespent->note = $this->timespent_note;
1579 $timespent->datec = $this->db->idate($now);
1580
1581 $result = $timespent->create($user);
1582
1583 if ($result > 0) {
1584 $ret = $result;
1585 $this->timespent_id = $result;
1586
1587 if (!$notrigger) {
1588 // Call trigger
1589 $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1590 if ($result < 0) {
1591 $ret = -1;
1592 }
1593 // End call triggers
1594 }
1595 } else {
1596 $this->error = $this->db->lasterror();
1597 $ret = -1;
1598 }
1599
1600 if ($ret > 0) {
1601 // Recalculate amount of time spent for task and update denormalized field
1602 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1603 $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).")";
1604 if (isset($this->progress)) {
1605 $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1606 }
1607 $sql .= " WHERE rowid = ".((int) $this->id);
1608
1609 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1610 if (!$this->db->query($sql)) {
1611 $this->error = $this->db->lasterror();
1612 $ret = -2;
1613 }
1614
1615 // Update hourly rate of this time spent entry
1616 $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1617 if (!empty($resql_thm_user)) {
1618 $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1619 $timespent->thm = $obj_thm_user->thm;
1620 }
1621 $res_update = $timespent->update($user);
1622
1623 dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1624 if ($res_update <= 0) {
1625 $this->error = $this->db->lasterror();
1626 $ret = -2;
1627 }
1628 }
1629
1630 if ($ret > 0) {
1631 $this->db->commit();
1632 } else {
1633 $this->db->rollback();
1634 }
1635 return $ret;
1636 }
1637
1644 public function fetchTimeSpentOnTask($morewherefilter = '')
1645 {
1646 $arrayres = array();
1647
1648 $sql = "SELECT";
1649 $sql .= " s.rowid as socid,";
1650 $sql .= " s.nom as thirdparty_name,";
1651 $sql .= " s.email as thirdparty_email,";
1652 $sql .= " ptt.rowid,";
1653 $sql .= " ptt.ref_ext,";
1654 $sql .= " ptt.fk_element as fk_task,";
1655 $sql .= " ptt.element_date as task_date,";
1656 $sql .= " ptt.element_datehour as task_datehour,";
1657 $sql .= " ptt.element_date_withhour as task_date_withhour,";
1658 $sql .= " ptt.element_duration as task_duration,";
1659 $sql .= " ptt.fk_user,";
1660 $sql .= " ptt.note,";
1661 $sql .= " ptt.thm,";
1662 $sql .= " pt.rowid as task_id,";
1663 $sql .= " pt.ref as task_ref,";
1664 $sql .= " pt.label as task_label,";
1665 $sql .= " p.rowid as project_id,";
1666 $sql .= " p.ref as project_ref,";
1667 $sql .= " p.title as project_label,";
1668 $sql .= " p.public as public";
1669 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1670 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1671 $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1672 $sql .= " AND ptt.elementtype = 'task'";
1673 $sql .= " AND pt.rowid = ".((int) $this->id);
1674 $sql .= " AND pt.entity IN (".getEntity('project').")";
1675 if ($morewherefilter) {
1676 $sql .= $morewherefilter;
1677 }
1678
1679 dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1680 $resql = $this->db->query($sql);
1681 if ($resql) {
1682 $num = $this->db->num_rows($resql);
1683
1684 $i = 0;
1685 while ($i < $num) {
1686 $obj = $this->db->fetch_object($resql);
1687
1688 $newobj = new stdClass();
1689
1690 $newobj->socid = $obj->socid;
1691 $newobj->thirdparty_name = $obj->thirdparty_name;
1692 $newobj->thirdparty_email = $obj->thirdparty_email;
1693
1694 $newobj->fk_project = $obj->project_id;
1695 $newobj->project_ref = $obj->project_ref;
1696 $newobj->project_label = $obj->project_label;
1697 $newobj->public = $obj->project_public;
1698
1699 $newobj->fk_task = $obj->task_id;
1700 $newobj->task_ref = $obj->task_ref;
1701 $newobj->task_label = $obj->task_label;
1702
1703 $newobj->timespent_line_id = $obj->rowid;
1704 $newobj->timespent_line_ref_ext = $obj->ref_ext;
1705 $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1706 $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1707 $newobj->timespent_line_withhour = $obj->task_date_withhour;
1708 $newobj->timespent_line_duration = $obj->task_duration;
1709 $newobj->timespent_line_fk_user = $obj->fk_user;
1710 $newobj->timespent_line_thm = $obj->thm; // hourly rate
1711 $newobj->timespent_line_note = $obj->note;
1712
1713 $arrayres[] = $newobj;
1714
1715 $i++;
1716 }
1717
1718 $this->db->free($resql);
1719
1720 $this->lines = $arrayres;
1721 return 1;
1722 } else {
1723 dol_print_error($this->db);
1724 $this->error = "Error ".$this->db->lasterror();
1725 return -1;
1726 }
1727 }
1728
1729
1737 public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1738 {
1739 if (is_object($userobj)) {
1740 $userid = $userobj->id;
1741 } else {
1742 $userid = $userobj; // old method
1743 }
1744
1745 $id = $this->id;
1746 if (empty($id) && empty($userid)) {
1747 dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1748 return -1;
1749 }
1750
1751 $result = array();
1752
1753 $sql = "SELECT";
1754 $sql .= " MIN(t.element_datehour) as min_date,";
1755 $sql .= " MAX(t.element_datehour) as max_date,";
1756 $sql .= " SUM(t.element_duration) as total_duration,";
1757 $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1758 $sql .= " COUNT(t.rowid) as nblines,";
1759 $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1760 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1761 $sql .= " WHERE t.elementtype='task'";
1762 if ($morewherefilter) {
1763 $sql .= $morewherefilter;
1764 }
1765 if ($id > 0) {
1766 $sql .= " AND t.fk_element = ".((int) $id);
1767 }
1768 if ($userid > 0) {
1769 $sql .= " AND t.fk_user = ".((int) $userid);
1770 }
1771
1772 dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1773 $resql = $this->db->query($sql);
1774 if ($resql) {
1775 $obj = $this->db->fetch_object($resql);
1776
1777 $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1778 $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1779 $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1780
1781 $this->timespent_min_date = $this->db->jdate($obj->min_date);
1782 $this->timespent_max_date = $this->db->jdate($obj->max_date);
1783 $this->timespent_total_duration = $obj->total_duration;
1784 $this->timespent_total_amount = $obj->total_amount;
1785 $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1786 $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1787
1788 $this->db->free($resql);
1789 } else {
1790 dol_print_error($this->db);
1791 }
1792 return $result;
1793 }
1794
1803 public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1804 {
1805 $id = $this->id;
1806
1807 $result = array();
1808
1809 $sql = "SELECT";
1810 $sql .= " SUM(t.element_duration) as nbseconds,";
1811 $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";
1812 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1813 $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id);
1814 if (is_object($fuser) && $fuser->id > 0) {
1815 $sql .= " AND fk_user = ".((int) $fuser->id);
1816 }
1817 if ($dates > 0) {
1818 $datefieldname = "element_datehour";
1819 $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1820 }
1821 if ($datee > 0) {
1822 $datefieldname = "element_datehour";
1823 $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1824 }
1825 //print $sql;
1826
1827 dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1828 $resql = $this->db->query($sql);
1829 if ($resql) {
1830 $obj = $this->db->fetch_object($resql);
1831
1832 $result['amount'] = $obj->amount;
1833 $result['nbseconds'] = $obj->nbseconds;
1834 $result['nblinesnull'] = $obj->nblinesnull;
1835
1836 $this->db->free($resql);
1837 return $result;
1838 } else {
1839 dol_print_error($this->db);
1840 return $result;
1841 }
1842 }
1843
1850 public function fetchTimeSpent($id)
1851 {
1852 $timespent = new TimeSpent($this->db);
1853 $timespent->fetch($id);
1854
1855 dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1856
1857 if ($timespent->id > 0) {
1858 $this->timespent_id = $timespent->id;
1859 $this->id = $timespent->fk_element;
1860 $this->timespent_date = $timespent->element_date;
1861 $this->timespent_datehour = $timespent->element_datehour;
1862 $this->timespent_withhour = $timespent->element_date_withhour;
1863 $this->timespent_duration = $timespent->element_duration;
1864 $this->timespent_fk_user = $timespent->fk_user;
1865 $this->timespent_fk_product = $timespent->fk_product;
1866 $this->timespent_thm = $timespent->thm; // hourly rate
1867 $this->timespent_note = $timespent->note;
1868
1869 return 1;
1870 }
1871
1872 return 0;
1873 }
1874
1882 public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1883 {
1884 $arrayres = array();
1885
1886 $sql = "SELECT";
1887 $sql .= " s.rowid as socid,";
1888 $sql .= " s.nom as thirdparty_name,";
1889 $sql .= " s.email as thirdparty_email,";
1890 $sql .= " ptt.rowid,";
1891 $sql .= " ptt.fk_element as fk_task,";
1892 $sql .= " ptt.element_date as task_date,";
1893 $sql .= " ptt.element_datehour as task_datehour,";
1894 $sql .= " ptt.element_date_withhour as task_date_withhour,";
1895 $sql .= " ptt.element_duration as task_duration,";
1896 $sql .= " ptt.fk_user,";
1897 $sql .= " ptt.note,";
1898 $sql .= " ptt.thm,";
1899 $sql .= " pt.rowid as task_id,";
1900 $sql .= " pt.ref as task_ref,";
1901 $sql .= " pt.label as task_label,";
1902 $sql .= " p.rowid as project_id,";
1903 $sql .= " p.ref as project_ref,";
1904 $sql .= " p.title as project_label,";
1905 $sql .= " p.public as public";
1906 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1907 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1908 $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1909 $sql .= " AND ptt.elementtype = 'task'";
1910 $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1911 $sql .= " AND pt.entity IN (".getEntity('project').")";
1912 if ($morewherefilter) {
1913 $sql .= $morewherefilter;
1914 }
1915
1916 dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1917 $resql = $this->db->query($sql);
1918 if ($resql) {
1919 $num = $this->db->num_rows($resql);
1920
1921 $i = 0;
1922 while ($i < $num) {
1923 $obj = $this->db->fetch_object($resql);
1924
1925 $newobj = new stdClass();
1926
1927 $newobj->socid = $obj->socid;
1928 $newobj->thirdparty_name = $obj->thirdparty_name;
1929 $newobj->thirdparty_email = $obj->thirdparty_email;
1930
1931 $newobj->fk_project = $obj->project_id;
1932 $newobj->project_ref = $obj->project_ref;
1933 $newobj->project_label = $obj->project_label;
1934 $newobj->public = $obj->project_public;
1935
1936 $newobj->fk_task = $obj->task_id;
1937 $newobj->task_ref = $obj->task_ref;
1938 $newobj->task_label = $obj->task_label;
1939
1940 $newobj->timespent_id = $obj->rowid;
1941 $newobj->timespent_date = $this->db->jdate($obj->task_date);
1942 $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1943 $newobj->timespent_withhour = $obj->task_date_withhour;
1944 $newobj->timespent_duration = $obj->task_duration;
1945 $newobj->timespent_fk_user = $obj->fk_user;
1946 $newobj->timespent_thm = $obj->thm; // hourly rate
1947 $newobj->timespent_note = $obj->note;
1948
1949 $arrayres[] = $newobj;
1950
1951 $i++;
1952 }
1953
1954 $this->db->free($resql);
1955 } else {
1956 dol_print_error($this->db);
1957 $this->error = "Error ".$this->db->lasterror();
1958 return -1;
1959 }
1960
1961 return $arrayres;
1962 }
1963
1971 public function updateTimeSpent($user, $notrigger = 0)
1972 {
1973 global $conf, $langs;
1974
1975 $ret = 0;
1976
1977 // Check parameters
1978 if ($this->timespent_date == '') {
1979 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1980 return -1;
1981 }
1982 if (!($this->timespent_fk_user > 0)) {
1983 $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1984 return -1;
1985 }
1986
1987 // Clean parameters
1988 if (empty($this->timespent_datehour)) {
1989 $this->timespent_datehour = $this->timespent_date;
1990 }
1991 if (isset($this->timespent_note)) {
1992 $this->timespent_note = trim($this->timespent_note);
1993 }
1994
1995 if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1996 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1997 $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1998
1999 if ($this->timespent_date < $restrictBefore) {
2000 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
2001 $this->errors[] = $this->error;
2002 return -1;
2003 }
2004 }
2005
2006 $this->db->begin();
2007
2008 $timespent = new TimeSpent($this->db);
2009 $timespent->fetch($this->timespent_id);
2010 $timespent->element_date = $this->timespent_date;
2011 $timespent->element_datehour = $this->timespent_datehour;
2012 $timespent->element_date_withhour = $this->timespent_withhour;
2013 $timespent->element_duration = $this->timespent_duration;
2014 $timespent->fk_user = $this->timespent_fk_user;
2015 $timespent->fk_product = $this->timespent_fk_product;
2016 $timespent->note = $this->timespent_note;
2017 $timespent->invoice_id = $this->timespent_invoiceid;
2018 $timespent->invoice_line_id = $this->timespent_invoicelineid;
2019
2020 dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
2021 if ($timespent->update($user) > 0) {
2022 if (!$notrigger) {
2023 // Call trigger
2024 $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
2025 if ($result < 0) {
2026 $this->db->rollback();
2027 $ret = -1;
2028 } else {
2029 $ret = 1;
2030 }
2031 // End call triggers
2032 } else {
2033 $ret = 1;
2034 }
2035 } else {
2036 $this->error = $this->db->lasterror();
2037 $this->db->rollback();
2038 $ret = -1;
2039 }
2040
2041 if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) {
2042 if ($this->timespent_old_duration != $this->timespent_duration) {
2043 // Recalculate amount of time spent for task and update denormalized field
2044 $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
2045 $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) . ")";
2046 if (isset($this->progress)) {
2047 $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
2048 }
2049 $sql .= " WHERE rowid = " . ((int) $this->id);
2050
2051 dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
2052 if (!$this->db->query($sql)) {
2053 $this->error = $this->db->lasterror();
2054 $this->db->rollback();
2055 $ret = -2;
2056 }
2057 }
2058
2059 // Update hourly rate of this time spent entry, but only if it was not set initially
2060 $res_update = 1;
2061 if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) {
2062 $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
2063 if (!empty($resql_thm_user)) {
2064 $obj_thm_user = $this->db->fetch_object($resql_thm_user);
2065 $timespent->thm = $obj_thm_user->thm;
2066 }
2067 $res_update = $timespent->update($user);
2068 }
2069
2070 dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
2071 if ($res_update <= 0) {
2072 $this->error = $this->db->lasterror();
2073 $ret = -2;
2074 }
2075 }
2076
2077 if ($ret >= 0) {
2078 $this->db->commit();
2079 }
2080 return $ret;
2081 }
2082
2090 public function delTimeSpent($user, $notrigger = 0)
2091 {
2092 global $conf, $langs;
2093
2094 $error = 0;
2095
2096 if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
2097 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2098 $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
2099
2100 if ($this->timespent_date < $restrictBefore) {
2101 $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
2102 $this->errors[] = $this->error;
2103 return -1;
2104 }
2105 }
2106
2107 $this->db->begin();
2108
2109 if (!$notrigger) {
2110 // Call trigger
2111 $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
2112 if ($result < 0) {
2113 $error++;
2114 }
2115 // End call triggers
2116 }
2117
2118 if (!$error) {
2119 $timespent = new TimeSpent($this->db);
2120 $timespent->fetch($this->timespent_id);
2121
2122 $res_del = $timespent->delete($user);
2123
2124 if ($res_del < 0) {
2125 $error++;
2126 $this->errors[] = "Error ".$this->db->lasterror();
2127 }
2128 }
2129
2130 if (!$error) {
2131 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
2132 $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
2133 $sql .= " WHERE rowid = ".((int) $this->id);
2134
2135 dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
2136 if ($this->db->query($sql)) {
2137 $result = 0;
2138 } else {
2139 $this->error = $this->db->lasterror();
2140 $result = -2;
2141 }
2142 }
2143
2144 // Commit or rollback
2145 if ($error) {
2146 foreach ($this->errors as $errmsg) {
2147 dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
2148 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2149 }
2150 $this->db->rollback();
2151 return -1 * $error;
2152 } else {
2153 $this->db->commit();
2154 return 1;
2155 }
2156 }
2157
2172 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)
2173 {
2174 global $langs, $conf;
2175
2176 $error = 0;
2177
2178 //Use 00:00 of today if time is use on task.
2179 $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
2180
2181 $datec = $now;
2182
2183 $clone_task = new Task($this->db);
2184 $origin_task = new Task($this->db);
2185
2186 $clone_task->context['createfromclone'] = 'createfromclone';
2187
2188 $this->db->begin();
2189
2190 // Load source object
2191 $clone_task->fetch($fromid);
2192 $clone_task->fetch_optionals();
2193 //var_dump($clone_task->array_options);exit;
2194
2195 $origin_task->fetch($fromid);
2196
2197 $defaultref = '';
2198 $obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
2199 if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').".php")) {
2200 require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').'.php';
2201 $modTask = new $obj();
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($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:626
$object ref
Definition info.php:79
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others (by children).
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage projects.
const STATUS_VALIDATED
Open/Validated status.
Class to manage tasks.
create($user, $notrigger=0)
Create into database.
fetch($id, $ref='', $loadparentdata=0)
Load object in memory from database.
getLibStatut($mode=0)
Return status label of object.
mergeContactTask($origin_id, $dest_id)
Merge contact of tasks.
const STATUS_TRANSFERRED
Transferred status.
getSumOfAmount($fuser='', $dates='', $datee='')
Calculate quantity and value of time consumed using the thm (hourly amount value of work for user ent...
mergeTask($task_origin_id)
Merge a task with another one, deleting the given task.
__construct($db)
Constructor.
fetchTimeSpentOnTask($morewherefilter='')
Fetch records of time spent of this task.
const STATUS_VALIDATED
Validated status (To do).
hasTimeSpent()
Return nb of time spent.
getNomUrl($withpicto=0, $option='', $mode='task', $addlabel=0, $sep=' - ', $notooltip=0, $save_lastsearch_value=-1)
Return clicable name (with picto eventually)
getListContactId($source='internal')
Return list of id of contacts of task.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
delTimeSpent($user, $notrigger=0)
Delete time spent.
mergeTimeSpentTask($origin_id, $dest_id)
Merge time spent of tasks.
const STATUS_DRAFT
Draft status.
fetchAllTimeSpent(User $userobj, $morewherefilter='')
Load all records of time spent.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getUserRolesForProjectsOrTasks($userp, $usert, $projectid='', $taskid=0, $filteronprojstatus=-1)
Return list of roles for a user for each projects or each tasks (or a particular project or a particu...
update($user=null, $notrigger=0)
Update database.
getSummaryOfTimeSpent($userobj=null, $morewherefilter='')
Calculate total of time spent for task.
getTooltipContentArray($params)
getTooltipContentArray
const STATUS_CANCELED
status canceled
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF.
fetchTimeSpent($id)
Load properties of timespent of a task from the time spent ID.
updateTimeSpent($user, $notrigger=0)
Update time spent.
createFromClone(User $user, $fromid, $project_id, $parent_task_id, $clone_change_dt=false, $clone_affectation=false, $clone_time=false, $clone_file=false, $clone_note=false, $clone_prog=false)
Load an object from its id and create a new one in database.
loadStateBoard()
Load indicators this->nb for state board.
initAsSpecimen()
Initialise an instance with random values.
getTasksArray($usert=null, $userp=null, $projectid=0, $socid=0, $mode=0, $filteronproj='', $filteronprojstatus='-1', $morewherefilter='', $filteronprojuser=0, $filterontaskuser=0, $extrafields=null, $includebilltime=0, $search_array_options=array(), $loadextras=0, $loadRoleMode=1, $sortfield='', $sortorder='')
Return list of tasks for all projects or for one particular project Sort order is on project,...
addTimeSpent($user, $notrigger=0)
Add time spent.
hasDelay()
Is the task delayed?
LibStatut($status, $mode=0)
Return status label for an object.
const STATUS_CLOSED
Finished status.
hasChildren()
Return nb of children.
Class for TimeSpent.
Class to manage Dolibarr users.
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:124
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
dol_move_dir($srcdir, $destdir, $overwriteifexists=1, $indexdatabase=1, $renamedircontent=1)
Move a directory into another name.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)