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  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <https://www.gnu.org/licenses/>.
23  */
24 
31 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
32 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
33 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
34 require_once DOL_DOCUMENT_ROOT.'/core/class/timespent.class.php';
35 
36 
40 class Task extends CommonObjectLine
41 {
45  public $element = 'project_task';
46 
50  public $table_element = 'projet_task';
51 
55  public $fk_element = 'fk_element';
56 
60  public $picto = 'projecttask';
61 
65  protected $childtables = array(
66  'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task')
67  );
68 
72  public $fk_task_parent = 0;
73 
77  public $label;
78 
82  public $description;
83 
87  public $duration_effective;
88 
92  public $planned_workload;
93 
98  public $date_c;
99 
103  public $progress;
104 
110  public $dateo;
111 
116  public $date_start;
117 
123  public $datee;
124 
129  public $date_end;
130 
135  public $fk_statut;
136 
140  public $status;
141 
145  public $priority;
146 
150  public $fk_user_creat;
151 
155  public $fk_user_valid;
156 
157  public $rang;
158 
159  public $timespent_min_date;
160  public $timespent_max_date;
161  public $timespent_total_duration;
162  public $timespent_total_amount;
163  public $timespent_nblinesnull;
164  public $timespent_nblines;
165  // For detail of lines of timespent record, there is the property ->lines in common
166 
167  // Var used to call method addTimeSpent(). Bad practice.
168  public $timespent_id;
169  public $timespent_duration;
170  public $timespent_old_duration;
171  public $timespent_date;
172  public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
173  public $timespent_withhour; // 1 = we entered also start hours for timesheet line
174  public $timespent_fk_user;
175  public $timespent_thm;
176  public $timespent_note;
177  public $timespent_fk_product;
178  public $timespent_invoiceid;
179  public $timespent_invoicelineid;
180 
181  public $comments = array();
182 
183  // Properties calculated from sum of llx_element_time linked to task
184  public $tobill;
185 
189  public $billed;
190 
191  // Properties to store project information
192  public $projectref;
193  public $projectstatus;
194  public $projectlabel;
195  public $opp_amount;
196  public $opp_percent;
197  public $fk_opp_status;
198  public $usage_bill_time;
199  public $public;
200  public $array_options_project;
201 
202  // Properties to store thirdparty of project information
203  public $socid;
204  public $thirdparty_id;
205  public $thirdparty_name;
206  public $thirdparty_email;
207 
208  // store parent ref and position
209  public $task_parent_ref;
210  public $task_parent_position;
211 
212 
213 
217  public $budget_amount;
218 
222  public $project_budget_amount;
223 
227  const STATUS_DRAFT = 0;
228 
232  const STATUS_VALIDATED = 1;
233 
237  const STATUS_CLOSED = 3;
238 
243 
247  const STATUS_CANCELED = 9;
248 
249 
255  public function __construct($db)
256  {
257  $this->db = $db;
258  }
259 
260 
268  public function create($user, $notrigger = 0)
269  {
270  global $conf, $langs;
271 
272  //For the date
273  $now = dol_now();
274 
275  $error = 0;
276 
277  // Clean parameters
278  $this->label = trim($this->label);
279  $this->description = trim($this->description);
280  $this->note_public = trim($this->note_public);
281  $this->note_private = trim($this->note_private);
282 
283  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
284  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
285  return -1;
286  }
287 
288  // Insert request
289  $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
290  $sql .= "entity";
291  $sql .= ", fk_projet";
292  $sql .= ", ref";
293  $sql .= ", fk_task_parent";
294  $sql .= ", label";
295  $sql .= ", description";
296  $sql .= ", note_public";
297  $sql .= ", note_private";
298  $sql .= ", datec";
299  $sql .= ", fk_user_creat";
300  $sql .= ", dateo";
301  $sql .= ", datee";
302  $sql .= ", planned_workload";
303  $sql .= ", progress";
304  $sql .= ", budget_amount";
305  $sql .= ", priority";
306  $sql .= ") VALUES (";
307  $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
308  $sql .= ", ".((int) $this->fk_project);
309  $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
310  $sql .= ", ".((int) $this->fk_task_parent);
311  $sql .= ", '".$this->db->escape($this->label)."'";
312  $sql .= ", '".$this->db->escape($this->description)."'";
313  $sql .= ", '".$this->db->escape($this->note_public)."'";
314  $sql .= ", '".$this->db->escape($this->note_private)."'";
315  $sql .= ", '".$this->db->idate($now)."'";
316  $sql .= ", ".((int) $user->id);
317  $sql .= ", ".(isDolTms($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : 'null');
318  $sql .= ", ".(isDolTms($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : 'null');
319  $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
320  $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
321  $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
322  $sql .= ", ".(($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null');
323  $sql .= ")";
324 
325  $this->db->begin();
326 
327  dol_syslog(get_class($this)."::create", LOG_DEBUG);
328  $resql = $this->db->query($sql);
329  if (!$resql) {
330  $error++;
331  $this->errors[] = "Error ".$this->db->lasterror();
332  }
333 
334  if (!$error) {
335  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
336  // Update extrafield
337  $result = $this->insertExtraFields();
338  if ($result < 0) {
339  $error++;
340  }
341  }
342 
343  if (!$error) {
344  if (!$notrigger) {
345  // Call trigger
346  $result = $this->call_trigger('TASK_CREATE', $user);
347  if ($result < 0) {
348  $error++;
349  }
350  // End call triggers
351  }
352  }
353 
354  // Commit or rollback
355  if ($error) {
356  foreach ($this->errors as $errmsg) {
357  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
358  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
359  }
360  $this->db->rollback();
361  return -1 * $error;
362  } else {
363  $this->db->commit();
364  return $this->id;
365  }
366  }
367 
368 
377  public function fetch($id, $ref = '', $loadparentdata = 0)
378  {
379  $sql = "SELECT";
380  $sql .= " t.rowid,";
381  $sql .= " t.ref,";
382  $sql .= " t.entity,";
383  $sql .= " t.fk_projet as fk_project,";
384  $sql .= " t.fk_task_parent,";
385  $sql .= " t.label,";
386  $sql .= " t.description,";
387  $sql .= " t.duration_effective,";
388  $sql .= " t.planned_workload,";
389  $sql .= " t.datec,";
390  $sql .= " t.dateo as date_start,";
391  $sql .= " t.datee as date_end,";
392  $sql .= " t.fk_user_creat,";
393  $sql .= " t.fk_user_valid,";
394  $sql .= " t.fk_statut as status,";
395  $sql .= " t.progress,";
396  $sql .= " t.budget_amount,";
397  $sql .= " t.priority,";
398  $sql .= " t.note_private,";
399  $sql .= " t.note_public,";
400  $sql .= " t.rang";
401  if (!empty($loadparentdata)) {
402  $sql .= ", t2.ref as task_parent_ref";
403  $sql .= ", t2.rang as task_parent_position";
404  }
405  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
406  if (!empty($loadparentdata)) {
407  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
408  }
409  $sql .= " WHERE ";
410  if (!empty($ref)) {
411  $sql .= "entity IN (".getEntity('project').")";
412  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
413  } else {
414  $sql .= "t.rowid = ".((int) $id);
415  }
416 
417  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
418  $resql = $this->db->query($sql);
419  if ($resql) {
420  $num_rows = $this->db->num_rows($resql);
421 
422  if ($num_rows) {
423  $obj = $this->db->fetch_object($resql);
424 
425  $this->id = $obj->rowid;
426  $this->ref = $obj->ref;
427  $this->entity = $obj->entity;
428  $this->fk_project = $obj->fk_project;
429  $this->fk_task_parent = $obj->fk_task_parent;
430  $this->label = $obj->label;
431  $this->description = $obj->description;
432  $this->duration_effective = $obj->duration_effective;
433  $this->planned_workload = $obj->planned_workload;
434  $this->date_c = $this->db->jdate($obj->datec);
435  $this->date_start = $this->db->jdate($obj->date_start);
436  $this->date_end = $this->db->jdate($obj->date_end);
437  $this->fk_user_creat = $obj->fk_user_creat;
438  $this->fk_user_valid = $obj->fk_user_valid;
439  $this->fk_statut = $obj->status;
440  $this->status = $obj->status;
441  $this->progress = $obj->progress;
442  $this->budget_amount = $obj->budget_amount;
443  $this->priority = $obj->priority;
444  $this->note_private = $obj->note_private;
445  $this->note_public = $obj->note_public;
446  $this->rang = $obj->rang;
447 
448  if (!empty($loadparentdata)) {
449  $this->task_parent_ref = $obj->task_parent_ref;
450  $this->task_parent_position = $obj->task_parent_position;
451  }
452 
453  // Retrieve all extrafield
454  $this->fetch_optionals();
455  }
456 
457  $this->db->free($resql);
458 
459  if ($num_rows) {
460  return 1;
461  } else {
462  return 0;
463  }
464  } else {
465  $this->error = "Error ".$this->db->lasterror();
466  return -1;
467  }
468  }
469 
470 
478  public function update($user = null, $notrigger = 0)
479  {
480  global $conf, $langs;
481  $error = 0;
482 
483  // Clean parameters
484  if (isset($this->fk_project)) {
485  $this->fk_project = (int) $this->fk_project;
486  }
487  if (isset($this->ref)) {
488  $this->ref = trim($this->ref);
489  }
490  if (isset($this->fk_task_parent)) {
491  $this->fk_task_parent = (int) $this->fk_task_parent;
492  }
493  if (isset($this->label)) {
494  $this->label = trim($this->label);
495  }
496  if (isset($this->description)) {
497  $this->description = trim($this->description);
498  }
499  if (isset($this->note_public)) {
500  $this->note_public = trim($this->note_public);
501  }
502  if (isset($this->note_private)) {
503  $this->note_private = trim($this->note_private);
504  }
505  if (isset($this->duration_effective)) {
506  $this->duration_effective = trim($this->duration_effective);
507  }
508  if (isset($this->planned_workload)) {
509  $this->planned_workload = trim($this->planned_workload);
510  }
511  if (isset($this->budget_amount)) {
512  $this->budget_amount = (float) $this->budget_amount;
513  }
514 
515  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
516  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
517  return -1;
518  }
519 
520  // Check parameters
521  // Put here code to add control on parameters values
522 
523  // Update request
524  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
525  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
526  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
527  $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
528  $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
529  $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
530  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
531  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
532  $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
533  $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
534  $sql .= " dateo=".(isDolTms($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
535  $sql .= " datee=".(isDolTms($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
536  $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
537  $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
538  $sql .= " rang=".((!empty($this->rang)) ? ((int) $this->rang) : "0").",";
539  $sql .= " priority=".((!empty($this->priority)) ? ((int) $this->priority) : "0");
540  $sql .= " WHERE rowid=".((int) $this->id);
541 
542  $this->db->begin();
543 
544  dol_syslog(get_class($this)."::update", LOG_DEBUG);
545  $resql = $this->db->query($sql);
546  if (!$resql) {
547  $error++;
548  $this->errors[] = "Error ".$this->db->lasterror();
549  }
550 
551  // Update extrafield
552  if (!$error) {
553  $result = $this->insertExtraFields();
554  if ($result < 0) {
555  $error++;
556  }
557  }
558 
559  if (!$error && getDolGlobalString('PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE')) {
560  // Close the parent project if it is open (validated) and its tasks are 100% completed
561  $project = new Project($this->db);
562  if ($project->fetch($this->fk_project) > 0) {
563  if ($project->statut == Project::STATUS_VALIDATED) {
564  $project->getLinesArray(null); // this method does not return <= 0 if fails
565  $projectCompleted = array_reduce(
566  $project->lines,
572  static function ($allTasksCompleted, $task) {
573  return $allTasksCompleted && $task->progress >= 100;
574  },
575  1
576  );
577  if ($projectCompleted) {
578  if ($project->setClose($user) <= 0) {
579  $error++;
580  }
581  }
582  }
583  } else {
584  $error++;
585  }
586  if ($error) {
587  $this->errors[] = $project->error;
588  }
589  }
590 
591  if (!$error) {
592  if (!$notrigger) {
593  // Call trigger
594  $result = $this->call_trigger('TASK_MODIFY', $user);
595  if ($result < 0) {
596  $error++;
597  }
598  // End call triggers
599  }
600  }
601 
602  if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
603  // We remove directory
604  if ($conf->project->dir_output) {
605  $project = new Project($this->db);
606  $project->fetch($this->fk_project);
607 
608  $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
609  $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
610  if (file_exists($olddir)) {
611  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
612  $res = dol_move_dir($olddir, $newdir);
613  if (!$res) {
614  $langs->load("errors");
615  $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
616  $error++;
617  }
618  }
619  }
620  }
621 
622  // Commit or rollback
623  if ($error) {
624  foreach ($this->errors as $errmsg) {
625  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
626  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
627  }
628  $this->db->rollback();
629  return -1 * $error;
630  } else {
631  $this->db->commit();
632  return 1;
633  }
634  }
635 
636 
644  public function delete($user, $notrigger = 0)
645  {
646  global $conf;
647  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
648 
649  $error = 0;
650 
651  $this->db->begin();
652 
653  if ($this->hasChildren() > 0) {
654  dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
655  $this->error = 'ErrorRecordHasSubTasks';
656  $this->db->rollback();
657  return 0;
658  }
659 
660  $objectisused = $this->isObjectUsed($this->id);
661  if (!empty($objectisused)) {
662  dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
663  $this->error = 'ErrorRecordHasChildren';
664  $this->db->rollback();
665  return 0;
666  }
667 
668  if (!$error) {
669  // Delete linked contacts
670  $res = $this->delete_linked_contact();
671  if ($res < 0) {
672  $this->error = 'ErrorFailToDeleteLinkedContact';
673  //$error++;
674  $this->db->rollback();
675  return 0;
676  }
677  }
678 
679  if (!$error) {
680  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_time";
681  $sql .= " WHERE fk_element = ".((int) $this->id)." AND elementtype = 'task'";
682 
683  $resql = $this->db->query($sql);
684  if (!$resql) {
685  $error++;
686  $this->errors[] = "Error ".$this->db->lasterror();
687  }
688  }
689 
690  if (!$error) {
691  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
692  $sql .= " WHERE fk_object = ".((int) $this->id);
693 
694  $resql = $this->db->query($sql);
695  if (!$resql) {
696  $error++;
697  $this->errors[] = "Error ".$this->db->lasterror();
698  }
699  }
700 
701  if (!$error) {
702  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
703  $sql .= " WHERE rowid=".((int) $this->id);
704 
705  $resql = $this->db->query($sql);
706  if (!$resql) {
707  $error++;
708  $this->errors[] = "Error ".$this->db->lasterror();
709  }
710  }
711 
712  if (!$error) {
713  if (!$notrigger) {
714  // Call trigger
715  $result = $this->call_trigger('TASK_DELETE', $user);
716  if ($result < 0) {
717  $error++;
718  }
719  // End call triggers
720  }
721  }
722 
723  // Commit or rollback
724  if ($error) {
725  foreach ($this->errors as $errmsg) {
726  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
727  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
728  }
729  $this->db->rollback();
730  return -1 * $error;
731  } else {
732  //Delete associated link file
733  if ($conf->project->dir_output) {
734  $projectstatic = new Project($this->db);
735  $projectstatic->fetch($this->fk_project);
736 
737  $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
738  dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
739  if (file_exists($dir)) {
740  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
741  $res = @dol_delete_dir_recursive($dir);
742  if (!$res) {
743  $this->error = 'ErrorFailToDeleteDir';
744  $this->db->rollback();
745  return 0;
746  }
747  }
748  }
749 
750  $this->db->commit();
751 
752  return 1;
753  }
754  }
755 
761  public function hasChildren()
762  {
763  $error = 0;
764  $ret = 0;
765 
766  $sql = "SELECT COUNT(*) as nb";
767  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
768  $sql .= " WHERE fk_task_parent = ".((int) $this->id);
769 
770  dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
771  $resql = $this->db->query($sql);
772  if (!$resql) {
773  $error++;
774  $this->errors[] = "Error ".$this->db->lasterror();
775  } else {
776  $obj = $this->db->fetch_object($resql);
777  if ($obj) {
778  $ret = $obj->nb;
779  }
780  $this->db->free($resql);
781  }
782 
783  if (!$error) {
784  return $ret;
785  } else {
786  return -1;
787  }
788  }
789 
795  public function hasTimeSpent()
796  {
797  $error = 0;
798  $ret = 0;
799 
800  $sql = "SELECT COUNT(*) as nb";
801  $sql .= " FROM ".MAIN_DB_PREFIX."element_time";
802  $sql .= " WHERE fk_element = ".((int) $this->id);
803  $sql .= " AND elementtype = 'task'";
804 
805  dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
806  $resql = $this->db->query($sql);
807  if (!$resql) {
808  $error++;
809  $this->errors[] = "Error ".$this->db->lasterror();
810  } else {
811  $obj = $this->db->fetch_object($resql);
812  if ($obj) {
813  $ret = $obj->nb;
814  }
815  $this->db->free($resql);
816  }
817 
818  if (!$error) {
819  return $ret;
820  } else {
821  return -1;
822  }
823  }
824 
825 
833  public function getTooltipContentArray($params)
834  {
835  global $langs;
836 
837  $langs->load('projects');
838 
839  $datas = [];
840  $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
841  if (!empty($this->ref)) {
842  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
843  }
844  if (!empty($this->label)) {
845  $datas['label'] = '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
846  }
847  if ($this->date_start || $this->date_end) {
848  $datas['range'] = "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
849  }
850 
851  return $datas;
852  }
853 
866  public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
867  {
868  global $action, $conf, $hookmanager, $langs;
869 
870  if (!empty($conf->dol_no_mouse_hover)) {
871  $notooltip = 1; // Force disable tooltips
872  }
873 
874  $result = '';
875  $params = [
876  'id' => $this->id,
877  'objecttype' => $this->element,
878  ];
879  $classfortooltip = 'classfortooltip';
880  $dataparams = '';
881  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
882  $classfortooltip = 'classforajaxtooltip';
883  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
884  $label = '';
885  } else {
886  $label = implode($this->getTooltipContentArray($params));
887  }
888 
889  $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
890  // Add param to save lastsearch_values or not
891  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
892  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
893  $add_save_lastsearch_values = 1;
894  }
895  if ($add_save_lastsearch_values) {
896  $url .= '&save_lastsearch_values=1';
897  }
898 
899  $linkclose = '';
900  if (empty($notooltip)) {
901  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
902  $label = $langs->trans("ShowTask");
903  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
904  }
905  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
906  $linkclose .= $dataparams.' class="'.$classfortooltip.' nowraponall"';
907  } else {
908  $linkclose .= ' class="nowraponall"';
909  }
910 
911  $linkstart = '<a href="'.$url.'"';
912  $linkstart .= $linkclose.'>';
913  $linkend = '</a>';
914 
915  $picto = 'projecttask';
916 
917  $result .= $linkstart;
918  if ($withpicto) {
919  $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
920  }
921  if ($withpicto != 2) {
922  $result .= $this->ref;
923  }
924  $result .= $linkend;
925  if ($withpicto != 2) {
926  $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
927  }
928 
929  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
930  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
931  if ($reshook > 0) {
932  $result = $hookmanager->resPrint;
933  } else {
934  $result .= $hookmanager->resPrint;
935  }
936 
937  return $result;
938  }
939 
947  public function initAsSpecimen()
948  {
949  global $user;
950 
951  $this->id = 0;
952 
953  $this->fk_project = 0;
954  $this->ref = 'TK01';
955  $this->fk_task_parent = 0;
956  $this->label = 'Specimen task TK01';
957  $this->duration_effective = '';
958  $this->fk_user_creat = $user->id;
959  $this->progress = 25;
960  $this->status = 0;
961  $this->priority = 0;
962  $this->note_private = 'This is a specimen private note';
963  $this->note_public = 'This is a specimen public note';
964 
965  return 1;
966  }
967 
991  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 = '')
992  {
993  global $hookmanager;
994 
995  $tasks = array();
996 
997  //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
998 
999  // List of tasks (does not care about permissions. Filtering will be done later)
1000  $sql = "SELECT ";
1001  if ($filteronprojuser > 0 || $filterontaskuser > 0) {
1002  $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
1003  }
1004  $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
1005  $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,";
1006  $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
1007  $sql .= " t.budget_amount,";
1008  $sql .= " t.note_public, t.note_private,";
1009  $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
1010  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
1011  if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
1012  if (!empty($extrafields->attributes['projet']['label'])) {
1013  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1014  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
1015  }
1016  }
1017  if (!empty($extrafields->attributes['projet_task']['label'])) {
1018  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1019  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
1020  }
1021  }
1022  }
1023  if ($includebilltime) {
1024  $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";
1025  }
1026 
1027  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1028  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1029  if ($loadextras) {
1030  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
1031  }
1032 
1033  if ($mode == 0) {
1034  if ($filteronprojuser > 0) {
1035  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1036  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1037  }
1038  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1039  if ($loadextras) {
1040  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
1041  }
1042  if ($includebilltime) {
1043  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1044  }
1045  if ($filterontaskuser > 0) {
1046  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1047  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1048  }
1049  $sql .= " WHERE p.entity IN (".getEntity('project').")";
1050  $sql .= " AND t.fk_projet = p.rowid";
1051  } elseif ($mode == 1) {
1052  if ($filteronprojuser > 0) {
1053  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1054  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1055  }
1056  if ($filterontaskuser > 0) {
1057  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
1058  if ($includebilltime) {
1059  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1060  }
1061  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
1062  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
1063  } else {
1064  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
1065  if ($includebilltime) {
1066  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
1067  }
1068  }
1069  $sql .= " WHERE p.entity IN (".getEntity('project').")";
1070  } else {
1071  return 'BadValueForParameterMode';
1072  }
1073 
1074  if ($filteronprojuser > 0) {
1075  $sql .= " AND p.rowid = ec.element_id";
1076  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1077  $sql .= " AND ctc.element = 'project'";
1078  $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
1079  $sql .= " AND ec.statut = 4";
1080  $sql .= " AND ctc.source = 'internal'";
1081  }
1082  if ($filterontaskuser > 0) {
1083  $sql .= " AND t.fk_projet = p.rowid";
1084  $sql .= " AND p.rowid = ec2.element_id";
1085  $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
1086  $sql .= " AND ctc2.element = 'project_task'";
1087  $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
1088  $sql .= " AND ec2.statut = 4";
1089  $sql .= " AND ctc2.source = 'internal'";
1090  }
1091  if ($socid) {
1092  $sql .= " AND p.fk_soc = ".((int) $socid);
1093  }
1094  if ($projectid) {
1095  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1096  }
1097  if ($filteronproj) {
1098  $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
1099  }
1100  if ($filteronprojstatus && (int) $filteronprojstatus != '-1') {
1101  $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
1102  }
1103  if ($morewherefilter) {
1104  $sql .= $morewherefilter;
1105  }
1106 
1107  // Add where from extra fields
1108  $extrafieldsobjectkey = 'projet_task';
1109  $extrafieldsobjectprefix = 'efpt.';
1110  global $db, $conf; // needed for extrafields_list_search_sql.tpl
1111  include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
1112 
1113  // Add where from hooks
1114  $parameters = array();
1115  $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
1116  $sql .= $hookmanager->resPrint;
1117  if ($includebilltime) {
1118  $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
1119  $sql .= " t.datec, t.dateo, t.datee, t.tms,";
1120  $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
1121  $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
1122  $sql .= " t.budget_amount,";
1123  $sql .= " t.note_public, t.note_private,";
1124  $sql .= " s.rowid, s.nom, s.email,";
1125  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
1126  if ($loadextras) {
1127  if (!empty($extrafields->attributes['projet']['label'])) {
1128  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1129  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
1130  }
1131  }
1132  if (!empty($extrafields->attributes['projet_task']['label'])) {
1133  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1134  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
1135  }
1136  }
1137  }
1138  }
1139 
1140  if ($sortfield && $sortorder) {
1141  $sql .= $this->db->order($sortfield, $sortorder);
1142  } else {
1143  $sql .= " ORDER BY p.ref, t.rang, t.dateo";
1144  }
1145 
1146  //print $sql;exit;
1147  dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
1148  $resql = $this->db->query($sql);
1149  if ($resql) {
1150  $num = $this->db->num_rows($resql);
1151  $i = 0;
1152  // Loop on each record found, so each couple (project id, task id)
1153  while ($i < $num) {
1154  $error = 0;
1155 
1156  $obj = $this->db->fetch_object($resql);
1157 
1158  if ($loadRoleMode) {
1159  if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
1160  if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
1161  $error++;
1162  }
1163  }
1164  if (is_object($usert)) { // If we ask a filter on a user affected to a task
1165  if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
1166  $error++;
1167  }
1168  }
1169  }
1170 
1171  if (!$error) {
1172  $tasks[$i] = new Task($this->db);
1173  $tasks[$i]->id = $obj->taskid;
1174  $tasks[$i]->ref = $obj->taskref;
1175  $tasks[$i]->fk_project = $obj->projectid;
1176 
1177  // Data from project
1178  $tasks[$i]->projectref = $obj->ref;
1179  $tasks[$i]->projectlabel = $obj->plabel;
1180  $tasks[$i]->projectstatus = $obj->projectstatus;
1181  $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1182  $tasks[$i]->opp_amount = $obj->opp_amount;
1183  $tasks[$i]->opp_percent = $obj->opp_percent;
1184  $tasks[$i]->budget_amount = $obj->budget_amount;
1185  $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1186  $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1187 
1188  $tasks[$i]->label = $obj->label;
1189  $tasks[$i]->description = $obj->description;
1190 
1191  $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1192  $tasks[$i]->note_public = $obj->note_public;
1193  $tasks[$i]->note_private = $obj->note_private;
1194  $tasks[$i]->duration_effective = $obj->duration_effective;
1195  $tasks[$i]->planned_workload = $obj->planned_workload;
1196 
1197  if ($includebilltime) {
1198  // Data summed from element_time linked to task
1199  $tasks[$i]->tobill = $obj->tobill;
1200  $tasks[$i]->billed = $obj->billed;
1201  }
1202 
1203  $tasks[$i]->progress = $obj->progress;
1204  $tasks[$i]->fk_statut = $obj->status;
1205  $tasks[$i]->status = $obj->status;
1206  $tasks[$i]->public = $obj->public;
1207  $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1208  $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1209  $tasks[$i]->rang = $obj->rang;
1210  $tasks[$i]->priority = $obj->priority;
1211 
1212  $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1213  $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1214  $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1215  $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1216 
1217  if ($loadextras) {
1218  if (!empty($extrafields->attributes['projet']['label'])) {
1219  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1220  if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1221  $tmpvar = 'options_'.$key;
1222  $tasks[$i]->array_options_project['options_'.$key] = $obj->$tmpvar;
1223  }
1224  }
1225  }
1226  }
1227 
1228  if ($loadextras) {
1229  $tasks[$i]->fetch_optionals();
1230  }
1231  }
1232 
1233  $i++;
1234  }
1235  $this->db->free($resql);
1236  } else {
1237  dol_print_error($this->db);
1238  }
1239 
1240  return $tasks;
1241  }
1242 
1253  public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1254  {
1255  $arrayroles = array();
1256 
1257  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".json_encode(is_object($userp))." usert=".json_encode(is_object($usert))." projectid=".$projectid." taskid=".$taskid);
1258 
1259  // We want role of user for a projet or role of user for a task. Both are not possible.
1260  if (empty($userp) && empty($usert)) {
1261  $this->error = "CallWithWrongParameters";
1262  return -1;
1263  }
1264  if (!empty($userp) && !empty($usert)) {
1265  $this->error = "CallWithWrongParameters";
1266  return -1;
1267  }
1268 
1269  /* Liste des taches et role sur les projects ou taches */
1270  $sql = "SELECT ";
1271  if ($userp) {
1272  $sql .= " p.rowid as pid,";
1273  } else {
1274  $sql .= " pt.rowid as pid,";
1275  }
1276  $sql .= " ec.element_id, ctc.code, ctc.source";
1277  if ($userp) {
1278  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1279  }
1280  if ($usert && $filteronprojstatus > -1) {
1281  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1282  }
1283  if ($usert && $filteronprojstatus <= -1) {
1284  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1285  }
1286  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1287  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1288  if ($userp) {
1289  $sql .= " WHERE p.rowid = ec.element_id";
1290  } else {
1291  $sql .= " WHERE pt.rowid = ec.element_id";
1292  }
1293  if ($userp && $filteronprojstatus > -1) {
1294  $sql .= " AND p.fk_statut = ".((int) $filteronprojstatus);
1295  }
1296  if ($usert && $filteronprojstatus > -1) {
1297  $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1298  }
1299  if ($userp) {
1300  $sql .= " AND ctc.element = 'project'";
1301  }
1302  if ($usert) {
1303  $sql .= " AND ctc.element = 'project_task'";
1304  }
1305  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1306  if ($userp) {
1307  $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1308  }
1309  if ($usert) {
1310  $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1311  }
1312  $sql .= " AND ec.statut = 4";
1313  $sql .= " AND ctc.source = 'internal'";
1314  if ($projectid) {
1315  if ($userp) {
1316  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1317  }
1318  if ($usert) {
1319  $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1320  }
1321  }
1322  if ($taskid) {
1323  if ($userp) {
1324  $sql .= " ERROR SHOULD NOT HAPPENS";
1325  }
1326  if ($usert) {
1327  $sql .= " AND pt.rowid = ".((int) $taskid);
1328  }
1329  }
1330  //print $sql;
1331 
1332  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1333  $resql = $this->db->query($sql);
1334  if ($resql) {
1335  $num = $this->db->num_rows($resql);
1336  $i = 0;
1337  while ($i < $num) {
1338  $obj = $this->db->fetch_object($resql);
1339  if (empty($arrayroles[$obj->pid])) {
1340  $arrayroles[$obj->pid] = $obj->code;
1341  } else {
1342  $arrayroles[$obj->pid] .= ','.$obj->code;
1343  }
1344  $i++;
1345  }
1346  $this->db->free($resql);
1347  } else {
1348  dol_print_error($this->db);
1349  }
1350 
1351  return $arrayroles;
1352  }
1353 
1354 
1361  public function getListContactId($source = 'internal')
1362  {
1363  $contactAlreadySelected = array();
1364  $tab = $this->liste_contact(-1, $source);
1365  //var_dump($tab);
1366  $num = count($tab);
1367  $i = 0;
1368  while ($i < $num) {
1369  if ($source == 'thirdparty') {
1370  $contactAlreadySelected[$i] = $tab[$i]['socid'];
1371  } else {
1372  $contactAlreadySelected[$i] = $tab[$i]['id'];
1373  }
1374  $i++;
1375  }
1376  return $contactAlreadySelected;
1377  }
1378 
1386  public function mergeContactTask($origin_id, $dest_id)
1387  {
1388  $error = 0;
1389  $origintask = new Task($this->db);
1390  $result = $origintask->fetch($origin_id);
1391  if ($result <= 0) {
1392  return false;
1393  }
1394 
1395  //Get list of origin contacts
1396  $arraycontactorigin = array_merge($origintask->liste_contact(-1, 'internal'), $origintask->liste_contact(-1, 'external'));
1397  if (is_array($arraycontactorigin)) {
1398  foreach ($arraycontactorigin as $key => $contact) {
1399  $result = $this->add_contact($contact["id"], $contact["fk_c_type_contact"], $contact["source"]);
1400  if ($result < 0) {
1401  return false;
1402  }
1403  }
1404  }
1405  return true;
1406  }
1407 
1415  public function mergeTimeSpentTask($origin_id, $dest_id)
1416  {
1417  $ret = true;
1418 
1419  $this->db->begin();
1420 
1421  $sql = "UPDATE ".MAIN_DB_PREFIX."element_time as et";
1422  $sql .= " SET et.fk_element = ".((int) $dest_id);
1423  $sql .= " WHERE et.elementtype = 'task'";
1424  $sql .= " AND et.fk_element = ".((int) $origin_id);
1425 
1426  dol_syslog(get_class($this)."::mergeTimeSpentTask", LOG_DEBUG);
1427  if (!$this->db->query($sql)) {
1428  $this->error = $this->db->lasterror();
1429  $ret = false;
1430  }
1431 
1432  if ($ret) {
1433  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1434  $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).")";
1435  $sql .= " WHERE rowid = ".((int) $dest_id);
1436 
1437  dol_syslog(get_class($this)."::mergeTimeSpentTask update project_task", LOG_DEBUG);
1438  if (!$this->db->query($sql)) {
1439  $this->error = $this->db->lasterror();
1440  $ret = false;
1441  }
1442  }
1443 
1444  if ($ret == true) {
1445  $this->db->commit();
1446  } else {
1447  $this->db->rollback();
1448  }
1449  return $ret;
1450  }
1451 
1459  public function addTimeSpent($user, $notrigger = 0)
1460  {
1461  global $langs;
1462 
1463  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1464 
1465  $ret = 0;
1466  $now = dol_now();
1467 
1468  // Check parameters
1469  if (!is_object($user)) {
1470  dol_print_error(null, "Method addTimeSpent was called with wrong parameter user");
1471  return -1;
1472  }
1473 
1474  // Clean parameters
1475  if (isset($this->timespent_note)) {
1476  $this->timespent_note = trim($this->timespent_note);
1477  }
1478  if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1479  $this->timespent_datehour = $this->timespent_date;
1480  }
1481 
1482  if (getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1483  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1484  $restrictBefore = dol_time_plus_duree(dol_now(), - getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'), 'm');
1485 
1486  if ($this->timespent_date < $restrictBefore) {
1487  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1488  $this->errors[] = $this->error;
1489  return -1;
1490  }
1491  }
1492 
1493  $this->db->begin();
1494 
1495  $timespent = new TimeSpent($this->db);
1496  $timespent->fk_element = $this->id;
1497  $timespent->elementtype = 'task';
1498  $timespent->element_date = $this->timespent_date;
1499  $timespent->element_datehour = $this->timespent_datehour;
1500  $timespent->element_date_withhour = $this->timespent_withhour;
1501  $timespent->element_duration = $this->timespent_duration;
1502  $timespent->fk_user = $this->timespent_fk_user;
1503  $timespent->fk_product = $this->timespent_fk_product;
1504  $timespent->note = $this->timespent_note;
1505  $timespent->datec = $this->db->idate($now);
1506 
1507  $result = $timespent->create($user);
1508 
1509  if ($result > 0) {
1510  $ret = $result;
1511  $this->timespent_id = $result;
1512 
1513  if (!$notrigger) {
1514  // Call trigger
1515  $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1516  if ($result < 0) {
1517  $ret = -1;
1518  }
1519  // End call triggers
1520  }
1521  } else {
1522  $this->error = $this->db->lasterror();
1523  $ret = -1;
1524  }
1525 
1526  if ($ret > 0) {
1527  // Recalculate amount of time spent for task and update denormalized field
1528  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1529  $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).")";
1530  if (isset($this->progress)) {
1531  $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1532  }
1533  $sql .= " WHERE rowid = ".((int) $this->id);
1534 
1535  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1536  if (!$this->db->query($sql)) {
1537  $this->error = $this->db->lasterror();
1538  $ret = -2;
1539  }
1540 
1541  // Update hourly rate of this time spent entry
1542  $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1543  if (!empty($resql_thm_user)) {
1544  $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1545  $timespent->thm = $obj_thm_user->thm;
1546  }
1547  $res_update = $timespent->update($user);
1548 
1549  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1550  if ($res_update <= 0) {
1551  $this->error = $this->db->lasterror();
1552  $ret = -2;
1553  }
1554  }
1555 
1556  if ($ret > 0) {
1557  $this->db->commit();
1558  } else {
1559  $this->db->rollback();
1560  }
1561  return $ret;
1562  }
1563 
1570  public function fetchTimeSpentOnTask($morewherefilter = '')
1571  {
1572  $arrayres = array();
1573 
1574  $sql = "SELECT";
1575  $sql .= " s.rowid as socid,";
1576  $sql .= " s.nom as thirdparty_name,";
1577  $sql .= " s.email as thirdparty_email,";
1578  $sql .= " ptt.rowid,";
1579  $sql .= " ptt.ref_ext,";
1580  $sql .= " ptt.fk_element as fk_task,";
1581  $sql .= " ptt.element_date as task_date,";
1582  $sql .= " ptt.element_datehour as task_datehour,";
1583  $sql .= " ptt.element_date_withhour as task_date_withhour,";
1584  $sql .= " ptt.element_duration as task_duration,";
1585  $sql .= " ptt.fk_user,";
1586  $sql .= " ptt.note,";
1587  $sql .= " ptt.thm,";
1588  $sql .= " pt.rowid as task_id,";
1589  $sql .= " pt.ref as task_ref,";
1590  $sql .= " pt.label as task_label,";
1591  $sql .= " p.rowid as project_id,";
1592  $sql .= " p.ref as project_ref,";
1593  $sql .= " p.title as project_label,";
1594  $sql .= " p.public as public";
1595  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1596  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1597  $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1598  $sql .= " AND ptt.elementtype = 'task'";
1599  $sql .= " AND pt.rowid = ".((int) $this->id);
1600  $sql .= " AND pt.entity IN (".getEntity('project').")";
1601  if ($morewherefilter) {
1602  $sql .= $morewherefilter;
1603  }
1604 
1605  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1606  $resql = $this->db->query($sql);
1607  if ($resql) {
1608  $num = $this->db->num_rows($resql);
1609 
1610  $i = 0;
1611  while ($i < $num) {
1612  $obj = $this->db->fetch_object($resql);
1613 
1614  $newobj = new stdClass();
1615 
1616  $newobj->socid = $obj->socid;
1617  $newobj->thirdparty_name = $obj->thirdparty_name;
1618  $newobj->thirdparty_email = $obj->thirdparty_email;
1619 
1620  $newobj->fk_project = $obj->project_id;
1621  $newobj->project_ref = $obj->project_ref;
1622  $newobj->project_label = $obj->project_label;
1623  $newobj->public = $obj->project_public;
1624 
1625  $newobj->fk_task = $obj->task_id;
1626  $newobj->task_ref = $obj->task_ref;
1627  $newobj->task_label = $obj->task_label;
1628 
1629  $newobj->timespent_line_id = $obj->rowid;
1630  $newobj->timespent_line_ref_ext = $obj->ref_ext;
1631  $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1632  $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1633  $newobj->timespent_line_withhour = $obj->task_date_withhour;
1634  $newobj->timespent_line_duration = $obj->task_duration;
1635  $newobj->timespent_line_fk_user = $obj->fk_user;
1636  $newobj->timespent_line_thm = $obj->thm; // hourly rate
1637  $newobj->timespent_line_note = $obj->note;
1638 
1639  $arrayres[] = $newobj;
1640 
1641  $i++;
1642  }
1643 
1644  $this->db->free($resql);
1645 
1646  $this->lines = $arrayres;
1647  return 1;
1648  } else {
1649  dol_print_error($this->db);
1650  $this->error = "Error ".$this->db->lasterror();
1651  return -1;
1652  }
1653  }
1654 
1655 
1663  public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1664  {
1665  if (is_object($userobj)) {
1666  $userid = $userobj->id;
1667  } else {
1668  $userid = $userobj; // old method
1669  }
1670 
1671  $id = $this->id;
1672  if (empty($id) && empty($userid)) {
1673  dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1674  return -1;
1675  }
1676 
1677  $result = array();
1678 
1679  $sql = "SELECT";
1680  $sql .= " MIN(t.element_datehour) as min_date,";
1681  $sql .= " MAX(t.element_datehour) as max_date,";
1682  $sql .= " SUM(t.element_duration) as total_duration,";
1683  $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1684  $sql .= " COUNT(t.rowid) as nblines,";
1685  $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1686  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1687  $sql .= " WHERE t.elementtype='task'";
1688  if ($morewherefilter) {
1689  $sql .= $morewherefilter;
1690  }
1691  if ($id > 0) {
1692  $sql .= " AND t.fk_element = ".((int) $id);
1693  }
1694  if ($userid > 0) {
1695  $sql .= " AND t.fk_user = ".((int) $userid);
1696  }
1697 
1698  dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1699  $resql = $this->db->query($sql);
1700  if ($resql) {
1701  $obj = $this->db->fetch_object($resql);
1702 
1703  $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1704  $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1705  $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1706 
1707  $this->timespent_min_date = $this->db->jdate($obj->min_date);
1708  $this->timespent_max_date = $this->db->jdate($obj->max_date);
1709  $this->timespent_total_duration = $obj->total_duration;
1710  $this->timespent_total_amount = $obj->total_amount;
1711  $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1712  $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1713 
1714  $this->db->free($resql);
1715  } else {
1716  dol_print_error($this->db);
1717  }
1718  return $result;
1719  }
1720 
1729  public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1730  {
1731  $id = $this->id;
1732 
1733  $result = array();
1734 
1735  $sql = "SELECT";
1736  $sql .= " SUM(t.element_duration) as nbseconds,";
1737  $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";
1738  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1739  $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id);
1740  if (is_object($fuser) && $fuser->id > 0) {
1741  $sql .= " AND fk_user = ".((int) $fuser->id);
1742  }
1743  if ($dates > 0) {
1744  $datefieldname = "element_datehour";
1745  $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1746  }
1747  if ($datee > 0) {
1748  $datefieldname = "element_datehour";
1749  $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1750  }
1751  //print $sql;
1752 
1753  dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1754  $resql = $this->db->query($sql);
1755  if ($resql) {
1756  $obj = $this->db->fetch_object($resql);
1757 
1758  $result['amount'] = $obj->amount;
1759  $result['nbseconds'] = $obj->nbseconds;
1760  $result['nblinesnull'] = $obj->nblinesnull;
1761 
1762  $this->db->free($resql);
1763  return $result;
1764  } else {
1765  dol_print_error($this->db);
1766  return $result;
1767  }
1768  }
1769 
1776  public function fetchTimeSpent($id)
1777  {
1778  $timespent = new TimeSpent($this->db);
1779  $timespent->fetch($id);
1780 
1781  dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1782 
1783  if ($timespent->id > 0) {
1784  $this->timespent_id = $timespent->id;
1785  $this->id = $timespent->fk_element;
1786  $this->timespent_date = $timespent->element_date;
1787  $this->timespent_datehour = $timespent->element_datehour;
1788  $this->timespent_withhour = $timespent->element_date_withhour;
1789  $this->timespent_duration = $timespent->element_duration;
1790  $this->timespent_fk_user = $timespent->fk_user;
1791  $this->timespent_fk_product = $timespent->fk_product;
1792  $this->timespent_thm = $timespent->thm; // hourly rate
1793  $this->timespent_note = $timespent->note;
1794 
1795  return 1;
1796  }
1797 
1798  return 0;
1799  }
1800 
1808  public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1809  {
1810  $arrayres = array();
1811 
1812  $sql = "SELECT";
1813  $sql .= " s.rowid as socid,";
1814  $sql .= " s.nom as thirdparty_name,";
1815  $sql .= " s.email as thirdparty_email,";
1816  $sql .= " ptt.rowid,";
1817  $sql .= " ptt.fk_element as fk_task,";
1818  $sql .= " ptt.element_date as task_date,";
1819  $sql .= " ptt.element_datehour as task_datehour,";
1820  $sql .= " ptt.element_date_withhour as task_date_withhour,";
1821  $sql .= " ptt.element_duration as task_duration,";
1822  $sql .= " ptt.fk_user,";
1823  $sql .= " ptt.note,";
1824  $sql .= " ptt.thm,";
1825  $sql .= " pt.rowid as task_id,";
1826  $sql .= " pt.ref as task_ref,";
1827  $sql .= " pt.label as task_label,";
1828  $sql .= " p.rowid as project_id,";
1829  $sql .= " p.ref as project_ref,";
1830  $sql .= " p.title as project_label,";
1831  $sql .= " p.public as public";
1832  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1833  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1834  $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1835  $sql .= " AND ptt.elementtype = 'task'";
1836  $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1837  $sql .= " AND pt.entity IN (".getEntity('project').")";
1838  if ($morewherefilter) {
1839  $sql .= $morewherefilter;
1840  }
1841 
1842  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1843  $resql = $this->db->query($sql);
1844  if ($resql) {
1845  $num = $this->db->num_rows($resql);
1846 
1847  $i = 0;
1848  while ($i < $num) {
1849  $obj = $this->db->fetch_object($resql);
1850 
1851  $newobj = new stdClass();
1852 
1853  $newobj->socid = $obj->socid;
1854  $newobj->thirdparty_name = $obj->thirdparty_name;
1855  $newobj->thirdparty_email = $obj->thirdparty_email;
1856 
1857  $newobj->fk_project = $obj->project_id;
1858  $newobj->project_ref = $obj->project_ref;
1859  $newobj->project_label = $obj->project_label;
1860  $newobj->public = $obj->project_public;
1861 
1862  $newobj->fk_task = $obj->task_id;
1863  $newobj->task_ref = $obj->task_ref;
1864  $newobj->task_label = $obj->task_label;
1865 
1866  $newobj->timespent_id = $obj->rowid;
1867  $newobj->timespent_date = $this->db->jdate($obj->task_date);
1868  $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1869  $newobj->timespent_withhour = $obj->task_date_withhour;
1870  $newobj->timespent_duration = $obj->task_duration;
1871  $newobj->timespent_fk_user = $obj->fk_user;
1872  $newobj->timespent_thm = $obj->thm; // hourly rate
1873  $newobj->timespent_note = $obj->note;
1874 
1875  $arrayres[] = $newobj;
1876 
1877  $i++;
1878  }
1879 
1880  $this->db->free($resql);
1881  } else {
1882  dol_print_error($this->db);
1883  $this->error = "Error ".$this->db->lasterror();
1884  return -1;
1885  }
1886 
1887  return $arrayres;
1888  }
1889 
1897  public function updateTimeSpent($user, $notrigger = 0)
1898  {
1899  global $conf, $langs;
1900 
1901  $ret = 0;
1902 
1903  // Check parameters
1904  if ($this->timespent_date == '') {
1905  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1906  return -1;
1907  }
1908  if (!($this->timespent_fk_user > 0)) {
1909  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1910  return -1;
1911  }
1912 
1913  // Clean parameters
1914  if (empty($this->timespent_datehour)) {
1915  $this->timespent_datehour = $this->timespent_date;
1916  }
1917  if (isset($this->timespent_note)) {
1918  $this->timespent_note = trim($this->timespent_note);
1919  }
1920 
1921  if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1922  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1923  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1924 
1925  if ($this->timespent_date < $restrictBefore) {
1926  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1927  $this->errors[] = $this->error;
1928  return -1;
1929  }
1930  }
1931 
1932  $this->db->begin();
1933 
1934  $timespent = new TimeSpent($this->db);
1935  $timespent->fetch($this->timespent_id);
1936  $timespent->element_date = $this->timespent_date;
1937  $timespent->element_datehour = $this->timespent_datehour;
1938  $timespent->element_date_withhour = $this->timespent_withhour;
1939  $timespent->element_duration = $this->timespent_duration;
1940  $timespent->fk_user = $this->timespent_fk_user;
1941  $timespent->fk_product = $this->timespent_fk_product;
1942  $timespent->note = $this->timespent_note;
1943  $timespent->invoice_id = $this->timespent_invoiceid;
1944  $timespent->invoice_line_id = $this->timespent_invoicelineid;
1945 
1946  dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1947  if ($timespent->update($user) > 0) {
1948  if (!$notrigger) {
1949  // Call trigger
1950  $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1951  if ($result < 0) {
1952  $this->db->rollback();
1953  $ret = -1;
1954  } else {
1955  $ret = 1;
1956  }
1957  // End call triggers
1958  } else {
1959  $ret = 1;
1960  }
1961  } else {
1962  $this->error = $this->db->lasterror();
1963  $this->db->rollback();
1964  $ret = -1;
1965  }
1966 
1967  if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) {
1968  if ($this->timespent_old_duration != $this->timespent_duration) {
1969  // Recalculate amount of time spent for task and update denormalized field
1970  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1971  $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) . ")";
1972  if (isset($this->progress)) {
1973  $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1974  }
1975  $sql .= " WHERE rowid = " . ((int) $this->id);
1976 
1977  dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1978  if (!$this->db->query($sql)) {
1979  $this->error = $this->db->lasterror();
1980  $this->db->rollback();
1981  $ret = -2;
1982  }
1983  }
1984 
1985  // Update hourly rate of this time spent entry, but only if it was not set initially
1986  $res_update = 1;
1987  if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) {
1988  $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1989  if (!empty($resql_thm_user)) {
1990  $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1991  $timespent->thm = $obj_thm_user->thm;
1992  }
1993  $res_update = $timespent->update($user);
1994  }
1995 
1996  dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1997  if ($res_update <= 0) {
1998  $this->error = $this->db->lasterror();
1999  $ret = -2;
2000  }
2001  }
2002 
2003  if ($ret >= 0) {
2004  $this->db->commit();
2005  }
2006  return $ret;
2007  }
2008 
2016  public function delTimeSpent($user, $notrigger = 0)
2017  {
2018  global $conf, $langs;
2019 
2020  $error = 0;
2021 
2022  if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
2023  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2024  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
2025 
2026  if ($this->timespent_date < $restrictBefore) {
2027  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
2028  $this->errors[] = $this->error;
2029  return -1;
2030  }
2031  }
2032 
2033  $this->db->begin();
2034 
2035  if (!$notrigger) {
2036  // Call trigger
2037  $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
2038  if ($result < 0) {
2039  $error++;
2040  }
2041  // End call triggers
2042  }
2043 
2044  if (!$error) {
2045  $timespent = new TimeSpent($this->db);
2046  $timespent->fetch($this->timespent_id);
2047 
2048  $res_del = $timespent->delete($user);
2049 
2050  if ($res_del < 0) {
2051  $error++;
2052  $this->errors[] = "Error ".$this->db->lasterror();
2053  }
2054  }
2055 
2056  if (!$error) {
2057  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
2058  $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
2059  $sql .= " WHERE rowid = ".((int) $this->id);
2060 
2061  dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
2062  if ($this->db->query($sql)) {
2063  $result = 0;
2064  } else {
2065  $this->error = $this->db->lasterror();
2066  $result = -2;
2067  }
2068  }
2069 
2070  // Commit or rollback
2071  if ($error) {
2072  foreach ($this->errors as $errmsg) {
2073  dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
2074  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2075  }
2076  $this->db->rollback();
2077  return -1 * $error;
2078  } else {
2079  $this->db->commit();
2080  return 1;
2081  }
2082  }
2083 
2098  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)
2099  {
2100  global $langs, $conf;
2101 
2102  $error = 0;
2103 
2104  //Use 00:00 of today if time is use on task.
2105  $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
2106 
2107  $datec = $now;
2108 
2109  $clone_task = new Task($this->db);
2110  $origin_task = new Task($this->db);
2111 
2112  $clone_task->context['createfromclone'] = 'createfromclone';
2113 
2114  $this->db->begin();
2115 
2116  // Load source object
2117  $clone_task->fetch($fromid);
2118  $clone_task->fetch_optionals();
2119  //var_dump($clone_task->array_options);exit;
2120 
2121  $origin_task->fetch($fromid);
2122 
2123  $defaultref = '';
2124  $obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
2125  if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').".php")) {
2126  require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON').'.php';
2127  $modTask = new $obj();
2128  $defaultref = $modTask->getNextValue(0, $clone_task);
2129  }
2130 
2131  $ori_project_id = $clone_task->fk_project;
2132 
2133  $clone_task->id = 0;
2134  $clone_task->ref = $defaultref;
2135  $clone_task->fk_project = $project_id;
2136  $clone_task->fk_task_parent = $parent_task_id;
2137  $clone_task->date_c = $datec;
2138  $clone_task->planned_workload = $origin_task->planned_workload;
2139  $clone_task->rang = $origin_task->rang;
2140  $clone_task->priority = $origin_task->priority;
2141 
2142  //Manage Task Date
2143  if ($clone_change_dt) {
2144  $projectstatic = new Project($this->db);
2145  $projectstatic->fetch($ori_project_id);
2146 
2147  //Origin project start date
2148  $orign_project_dt_start = $projectstatic->date_start;
2149 
2150  //Calculate new task start date with difference between origin proj start date and origin task start date
2151  if (!empty($clone_task->date_start)) {
2152  $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
2153  }
2154 
2155  //Calculate new task end date with difference between origin proj end date and origin task end date
2156  if (!empty($clone_task->date_end)) {
2157  $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
2158  }
2159  }
2160 
2161  if (!$clone_prog) {
2162  $clone_task->progress = 0;
2163  }
2164 
2165  // Create clone
2166  $result = $clone_task->create($user);
2167 
2168  // Other options
2169  if ($result < 0) {
2170  $this->error = $clone_task->error;
2171  $error++;
2172  }
2173  // End
2174  if ($error) {
2175  $clone_task_id = 0; // For static tool check
2176  } else {
2177  $clone_task_id = $clone_task->id;
2178  $clone_task_ref = $clone_task->ref;
2179 
2180  //Note Update
2181  if (!$clone_note) {
2182  $clone_task->note_private = '';
2183  $clone_task->note_public = '';
2184  } else {
2185  $this->db->begin();
2186  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
2187  if ($res < 0) {
2188  $this->error .= $clone_task->error;
2189  $error++;
2190  $this->db->rollback();
2191  } else {
2192  $this->db->commit();
2193  }
2194 
2195  $this->db->begin();
2196  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
2197  if ($res < 0) {
2198  $this->error .= $clone_task->error;
2199  $error++;
2200  $this->db->rollback();
2201  } else {
2202  $this->db->commit();
2203  }
2204  }
2205 
2206  //Duplicate file
2207  if ($clone_file) {
2208  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2209 
2210  //retrieve project origin ref to know folder to copy
2211  $projectstatic = new Project($this->db);
2212  $projectstatic->fetch($ori_project_id);
2213  $ori_project_ref = $projectstatic->ref;
2214 
2215  if ($ori_project_id != $project_id) {
2216  $projectstatic->fetch($project_id);
2217  $clone_project_ref = $projectstatic->ref;
2218  } else {
2219  $clone_project_ref = $ori_project_ref;
2220  }
2221 
2222  $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
2223  $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
2224 
2225  $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
2226  foreach ($filearray as $key => $file) {
2227  if (!file_exists($clone_task_dir)) {
2228  if (dol_mkdir($clone_task_dir) < 0) {
2229  $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
2230  $error++;
2231  }
2232  }
2233 
2234  $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
2235  if (is_numeric($rescopy) && $rescopy < 0) {
2236  $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
2237  $error++;
2238  }
2239  }
2240  }
2241 
2242  // clone affectation
2243  if ($clone_affectation) {
2244  $origin_task = new Task($this->db);
2245  $origin_task->fetch($fromid);
2246 
2247  foreach (array('internal', 'external') as $source) {
2248  $tab = $origin_task->liste_contact(-1, $source);
2249  $num = count($tab);
2250  $i = 0;
2251  while ($i < $num) {
2252  $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2253  if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2254  $langs->load("errors");
2255  $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2256  $error++;
2257  } else {
2258  if ($clone_task->error != '') {
2259  $this->error .= $clone_task->error;
2260  $error++;
2261  }
2262  }
2263  $i++;
2264  }
2265  }
2266  }
2267 
2268  if ($clone_time) {
2269  //TODO clone time of affectation
2270  }
2271  }
2272 
2273  unset($clone_task->context['createfromclone']);
2274 
2275  if (!$error) {
2276  $this->db->commit();
2277  return $clone_task_id;
2278  } else {
2279  $this->db->rollback();
2280  dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
2281  return -1;
2282  }
2283  }
2284 
2285 
2292  public function getLibStatut($mode = 0)
2293  {
2294  return $this->LibStatut($this->status, $mode);
2295  }
2296 
2297  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2305  public function LibStatut($status, $mode = 0)
2306  {
2307  // phpcs:enable
2308  global $langs;
2309 
2310  // list of Statut of the task
2311  $this->labelStatus[0] = 'Draft';
2312  $this->labelStatus[1] = 'ToDo';
2313  $this->labelStatus[2] = 'Running';
2314  $this->labelStatus[3] = 'Finish';
2315  $this->labelStatus[4] = 'Transfered';
2316  $this->labelStatusShort[0] = 'Draft';
2317  $this->labelStatusShort[1] = 'ToDo';
2318  $this->labelStatusShort[2] = 'Running';
2319  $this->labelStatusShort[3] = 'Completed';
2320  $this->labelStatusShort[4] = 'Transfered';
2321 
2322  if ($mode == 0) {
2323  return $langs->trans($this->labelStatus[$status]);
2324  } elseif ($mode == 1) {
2325  return $langs->trans($this->labelStatusShort[$status]);
2326  } elseif ($mode == 2) {
2327  if ($status == 0) {
2328  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0').' '.$langs->trans($this->labelStatusShort[$status]);
2329  } elseif ($status == 1) {
2330  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1').' '.$langs->trans($this->labelStatusShort[$status]);
2331  } elseif ($status == 2) {
2332  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3').' '.$langs->trans($this->labelStatusShort[$status]);
2333  } elseif ($status == 3) {
2334  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatusShort[$status]);
2335  } elseif ($status == 4) {
2336  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatusShort[$status]);
2337  } elseif ($status == 5) {
2338  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5').' '.$langs->trans($this->labelStatusShort[$status]);
2339  }
2340  } elseif ($mode == 3) {
2341  if ($status == 0) {
2342  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0');
2343  } elseif ($status == 1) {
2344  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1');
2345  } elseif ($status == 2) {
2346  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3');
2347  } elseif ($status == 3) {
2348  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2349  } elseif ($status == 4) {
2350  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2351  } elseif ($status == 5) {
2352  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5');
2353  }
2354  } elseif ($mode == 4) {
2355  if ($status == 0) {
2356  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0').' '.$langs->trans($this->labelStatus[$status]);
2357  } elseif ($status == 1) {
2358  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1').' '.$langs->trans($this->labelStatus[$status]);
2359  } elseif ($status == 2) {
2360  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3').' '.$langs->trans($this->labelStatus[$status]);
2361  } elseif ($status == 3) {
2362  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatus[$status]);
2363  } elseif ($status == 4) {
2364  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6').' '.$langs->trans($this->labelStatus[$status]);
2365  } elseif ($status == 5) {
2366  return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5').' '.$langs->trans($this->labelStatus[$status]);
2367  }
2368  } elseif ($mode == 5) {
2369  /*if ($status==0) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2370  elseif ($status==1) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2371  elseif ($status==2) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2372  elseif ($status==3) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2373  elseif ($status==4) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2374  elseif ($status==5) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2375  */
2376  //else return $this->progress.' %';
2377  return '&nbsp;';
2378  } elseif ($mode == 6) {
2379  /*if ($status==0) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2380  elseif ($status==1) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2381  elseif ($status==2) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2382  elseif ($status==3) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2383  elseif ($status==4) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2384  elseif ($status==5) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2385  */
2386  //else return $this->progress.' %';
2387  return '&nbsp;';
2388  }
2389  return "";
2390  }
2391 
2402  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2403  {
2404  $outputlangs->load("projects");
2405 
2406  if (!dol_strlen($modele)) {
2407  $modele = 'nodefault';
2408 
2409  if (!empty($this->model_pdf)) {
2410  $modele = $this->model_pdf;
2411  } elseif (getDolGlobalString('PROJECT_TASK_ADDON_PDF')) {
2412  $modele = getDolGlobalString('PROJECT_TASK_ADDON_PDF');
2413  }
2414  }
2415 
2416  $modelpath = "core/modules/project/task/doc/";
2417 
2418  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2419  }
2420 
2421 
2422  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2429  public function load_board($user)
2430  {
2431  // phpcs:enable
2432  global $conf, $langs;
2433 
2434  // For external user, no check is done on company because readability is managed by public status of project and assignment.
2435  //$socid = $user->socid;
2436  $socid = 0;
2437 
2438  $projectstatic = new Project($this->db);
2439  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2440 
2441  // List of tasks (does not care about permissions. Filtering will be done later)
2442  $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2443  $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2444  $sql .= " t.dateo as date_start, t.datee as date_end";
2445  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2446  //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2447  //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2448  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2449  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2450  $sql .= " AND p.fk_statut = 1";
2451  $sql .= " AND t.fk_projet = p.rowid";
2452  $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2453  if (!$user->hasRight('projet', 'all', 'lire')) {
2454  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2455  }
2456  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2457  //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).")";
2458  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2459  // 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))";
2460 
2461  //print $sql;
2462  $resql = $this->db->query($sql);
2463  if ($resql) {
2464  $task_static = new Task($this->db);
2465 
2466  $response = new WorkboardResponse();
2467  $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2468  $response->label = $langs->trans("OpenedTasks");
2469  if ($user->hasRight("projet", "all", "lire")) {
2470  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2471  } else {
2472  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2473  }
2474  $response->img = img_object('', "task");
2475 
2476  // This assignment in condition is not a bug. It allows walking the results.
2477  while ($obj = $this->db->fetch_object($resql)) {
2478  $response->nbtodo++;
2479 
2480  $task_static->projectstatus = $obj->projectstatus;
2481  $task_static->progress = $obj->progress;
2482  $task_static->fk_statut = $obj->status;
2483  $task_static->status = $obj->status;
2484  $task_static->date_start = $this->db->jdate($obj->date_start);
2485  $task_static->date_end = $this->db->jdate($obj->date_end);
2486 
2487  if ($task_static->hasDelay()) {
2488  $response->nbtodolate++;
2489  }
2490  }
2491 
2492  return $response;
2493  } else {
2494  $this->error = $this->db->error();
2495  return -1;
2496  }
2497  }
2498 
2499 
2505  public function loadStateBoard()
2506  {
2507  global $user;
2508 
2509  $mine = 0;
2510  $socid = $user->socid;
2511 
2512  $projectstatic = new Project($this->db);
2513  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2514 
2515  // List of tasks (does not care about permissions. Filtering will be done later)
2516  $sql = "SELECT count(p.rowid) as nb";
2517  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2518  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2519  if (!$user->hasRight('societe', 'client', 'voir')) {
2520  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2521  }
2522  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2523  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2524  $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2525  if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
2526  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2527  }
2528  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2529  //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).")";
2530  if ($socid) {
2531  $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2532  }
2533  if (!$user->hasRight('societe', 'client', 'voir')) {
2534  $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2535  }
2536 
2537  $resql = $this->db->query($sql);
2538  if ($resql) {
2539  // This assignment in condition is not a bug. It allows walking the results.
2540  while ($obj = $this->db->fetch_object($resql)) {
2541  $this->nb["tasks"] = $obj->nb;
2542  }
2543  $this->db->free($resql);
2544  return 1;
2545  } else {
2546  dol_print_error($this->db);
2547  $this->error = $this->db->error();
2548  return -1;
2549  }
2550  }
2551 
2557  public function hasDelay()
2558  {
2559  global $conf;
2560 
2561  if (!($this->progress >= 0 && $this->progress < 100)) {
2562  return false;
2563  }
2564 
2565  $now = dol_now();
2566 
2567  $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
2568 
2569  return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2570  }
2571 
2579  public function getKanbanView($option = '', $arraydata = null)
2580  {
2581  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2582 
2583  $return = '<div class="box-flex-item box-flex-grow-zero">';
2584  $return .= '<div class="info-box info-box-sm info-box-kanban">';
2585  $return .= '<span class="info-box-icon bg-infobox-action">';
2586  $return .= img_picto('', $this->picto);
2587  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2588  $return .= '</span>';
2589  $return .= '<div class="info-box-content">';
2590  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2591  if ($selected >= 0) {
2592  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2593  }
2594  if (!empty($arraydata['projectlink'])) {
2595  //$tmpproject = $arraydata['project'];
2596  //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
2597  $return .= '<br><span class="info-box-status ">'.$arraydata['projectlink'].'</span>';
2598  }
2599  if (property_exists($this, 'budget_amount')) {
2600  //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
2601  }
2602  if (property_exists($this, 'duration_effective')) {
2603  $return .= '<br><div class="info-box-label progressinkanban paddingtop">'.getTaskProgressView($this, false, true).'</div>';
2604  }
2605  $return .= '</div>';
2606  $return .= '</div>';
2607  $return .= '</div>';
2608 
2609  return $return;
2610  }
2611 
2619  public function mergeTask($task_origin_id)
2620  {
2621  global $langs, $hookmanager, $user, $action;
2622 
2623  $error = 0;
2624  $task_origin = new Task($this->db); // The thirdparty that we will delete
2625 
2626  dol_syslog("mergeTask merge task id=".$task_origin_id." (will be deleted) into the task id=".$this->id);
2627 
2628  $langs->load('error');
2629 
2630  if (!$error && $task_origin->fetch($task_origin_id) < 1) {
2631  $this->error = $langs->trans('ErrorRecordNotFound');
2632  $error++;
2633  }
2634 
2635  if (!$error) {
2636  $this->db->begin();
2637 
2638  // Recopy some data
2639  $listofproperties = array(
2640  'label', 'description', 'duration_effective', 'planned_workload', 'datec', 'date_start',
2641  'date_end', 'fk_user_creat', 'fk_user_valid', 'fk_statut', 'progress', 'budget_amount',
2642  'priority', 'rang', 'fk_projet', 'fk_task_parent'
2643  );
2644  foreach ($listofproperties as $property) {
2645  if (empty($this->$property)) {
2646  $this->$property = $task_origin->$property;
2647  }
2648  }
2649 
2650  // Concat some data
2651  $listofproperties = array(
2652  'note_public', 'note_private'
2653  );
2654  foreach ($listofproperties as $property) {
2655  $this->$property = dol_concatdesc($this->$property, $task_origin->$property);
2656  }
2657 
2658  // Merge extrafields
2659  if (is_array($task_origin->array_options)) {
2660  foreach ($task_origin->array_options as $key => $val) {
2661  if (empty($this->array_options[$key])) {
2662  $this->array_options[$key] = $val;
2663  }
2664  }
2665  }
2666 
2667  // Update
2668  $result = $this->update($user);
2669 
2670  if ($result < 0) {
2671  $error++;
2672  }
2673 
2674  // Merge time spent
2675  if (!$error) {
2676  $result = $this->mergeTimeSpentTask($task_origin_id, $this->id);
2677  if ($result != true) {
2678  $error++;
2679  }
2680  }
2681 
2682  // Merge contacts
2683  if (!$error) {
2684  $result = $this->mergeContactTask($task_origin_id, $this->id);
2685  if ($result != true) {
2686  $error++;
2687  }
2688  }
2689 
2690  // External modules should update their ones too
2691  if (!$error) {
2692  $parameters = array('task_origin' => $task_origin->id, 'task_dest' => $this->id);
2693  $reshook = $hookmanager->executeHooks('replaceThirdparty', $parameters, $this, $action);
2694 
2695  if ($reshook < 0) {
2696  $this->error = $hookmanager->error;
2697  $this->errors = $hookmanager->errors;
2698  $error++;
2699  }
2700  }
2701 
2702 
2703  if (!$error) {
2704  $this->context = array('merge' => 1, 'mergefromid' => $task_origin->id, 'mergefromref' => $task_origin->ref);
2705 
2706  // Call trigger
2707  $result = $this->call_trigger('TASK_MODIFY', $user);
2708  if ($result < 0) {
2709  $error++;
2710  }
2711  // End call triggers
2712  }
2713 
2714  if (!$error) {
2715  // We finally remove the old task
2716  if ($task_origin->delete($user) < 1) {
2717  $this->error = $task_origin->error;
2718  $this->errors = $task_origin->errors;
2719  $error++;
2720  }
2721  }
2722 
2723  if (!$error) {
2724  $this->db->commit();
2725  return 0;
2726  } else {
2727  $langs->load("errors");
2728  $this->error = $langs->trans('ErrorsTaskMerge');
2729  $this->db->rollback();
2730  return -1;
2731  }
2732  }
2733 
2734  return -1;
2735  }
2736 }
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:624
$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.
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.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
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.
Definition: task.class.php:41
create($user, $notrigger=0)
Create into database.
Definition: task.class.php:268
fetch($id, $ref='', $loadparentdata=0)
Load object in memory from database.
Definition: task.class.php:377
getLibStatut($mode=0)
Return status label of object.
mergeContactTask($origin_id, $dest_id)
Merge contact of tasks.
const STATUS_TRANSFERRED
Transferred status.
Definition: task.class.php:242
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.
Definition: task.class.php:255
fetchTimeSpentOnTask($morewherefilter='')
Fetch records of time spent of this task.
const STATUS_VALIDATED
Validated status (To do).
Definition: task.class.php:232
hasTimeSpent()
Return nb of time spent.
Definition: task.class.php:795
getNomUrl($withpicto=0, $option='', $mode='task', $addlabel=0, $sep=' - ', $notooltip=0, $save_lastsearch_value=-1)
Return clicable name (with picto eventually)
Definition: task.class.php:866
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.
Definition: task.class.php:227
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.
Definition: task.class.php:478
getSummaryOfTimeSpent($userobj=null, $morewherefilter='')
Calculate total of time spent for task.
getTooltipContentArray($params)
getTooltipContentArray
Definition: task.class.php:833
const STATUS_CANCELED
status canceled
Definition: task.class.php:247
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.
Definition: task.class.php:947
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,...
Definition: task.class.php:991
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.
Definition: task.class.php:237
hasChildren()
Return nb of children.
Definition: task.class.php:761
Class for TimeSpent.
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:751
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(".$version.") pid 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)
Definition: files.lib.php:1620
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
Definition: files.lib.php:767
dol_move_dir($srcdir, $destdir, $overwriteifexists=1, $indexdatabase=1, $renamedircontent=1)
Move a directory into another name.
Definition: files.lib.php:1175
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)
div float
Unit price before taxes.
Definition: style.css.php:963