dolibarr  17.0.4
task.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2008-2014 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2010-2012 Regis Houssin <regis.houssin@inodbox.com>
4  * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
5  * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
6  * Copyright (C) 2020 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  */
22 
29 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
32 
33 
37 class Task extends CommonObjectLine
38 {
42  public $element = 'project_task';
43 
47  public $table_element = 'projet_task';
48 
52  public $fk_element = 'fk_task';
53 
57  public $picto = 'projecttask';
58 
62  protected $childtables = array(
63  'projet_task_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_task')
64  );
65 
69  public $fk_task_parent = 0;
70 
74  public $label;
75 
79  public $description;
80 
81  public $duration_effective; // total of time spent on this task
82  public $planned_workload;
83  public $date_c;
84  public $date_start;
85  public $date_end;
86  public $progress;
87 
91  public $datee;
92 
96  public $fk_statut;
97 
98  public $priority;
99 
103  public $fk_user_creat;
104 
108  public $fk_user_valid;
109 
110  public $rang;
111 
112  public $timespent_min_date;
113  public $timespent_max_date;
114  public $timespent_total_duration;
115  public $timespent_total_amount;
116  public $timespent_nblinesnull;
117  public $timespent_nblines;
118  // For detail of lines of timespent record, there is the property ->lines in common
119 
120  // Var used to call method addTimeSpent(). Bad practice.
121  public $timespent_id;
122  public $timespent_duration;
123  public $timespent_old_duration;
124  public $timespent_date;
125  public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
126  public $timespent_withhour; // 1 = we entered also start hours for timesheet line
127  public $timespent_fk_user;
128  public $timespent_thm;
129  public $timespent_note;
130  public $timespent_fk_product;
131 
132  public $comments = array();
133 
137  public $budget_amount;
138 
142  public $project_budget_amount;
143 
144  public $oldcopy;
145 
146 
152  public function __construct($db)
153  {
154  $this->db = $db;
155  }
156 
157 
165  public function create($user, $notrigger = 0)
166  {
167  global $conf, $langs;
168 
169  //For the date
170  $now = dol_now();
171 
172  $error = 0;
173 
174  // Clean parameters
175  $this->label = trim($this->label);
176  $this->description = trim($this->description);
177 
178  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
179  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
180  return -1;
181  }
182 
183  // Check parameters
184  // Put here code to add control on parameters values
185 
186  // Insert request
187  $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
188  $sql .= "entity";
189  $sql .= ", fk_projet";
190  $sql .= ", ref";
191  $sql .= ", fk_task_parent";
192  $sql .= ", label";
193  $sql .= ", description";
194  $sql .= ", datec";
195  $sql .= ", fk_user_creat";
196  $sql .= ", dateo";
197  $sql .= ", datee";
198  $sql .= ", planned_workload";
199  $sql .= ", progress";
200  $sql .= ", budget_amount";
201  $sql .= ") VALUES (";
202  $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
203  $sql .= ", ".((int) $this->fk_project);
204  $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
205  $sql .= ", ".((int) $this->fk_task_parent);
206  $sql .= ", '".$this->db->escape($this->label)."'";
207  $sql .= ", '".$this->db->escape($this->description)."'";
208  $sql .= ", '".$this->db->idate($now)."'";
209  $sql .= ", ".((int) $user->id);
210  $sql .= ", ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : 'null');
211  $sql .= ", ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : 'null');
212  $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
213  $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
214  $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
215  $sql .= ")";
216 
217  $this->db->begin();
218 
219  dol_syslog(get_class($this)."::create", LOG_DEBUG);
220  $resql = $this->db->query($sql);
221  if (!$resql) {
222  $error++; $this->errors[] = "Error ".$this->db->lasterror();
223  }
224 
225  if (!$error) {
226  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
227 
228  if (!$notrigger) {
229  // Call trigger
230  $result = $this->call_trigger('TASK_CREATE', $user);
231  if ($result < 0) {
232  $error++;
233  }
234  // End call triggers
235  }
236  }
237 
238  // Update extrafield
239  if (!$error) {
240  if (!$error) {
241  $result = $this->insertExtraFields();
242  if ($result < 0) {
243  $error++;
244  }
245  }
246  }
247 
248  // Commit or rollback
249  if ($error) {
250  foreach ($this->errors as $errmsg) {
251  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
252  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
253  }
254  $this->db->rollback();
255  return -1 * $error;
256  } else {
257  $this->db->commit();
258  return $this->id;
259  }
260  }
261 
262 
271  public function fetch($id, $ref = '', $loadparentdata = 0)
272  {
273  global $langs, $conf;
274 
275  $sql = "SELECT";
276  $sql .= " t.rowid,";
277  $sql .= " t.ref,";
278  $sql .= " t.entity,";
279  $sql .= " t.fk_projet as fk_project,";
280  $sql .= " t.fk_task_parent,";
281  $sql .= " t.label,";
282  $sql .= " t.description,";
283  $sql .= " t.duration_effective,";
284  $sql .= " t.planned_workload,";
285  $sql .= " t.datec,";
286  $sql .= " t.dateo,";
287  $sql .= " t.datee,";
288  $sql .= " t.fk_user_creat,";
289  $sql .= " t.fk_user_valid,";
290  $sql .= " t.fk_statut,";
291  $sql .= " t.progress,";
292  $sql .= " t.budget_amount,";
293  $sql .= " t.priority,";
294  $sql .= " t.note_private,";
295  $sql .= " t.note_public,";
296  $sql .= " t.rang";
297  if (!empty($loadparentdata)) {
298  $sql .= ", t2.ref as task_parent_ref";
299  $sql .= ", t2.rang as task_parent_position";
300  }
301  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
302  if (!empty($loadparentdata)) {
303  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
304  }
305  $sql .= " WHERE ";
306  if (!empty($ref)) {
307  $sql .= "entity IN (".getEntity('project').")";
308  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
309  } else {
310  $sql .= "t.rowid = ".((int) $id);
311  }
312 
313  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
314  $resql = $this->db->query($sql);
315  if ($resql) {
316  $num_rows = $this->db->num_rows($resql);
317 
318  if ($num_rows) {
319  $obj = $this->db->fetch_object($resql);
320 
321  $this->id = $obj->rowid;
322  $this->ref = $obj->ref;
323  $this->entity = $obj->entity;
324  $this->fk_project = $obj->fk_project;
325  $this->fk_task_parent = $obj->fk_task_parent;
326  $this->label = $obj->label;
327  $this->description = $obj->description;
328  $this->duration_effective = $obj->duration_effective;
329  $this->planned_workload = $obj->planned_workload;
330  $this->date_c = $this->db->jdate($obj->datec);
331  $this->date_start = $this->db->jdate($obj->dateo);
332  $this->date_end = $this->db->jdate($obj->datee);
333  $this->fk_user_creat = $obj->fk_user_creat;
334  $this->fk_user_valid = $obj->fk_user_valid;
335  $this->fk_statut = $obj->fk_statut;
336  $this->progress = $obj->progress;
337  $this->budget_amount = $obj->budget_amount;
338  $this->priority = $obj->priority;
339  $this->note_private = $obj->note_private;
340  $this->note_public = $obj->note_public;
341  $this->rang = $obj->rang;
342 
343  if (!empty($loadparentdata)) {
344  $this->task_parent_ref = $obj->task_parent_ref;
345  $this->task_parent_position = $obj->task_parent_position;
346  }
347 
348  // Retrieve all extrafield
349  $this->fetch_optionals();
350  }
351 
352  $this->db->free($resql);
353 
354  if ($num_rows) {
355  return 1;
356  } else {
357  return 0;
358  }
359  } else {
360  $this->error = "Error ".$this->db->lasterror();
361  return -1;
362  }
363  }
364 
365 
373  public function update($user = null, $notrigger = 0)
374  {
375  global $conf, $langs;
376  $error = 0;
377 
378  // Clean parameters
379  if (isset($this->fk_project)) {
380  $this->fk_project = trim($this->fk_project);
381  }
382  if (isset($this->ref)) {
383  $this->ref = trim($this->ref);
384  }
385  if (isset($this->fk_task_parent)) {
386  $this->fk_task_parent = (int) $this->fk_task_parent;
387  }
388  if (isset($this->label)) {
389  $this->label = trim($this->label);
390  }
391  if (isset($this->description)) {
392  $this->description = trim($this->description);
393  }
394  if (isset($this->duration_effective)) {
395  $this->duration_effective = trim($this->duration_effective);
396  }
397  if (isset($this->planned_workload)) {
398  $this->planned_workload = trim($this->planned_workload);
399  }
400  if (isset($this->budget_amount)) {
401  $this->budget_amount = trim($this->budget_amount);
402  }
403 
404  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
405  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
406  return -1;
407  }
408 
409  // Check parameters
410  // Put here code to add control on parameters values
411 
412  // Update request
413  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
414  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
415  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
416  $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
417  $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
418  $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
419  $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
420  $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
421  $sql .= " dateo=".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
422  $sql .= " datee=".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
423  $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
424  $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
425  $sql .= " rang=".((!empty($this->rang)) ? $this->rang : "0");
426  $sql .= " WHERE rowid=".((int) $this->id);
427 
428  $this->db->begin();
429 
430  dol_syslog(get_class($this)."::update", LOG_DEBUG);
431  $resql = $this->db->query($sql);
432  if (!$resql) {
433  $error++; $this->errors[] = "Error ".$this->db->lasterror();
434  }
435 
436  // Update extrafield
437  if (!$error) {
438  $result = $this->insertExtraFields();
439  if ($result < 0) {
440  $error++;
441  }
442  }
443 
444  if (!$error && !empty($conf->global->PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE)) {
445  // Close the parent project if it is open (validated) and its tasks are 100% completed
446  $project = new Project($this->db);
447  if ($project->fetch($this->fk_project) > 0) {
448  if ($project->statut == Project::STATUS_VALIDATED) {
449  $project->getLinesArray(null); // this method does not return <= 0 if fails
450  $projectCompleted = array_reduce(
451  $project->lines,
452  function ($allTasksCompleted, $task) {
453  return $allTasksCompleted && $task->progress >= 100;
454  },
455  1
456  );
457  if ($projectCompleted) {
458  if ($project->setClose($user) <= 0) {
459  $error++;
460  }
461  }
462  }
463  } else {
464  $error++;
465  }
466  if ($error) {
467  $this->errors[] = $project->error;
468  }
469  }
470 
471  if (!$error) {
472  if (!$notrigger) {
473  // Call trigger
474  $result = $this->call_trigger('TASK_MODIFY', $user);
475  if ($result < 0) {
476  $error++;
477  }
478  // End call triggers
479  }
480  }
481 
482  if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
483  // We remove directory
484  if ($conf->project->dir_output) {
485  $project = new Project($this->db);
486  $project->fetch($this->fk_project);
487 
488  $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
489  $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
490  if (file_exists($olddir)) {
491  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
492  $res = dol_move_dir($olddir, $newdir);
493  if (!$res) {
494  $langs->load("errors");
495  $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
496  $error++;
497  }
498  }
499  }
500  }
501 
502  // Commit or rollback
503  if ($error) {
504  foreach ($this->errors as $errmsg) {
505  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
506  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
507  }
508  $this->db->rollback();
509  return -1 * $error;
510  } else {
511  $this->db->commit();
512  return 1;
513  }
514  }
515 
516 
524  public function delete($user, $notrigger = 0)
525  {
526 
527  global $conf, $langs;
528  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
529 
530  $error = 0;
531 
532  $this->db->begin();
533 
534  if ($this->hasChildren() > 0) {
535  dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
536  $this->error = 'ErrorRecordHasSubTasks';
537  $this->db->rollback();
538  return 0;
539  }
540 
541  $objectisused = $this->isObjectUsed($this->id);
542  if (!empty($objectisused)) {
543  dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
544  $this->error = 'ErrorRecordHasChildren';
545  $this->db->rollback();
546  return 0;
547  }
548 
549  if (!$error) {
550  // Delete linked contacts
551  $res = $this->delete_linked_contact();
552  if ($res < 0) {
553  $this->error = 'ErrorFailToDeleteLinkedContact';
554  //$error++;
555  $this->db->rollback();
556  return 0;
557  }
558  }
559 
560  if (!$error) {
561  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
562  $sql .= " WHERE fk_task = ".((int) $this->id);
563 
564  $resql = $this->db->query($sql);
565  if (!$resql) {
566  $error++; $this->errors[] = "Error ".$this->db->lasterror();
567  }
568  }
569 
570  if (!$error) {
571  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
572  $sql .= " WHERE fk_object = ".((int) $this->id);
573 
574  $resql = $this->db->query($sql);
575  if (!$resql) {
576  $error++; $this->errors[] = "Error ".$this->db->lasterror();
577  }
578  }
579 
580  if (!$error) {
581  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
582  $sql .= " WHERE rowid=".((int) $this->id);
583 
584  $resql = $this->db->query($sql);
585  if (!$resql) {
586  $error++; $this->errors[] = "Error ".$this->db->lasterror();
587  }
588  }
589 
590  if (!$error) {
591  if (!$notrigger) {
592  // Call trigger
593  $result = $this->call_trigger('TASK_DELETE', $user);
594  if ($result < 0) {
595  $error++;
596  }
597  // End call triggers
598  }
599  }
600 
601  // Commit or rollback
602  if ($error) {
603  foreach ($this->errors as $errmsg) {
604  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
605  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
606  }
607  $this->db->rollback();
608  return -1 * $error;
609  } else {
610  //Delete associated link file
611  if ($conf->project->dir_output) {
612  $projectstatic = new Project($this->db);
613  $projectstatic->fetch($this->fk_project);
614 
615  $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
616  dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
617  if (file_exists($dir)) {
618  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
619  $res = @dol_delete_dir_recursive($dir);
620  if (!$res) {
621  $this->error = 'ErrorFailToDeleteDir';
622  $this->db->rollback();
623  return 0;
624  }
625  }
626  }
627 
628  $this->db->commit();
629 
630  return 1;
631  }
632  }
633 
639  public function hasChildren()
640  {
641  $error = 0;
642  $ret = 0;
643 
644  $sql = "SELECT COUNT(*) as nb";
645  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
646  $sql .= " WHERE fk_task_parent = ".((int) $this->id);
647 
648  dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
649  $resql = $this->db->query($sql);
650  if (!$resql) {
651  $error++; $this->errors[] = "Error ".$this->db->lasterror();
652  } else {
653  $obj = $this->db->fetch_object($resql);
654  if ($obj) {
655  $ret = $obj->nb;
656  }
657  $this->db->free($resql);
658  }
659 
660  if (!$error) {
661  return $ret;
662  } else {
663  return -1;
664  }
665  }
666 
672  public function hasTimeSpent()
673  {
674  $error = 0;
675  $ret = 0;
676 
677  $sql = "SELECT COUNT(*) as nb";
678  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time";
679  $sql .= " WHERE fk_task = ".((int) $this->id);
680 
681  dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
682  $resql = $this->db->query($sql);
683  if (!$resql) {
684  $error++; $this->errors[] = "Error ".$this->db->lasterror();
685  } else {
686  $obj = $this->db->fetch_object($resql);
687  if ($obj) {
688  $ret = $obj->nb;
689  }
690  $this->db->free($resql);
691  }
692 
693  if (!$error) {
694  return $ret;
695  } else {
696  return -1;
697  }
698  }
699 
700 
713  public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
714  {
715  global $conf, $langs, $user;
716 
717  if (!empty($conf->dol_no_mouse_hover)) {
718  $notooltip = 1; // Force disable tooltips
719  }
720 
721  $result = '';
722  $label = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
723  if (!empty($this->ref)) {
724  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
725  }
726  if (!empty($this->label)) {
727  $label .= '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
728  }
729  if ($this->date_start || $this->date_end) {
730  $label .= "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
731  }
732 
733  $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
734  // Add param to save lastsearch_values or not
735  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
736  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
737  $add_save_lastsearch_values = 1;
738  }
739  if ($add_save_lastsearch_values) {
740  $url .= '&save_lastsearch_values=1';
741  }
742 
743  $linkclose = '';
744  if (empty($notooltip)) {
745  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
746  $label = $langs->trans("ShowTask");
747  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
748  }
749  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
750  $linkclose .= ' class="classfortooltip nowraponall"';
751  } else {
752  $linkclose .= ' class="nowraponall"';
753  }
754 
755  $linkstart = '<a href="'.$url.'"';
756  $linkstart .= $linkclose.'>';
757  $linkend = '</a>';
758 
759  $picto = 'projecttask';
760 
761  $result .= $linkstart;
762  if ($withpicto) {
763  $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
764  }
765  if ($withpicto != 2) {
766  $result .= $this->ref;
767  }
768  $result .= $linkend;
769  if ($withpicto != 2) {
770  $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
771  }
772 
773  return $result;
774  }
775 
783  public function initAsSpecimen()
784  {
785  $this->id = 0;
786 
787  $this->fk_project = '';
788  $this->ref = 'TK01';
789  $this->fk_task_parent = null;
790  $this->label = 'Specimen task TK01';
791  $this->duration_effective = '';
792  $this->fk_user_creat = null;
793  $this->progress = '25';
794  $this->fk_statut = null;
795  $this->note = 'This is a specimen task not';
796  }
797 
821  public function getTasksArray($usert = null, $userp = null, $projectid = 0, $socid = 0, $mode = 0, $filteronproj = '', $filteronprojstatus = '-1', $morewherefilter = '', $filteronprojuser = 0, $filterontaskuser = 0, $extrafields = array(), $includebilltime = 0, $search_array_options = array(), $loadextras = 0, $loadRoleMode = 1, $sortfield = '', $sortorder = '')
822  {
823  global $conf, $hookmanager;
824 
825  $tasks = array();
826 
827  //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
828 
829  // List of tasks (does not care about permissions. Filtering will be done later)
830  $sql = "SELECT ";
831  if ($filteronprojuser > 0 || $filterontaskuser > 0) {
832  $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
833  }
834  $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
835  $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,";
836  $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang,";
837  $sql .= " t.description, ";
838  $sql .= " t.budget_amount, ";
839  $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
840  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
841  if (!empty($extrafields->attributes['projet']['label'])) {
842  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
843  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
844  }
845  }
846  if (!empty($extrafields->attributes['projet_task']['label'])) {
847  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
848  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
849  }
850  }
851  if ($includebilltime) {
852  $sql .= ", SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "1", "0").") as tobill, SUM(tt.task_duration * ".$this->db->ifsql("invoice_id IS NULL", "0", "1").") as billed";
853  }
854 
855  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
856  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
857  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
858 
859  if ($mode == 0) {
860  if ($filteronprojuser > 0) {
861  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
862  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
863  }
864  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
865  if ($includebilltime) {
866  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
867  }
868  if ($filterontaskuser > 0) {
869  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
870  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
871  }
872  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
873  $sql .= " WHERE p.entity IN (".getEntity('project').")";
874  $sql .= " AND t.fk_projet = p.rowid";
875  } elseif ($mode == 1) {
876  if ($filteronprojuser > 0) {
877  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
878  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
879  }
880  if ($filterontaskuser > 0) {
881  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
882  if ($includebilltime) {
883  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
884  }
885  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
886  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
887  } else {
888  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
889  if ($includebilltime) {
890  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
891  }
892  }
893  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
894  $sql .= " WHERE p.entity IN (".getEntity('project').")";
895  } else {
896  return 'BadValueForParameterMode';
897  }
898 
899  if ($filteronprojuser > 0) {
900  $sql .= " AND p.rowid = ec.element_id";
901  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
902  $sql .= " AND ctc.element = 'project'";
903  $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
904  $sql .= " AND ec.statut = 4";
905  $sql .= " AND ctc.source = 'internal'";
906  }
907  if ($filterontaskuser > 0) {
908  $sql .= " AND t.fk_projet = p.rowid";
909  $sql .= " AND p.rowid = ec2.element_id";
910  $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
911  $sql .= " AND ctc2.element = 'project_task'";
912  $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
913  $sql .= " AND ec2.statut = 4";
914  $sql .= " AND ctc2.source = 'internal'";
915  }
916  if ($socid) {
917  $sql .= " AND p.fk_soc = ".((int) $socid);
918  }
919  if ($projectid) {
920  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
921  }
922  if ($filteronproj) {
923  $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
924  }
925  if ($filteronprojstatus && $filteronprojstatus != '-1') {
926  $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
927  }
928  if ($morewherefilter) {
929  $sql .= $morewherefilter;
930  }
931  // Add where from extra fields
932  $extrafieldsobjectkey = 'projet_task';
933  $extrafieldsobjectprefix = 'efpt.';
934  global $db; // needed for extrafields_list_search_sql.tpl
935  include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
936  // Add where from hooks
937  $parameters = array();
938  $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
939  $sql .= $hookmanager->resPrint;
940  if ($includebilltime) {
941  $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
942  $sql .= " t.datec, t.dateo, t.datee, t.tms,";
943  $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
944  $sql .= " t.dateo, t.datee, t.planned_workload, t.rang,";
945  $sql .= " t.description, ";
946  $sql .= " t.budget_amount, ";
947  $sql .= " s.rowid, s.nom, s.email,";
948  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
949  if (!empty($extrafields->attributes['projet']['label'])) {
950  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
951  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
952  }
953  }
954  if (!empty($extrafields->attributes['projet_task']['label'])) {
955  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
956  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
957  }
958  }
959  }
960 
961  if ($sortfield && $sortorder) {
962  $sql .= $this->db->order($sortfield, $sortorder);
963  } else {
964  $sql .= " ORDER BY p.ref, t.rang, t.dateo";
965  }
966 
967  //print $sql;exit;
968  dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
969  $resql = $this->db->query($sql);
970  if ($resql) {
971  $num = $this->db->num_rows($resql);
972  $i = 0;
973  // Loop on each record found, so each couple (project id, task id)
974  while ($i < $num) {
975  $error = 0;
976 
977  $obj = $this->db->fetch_object($resql);
978 
979  if ($loadRoleMode) {
980  if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
981  if (!$this->getUserRolesForProjectsOrTasks($userp, 0, $obj->projectid, 0)) {
982  $error++;
983  }
984  }
985  if (is_object($usert)) { // If we ask a filter on a user affected to a task
986  if (!$this->getUserRolesForProjectsOrTasks(0, $usert, $obj->projectid, $obj->taskid)) {
987  $error++;
988  }
989  }
990  }
991 
992  if (!$error) {
993  $tasks[$i] = new Task($this->db);
994  $tasks[$i]->id = $obj->taskid;
995  $tasks[$i]->ref = $obj->taskref;
996  $tasks[$i]->fk_project = $obj->projectid;
997  $tasks[$i]->projectref = $obj->ref;
998  $tasks[$i]->projectlabel = $obj->plabel;
999  $tasks[$i]->projectstatus = $obj->projectstatus;
1000 
1001  $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1002  $tasks[$i]->opp_amount = $obj->opp_amount;
1003  $tasks[$i]->opp_percent = $obj->opp_percent;
1004  $tasks[$i]->budget_amount = $obj->budget_amount;
1005  $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1006  $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1007 
1008  $tasks[$i]->label = $obj->label;
1009  $tasks[$i]->description = $obj->description;
1010  $tasks[$i]->fk_parent = $obj->fk_task_parent; // deprecated
1011  $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1012  $tasks[$i]->duration = $obj->duration_effective;
1013  $tasks[$i]->planned_workload = $obj->planned_workload;
1014 
1015  if ($includebilltime) {
1016  $tasks[$i]->tobill = $obj->tobill;
1017  $tasks[$i]->billed = $obj->billed;
1018  }
1019 
1020  $tasks[$i]->progress = $obj->progress;
1021  $tasks[$i]->fk_statut = $obj->status;
1022  $tasks[$i]->public = $obj->public;
1023  $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1024  $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1025  $tasks[$i]->rang = $obj->rang;
1026 
1027  $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1028  $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1029  $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1030  $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1031 
1032  if (!empty($extrafields->attributes['projet']['label'])) {
1033  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1034  if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1035  $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
1036  }
1037  }
1038  }
1039 
1040  if (!empty($extrafields->attributes['projet_task']['label'])) {
1041  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1042  if ($extrafields->attributes['projet_task']['type'][$key] != 'separate') {
1043  $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
1044  }
1045  }
1046  }
1047 
1048  if ($loadextras) {
1049  $tasks[$i]->fetch_optionals();
1050  }
1051  }
1052 
1053  $i++;
1054  }
1055  $this->db->free($resql);
1056  } else {
1057  dol_print_error($this->db);
1058  }
1059 
1060  return $tasks;
1061  }
1062 
1073  public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1074  {
1075  $arrayroles = array();
1076 
1077  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".is_object($userp)." usert=".is_object($usert)." projectid=".$projectid." taskid=".$taskid);
1078 
1079  // We want role of user for a projet or role of user for a task. Both are not possible.
1080  if (empty($userp) && empty($usert)) {
1081  $this->error = "CallWithWrongParameters";
1082  return -1;
1083  }
1084  if (!empty($userp) && !empty($usert)) {
1085  $this->error = "CallWithWrongParameters";
1086  return -1;
1087  }
1088 
1089  /* Liste des taches et role sur les projets ou taches */
1090  $sql = "SELECT pt.rowid as pid, ec.element_id, ctc.code, ctc.source";
1091  if ($userp) {
1092  $sql .= " FROM ".MAIN_DB_PREFIX."projet as pt";
1093  }
1094  if ($usert && $filteronprojstatus > -1) {
1095  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1096  }
1097  if ($usert && $filteronprojstatus <= -1) {
1098  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1099  }
1100  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1101  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1102  $sql .= " WHERE pt.rowid = ec.element_id";
1103  if ($userp && $filteronprojstatus > -1) {
1104  $sql .= " AND pt.fk_statut = ".((int) $filteronprojstatus);
1105  }
1106  if ($usert && $filteronprojstatus > -1) {
1107  $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1108  }
1109  if ($userp) {
1110  $sql .= " AND ctc.element = 'project'";
1111  }
1112  if ($usert) {
1113  $sql .= " AND ctc.element = 'project_task'";
1114  }
1115  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1116  if ($userp) {
1117  $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1118  }
1119  if ($usert) {
1120  $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1121  }
1122  $sql .= " AND ec.statut = 4";
1123  $sql .= " AND ctc.source = 'internal'";
1124  if ($projectid) {
1125  if ($userp) {
1126  $sql .= " AND pt.rowid IN (".$this->db->sanitize($projectid).")";
1127  }
1128  if ($usert) {
1129  $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1130  }
1131  }
1132  if ($taskid) {
1133  if ($userp) {
1134  $sql .= " ERROR SHOULD NOT HAPPENS";
1135  }
1136  if ($usert) {
1137  $sql .= " AND pt.rowid = ".((int) $taskid);
1138  }
1139  }
1140  //print $sql;
1141 
1142  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1143  $resql = $this->db->query($sql);
1144  if ($resql) {
1145  $num = $this->db->num_rows($resql);
1146  $i = 0;
1147  while ($i < $num) {
1148  $obj = $this->db->fetch_object($resql);
1149  if (empty($arrayroles[$obj->pid])) {
1150  $arrayroles[$obj->pid] = $obj->code;
1151  } else {
1152  $arrayroles[$obj->pid] .= ','.$obj->code;
1153  }
1154  $i++;
1155  }
1156  $this->db->free($resql);
1157  } else {
1158  dol_print_error($this->db);
1159  }
1160 
1161  return $arrayroles;
1162  }
1163 
1164 
1171  public function getListContactId($source = 'internal')
1172  {
1173  $contactAlreadySelected = array();
1174  $tab = $this->liste_contact(-1, $source);
1175  //var_dump($tab);
1176  $num = count($tab);
1177  $i = 0;
1178  while ($i < $num) {
1179  if ($source == 'thirdparty') {
1180  $contactAlreadySelected[$i] = $tab[$i]['socid'];
1181  } else {
1182  $contactAlreadySelected[$i] = $tab[$i]['id'];
1183  }
1184  $i++;
1185  }
1186  return $contactAlreadySelected;
1187  }
1188 
1189 
1197  public function addTimeSpent($user, $notrigger = 0)
1198  {
1199  global $conf, $langs;
1200 
1201  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1202 
1203  $ret = 0;
1204  $now = dol_now();
1205 
1206  // Check parameters
1207  if (!is_object($user)) {
1208  dol_print_error('', "Method addTimeSpent was called with wrong parameter user");
1209  return -1;
1210  }
1211 
1212  // Clean parameters
1213  if (isset($this->timespent_note)) {
1214  $this->timespent_note = trim($this->timespent_note);
1215  }
1216  if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1217  $this->timespent_datehour = $this->timespent_date;
1218  }
1219 
1220  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1221  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1222  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1223 
1224  if ($this->timespent_date < $restrictBefore) {
1225  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1226  $this->errors[] = $this->error;
1227  return -1;
1228  }
1229  }
1230 
1231 
1232  $this->db->begin();
1233 
1234  $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task_time (";
1235  $sql .= "fk_task";
1236  $sql .= ", task_date";
1237  $sql .= ", task_datehour";
1238  $sql .= ", task_date_withhour";
1239  $sql .= ", task_duration";
1240  $sql .= ", fk_user";
1241  $sql .= ", fk_product";
1242  $sql .= ", note";
1243  $sql .= ", datec";
1244  $sql .= ") VALUES (";
1245  $sql .= ((int) $this->id);
1246  $sql .= ", '".$this->db->idate($this->timespent_date)."'";
1247  $sql .= ", '".$this->db->idate($this->timespent_datehour)."'";
1248  $sql .= ", ".(empty($this->timespent_withhour) ? 0 : 1);
1249  $sql .= ", ".((int) $this->timespent_duration);
1250  $sql .= ", ".((int) $this->timespent_fk_user);
1251  $sql .= ", ".((int) $this->timespent_fk_product);
1252  $sql .= ", ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null");
1253  $sql .= ", '".$this->db->idate($now)."'";
1254  $sql .= ")";
1255 
1256  $resql = $this->db->query($sql);
1257  if ($resql) {
1258  $tasktime_id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task_time");
1259  $ret = $tasktime_id;
1260  $this->timespent_id = $ret;
1261 
1262  if (!$notrigger) {
1263  // Call trigger
1264  $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1265  if ($result < 0) {
1266  $ret = -1;
1267  }
1268  // End call triggers
1269  }
1270  } else {
1271  $this->error = $this->db->lasterror();
1272  $ret = -1;
1273  }
1274 
1275  if ($ret > 0) {
1276  // Recalculate amount of time spent for task and update denormalized field
1277  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1278  $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".((int) $this->id).")";
1279  if (isset($this->progress)) {
1280  $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1281  }
1282  $sql .= " WHERE rowid = ".((int) $this->id);
1283 
1284  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1285  if (!$this->db->query($sql)) {
1286  $this->error = $this->db->lasterror();
1287  $ret = -2;
1288  }
1289 
1290  // Update hourly rate of this time spent entry
1291  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
1292  $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user
1293  $sql .= " WHERE rowid = ".((int) $tasktime_id);
1294 
1295  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1296  if (!$this->db->query($sql)) {
1297  $this->error = $this->db->lasterror();
1298  $ret = -2;
1299  }
1300  }
1301 
1302  if ($ret > 0) {
1303  $this->db->commit();
1304  } else {
1305  $this->db->rollback();
1306  }
1307  return $ret;
1308  }
1309 
1316  public function fetchTimeSpentOnTask($morewherefilter = '')
1317  {
1318  global $langs;
1319 
1320  $arrayres = array();
1321 
1322  $sql = "SELECT";
1323  $sql .= " s.rowid as socid,";
1324  $sql .= " s.nom as thirdparty_name,";
1325  $sql .= " s.email as thirdparty_email,";
1326  $sql .= " ptt.rowid,";
1327  $sql .= " ptt.fk_task,";
1328  $sql .= " ptt.task_date,";
1329  $sql .= " ptt.task_datehour,";
1330  $sql .= " ptt.task_date_withhour,";
1331  $sql .= " ptt.task_duration,";
1332  $sql .= " ptt.fk_user,";
1333  $sql .= " ptt.note,";
1334  $sql .= " ptt.thm,";
1335  $sql .= " pt.rowid as task_id,";
1336  $sql .= " pt.ref as task_ref,";
1337  $sql .= " pt.label as task_label,";
1338  $sql .= " p.rowid as project_id,";
1339  $sql .= " p.ref as project_ref,";
1340  $sql .= " p.title as project_label,";
1341  $sql .= " p.public as public";
1342  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1343  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1344  $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
1345  $sql .= " AND pt.rowid = ".((int) $this->id);
1346  $sql .= " AND pt.entity IN (".getEntity('project').")";
1347  if ($morewherefilter) {
1348  $sql .= $morewherefilter;
1349  }
1350 
1351  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1352  $resql = $this->db->query($sql);
1353  if ($resql) {
1354  $num = $this->db->num_rows($resql);
1355 
1356  $i = 0;
1357  while ($i < $num) {
1358  $obj = $this->db->fetch_object($resql);
1359 
1360  $newobj = new stdClass();
1361 
1362  $newobj->socid = $obj->socid;
1363  $newobj->thirdparty_name = $obj->thirdparty_name;
1364  $newobj->thirdparty_email = $obj->thirdparty_email;
1365 
1366  $newobj->fk_project = $obj->project_id;
1367  $newobj->project_ref = $obj->project_ref;
1368  $newobj->project_label = $obj->project_label;
1369  $newobj->public = $obj->project_public;
1370 
1371  $newobj->fk_task = $obj->task_id;
1372  $newobj->task_ref = $obj->task_ref;
1373  $newobj->task_label = $obj->task_label;
1374 
1375  $newobj->timespent_line_id = $obj->rowid;
1376  $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1377  $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1378  $newobj->timespent_line_withhour = $obj->task_date_withhour;
1379  $newobj->timespent_line_duration = $obj->task_duration;
1380  $newobj->timespent_line_fk_user = $obj->fk_user;
1381  $newobj->timespent_line_thm = $obj->thm; // hourly rate
1382  $newobj->timespent_line_note = $obj->note;
1383 
1384  $arrayres[] = $newobj;
1385 
1386  $i++;
1387  }
1388 
1389  $this->db->free($resql);
1390 
1391  $this->lines = $arrayres;
1392  return 1;
1393  } else {
1394  dol_print_error($this->db);
1395  $this->error = "Error ".$this->db->lasterror();
1396  return -1;
1397  }
1398  }
1399 
1400 
1408  public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1409  {
1410  global $langs;
1411 
1412  if (is_object($userobj)) {
1413  $userid = $userobj->id;
1414  } else {
1415  $userid = $userobj; // old method
1416  }
1417 
1418  $id = $this->id;
1419  if (empty($id) && empty($userid)) {
1420  dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1421  return -1;
1422  }
1423 
1424  $result = array();
1425 
1426  $sql = "SELECT";
1427  $sql .= " MIN(t.task_datehour) as min_date,";
1428  $sql .= " MAX(t.task_datehour) as max_date,";
1429  $sql .= " SUM(t.task_duration) as total_duration,";
1430  $sql .= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1431  $sql .= " COUNT(t.rowid) as nblines,";
1432  $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1433  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1434  $sql .= " WHERE 1 = 1";
1435  if ($morewherefilter) {
1436  $sql .= $morewherefilter;
1437  }
1438  if ($id > 0) {
1439  $sql .= " AND t.fk_task = ".((int) $id);
1440  }
1441  if ($userid > 0) {
1442  $sql .= " AND t.fk_user = ".((int) $userid);
1443  }
1444 
1445  dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1446  $resql = $this->db->query($sql);
1447  if ($resql) {
1448  $obj = $this->db->fetch_object($resql);
1449 
1450  $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1451  $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1452  $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1453 
1454  $this->timespent_min_date = $this->db->jdate($obj->min_date);
1455  $this->timespent_max_date = $this->db->jdate($obj->max_date);
1456  $this->timespent_total_duration = $obj->total_duration;
1457  $this->timespent_total_amount = $obj->total_amount;
1458  $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1459  $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1460 
1461  $this->db->free($resql);
1462  } else {
1463  dol_print_error($this->db);
1464  }
1465  return $result;
1466  }
1467 
1476  public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1477  {
1478  global $langs;
1479 
1480  if (empty($id)) {
1481  $id = $this->id;
1482  }
1483 
1484  $result = array();
1485 
1486  $sql = "SELECT";
1487  $sql .= " SUM(t.task_duration) as nbseconds,";
1488  $sql .= " SUM(t.task_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";
1489  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1490  $sql .= " WHERE t.fk_task = ".((int) $id);
1491  if (is_object($fuser) && $fuser->id > 0) {
1492  $sql .= " AND fk_user = ".((int) $fuser->id);
1493  }
1494  if ($dates > 0) {
1495  $datefieldname = "task_datehour";
1496  $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1497  }
1498  if ($datee > 0) {
1499  $datefieldname = "task_datehour";
1500  $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1501  }
1502  //print $sql;
1503 
1504  dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1505  $resql = $this->db->query($sql);
1506  if ($resql) {
1507  $obj = $this->db->fetch_object($resql);
1508 
1509  $result['amount'] = $obj->amount;
1510  $result['nbseconds'] = $obj->nbseconds;
1511  $result['nblinesnull'] = $obj->nblinesnull;
1512 
1513  $this->db->free($resql);
1514  return $result;
1515  } else {
1516  dol_print_error($this->db);
1517  return $result;
1518  }
1519  }
1520 
1527  public function fetchTimeSpent($id)
1528  {
1529  global $langs;
1530 
1531  $sql = "SELECT";
1532  $sql .= " t.rowid,";
1533  $sql .= " t.fk_task,";
1534  $sql .= " t.task_date,";
1535  $sql .= " t.task_datehour,";
1536  $sql .= " t.task_date_withhour,";
1537  $sql .= " t.task_duration,";
1538  $sql .= " t.fk_user,";
1539  $sql .= " t.fk_product,";
1540  $sql .= " t.thm,";
1541  $sql .= " t.note";
1542  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1543  $sql .= " WHERE t.rowid = ".((int) $id);
1544 
1545  dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1546  $resql = $this->db->query($sql);
1547  if ($resql) {
1548  if ($this->db->num_rows($resql)) {
1549  $obj = $this->db->fetch_object($resql);
1550 
1551  $this->timespent_id = $obj->rowid;
1552  $this->id = $obj->fk_task;
1553  $this->timespent_date = $this->db->jdate($obj->task_date);
1554  $this->timespent_datehour = $this->db->jdate($obj->task_datehour);
1555  $this->timespent_withhour = $obj->task_date_withhour;
1556  $this->timespent_duration = $obj->task_duration;
1557  $this->timespent_fk_user = $obj->fk_user;
1558  $this->timespent_fk_product = $obj->fk_product;
1559  $this->timespent_thm = $obj->thm; // hourly rate
1560  $this->timespent_note = $obj->note;
1561  }
1562 
1563  $this->db->free($resql);
1564 
1565  return 1;
1566  } else {
1567  $this->error = "Error ".$this->db->lasterror();
1568  return -1;
1569  }
1570  }
1571 
1579  public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1580  {
1581  global $langs;
1582 
1583  $arrayres = array();
1584 
1585  $sql = "SELECT";
1586  $sql .= " s.rowid as socid,";
1587  $sql .= " s.nom as thirdparty_name,";
1588  $sql .= " s.email as thirdparty_email,";
1589  $sql .= " ptt.rowid,";
1590  $sql .= " ptt.fk_task,";
1591  $sql .= " ptt.task_date,";
1592  $sql .= " ptt.task_datehour,";
1593  $sql .= " ptt.task_date_withhour,";
1594  $sql .= " ptt.task_duration,";
1595  $sql .= " ptt.fk_user,";
1596  $sql .= " ptt.note,";
1597  $sql .= " ptt.thm,";
1598  $sql .= " pt.rowid as task_id,";
1599  $sql .= " pt.ref as task_ref,";
1600  $sql .= " pt.label as task_label,";
1601  $sql .= " p.rowid as project_id,";
1602  $sql .= " p.ref as project_ref,";
1603  $sql .= " p.title as project_label,";
1604  $sql .= " p.public as public";
1605  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1606  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1607  $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
1608  $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1609  $sql .= " AND pt.entity IN (".getEntity('project').")";
1610  if ($morewherefilter) {
1611  $sql .= $morewherefilter;
1612  }
1613 
1614  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1615  $resql = $this->db->query($sql);
1616  if ($resql) {
1617  $num = $this->db->num_rows($resql);
1618 
1619  $i = 0;
1620  while ($i < $num) {
1621  $obj = $this->db->fetch_object($resql);
1622 
1623  $newobj = new stdClass();
1624 
1625  $newobj->socid = $obj->socid;
1626  $newobj->thirdparty_name = $obj->thirdparty_name;
1627  $newobj->thirdparty_email = $obj->thirdparty_email;
1628 
1629  $newobj->fk_project = $obj->project_id;
1630  $newobj->project_ref = $obj->project_ref;
1631  $newobj->project_label = $obj->project_label;
1632  $newobj->public = $obj->project_public;
1633 
1634  $newobj->fk_task = $obj->task_id;
1635  $newobj->task_ref = $obj->task_ref;
1636  $newobj->task_label = $obj->task_label;
1637 
1638  $newobj->timespent_id = $obj->rowid;
1639  $newobj->timespent_date = $this->db->jdate($obj->task_date);
1640  $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1641  $newobj->timespent_withhour = $obj->task_date_withhour;
1642  $newobj->timespent_duration = $obj->task_duration;
1643  $newobj->timespent_fk_user = $obj->fk_user;
1644  $newobj->timespent_thm = $obj->thm; // hourly rate
1645  $newobj->timespent_note = $obj->note;
1646 
1647  $arrayres[] = $newobj;
1648 
1649  $i++;
1650  }
1651 
1652  $this->db->free($resql);
1653  } else {
1654  dol_print_error($this->db);
1655  $this->error = "Error ".$this->db->lasterror();
1656  return -1;
1657  }
1658 
1659  return $arrayres;
1660  }
1661 
1669  public function updateTimeSpent($user, $notrigger = 0)
1670  {
1671  global $conf, $langs;
1672 
1673  $ret = 0;
1674 
1675  // Check parameters
1676  if ($this->timespent_date == '') {
1677  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1678  return -1;
1679  }
1680  if (!($this->timespent_fk_user > 0)) {
1681  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1682  return -1;
1683  }
1684 
1685  // Clean parameters
1686  if (empty($this->timespent_datehour)) {
1687  $this->timespent_datehour = $this->timespent_date;
1688  }
1689  if (isset($this->timespent_note)) {
1690  $this->timespent_note = trim($this->timespent_note);
1691  }
1692 
1693  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1694  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1695  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1696 
1697  if ($this->timespent_date < $restrictBefore) {
1698  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1699  $this->errors[] = $this->error;
1700  return -1;
1701  }
1702  }
1703 
1704  $this->db->begin();
1705 
1706  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time SET";
1707  $sql .= " task_date = '".$this->db->idate($this->timespent_date)."',";
1708  $sql .= " task_datehour = '".$this->db->idate($this->timespent_datehour)."',";
1709  $sql .= " task_date_withhour = ".(empty($this->timespent_withhour) ? 0 : 1).",";
1710  $sql .= " task_duration = ".((int) $this->timespent_duration).",";
1711  $sql .= " fk_user = ".((int) $this->timespent_fk_user).",";
1712  $sql .= " fk_product = ".((int) $this->timespent_fk_product).",";
1713  $sql .= " note = ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null");
1714  $sql .= " WHERE rowid = ".((int) $this->timespent_id);
1715 
1716  dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1717  if ($this->db->query($sql)) {
1718  if (!$notrigger) {
1719  // Call trigger
1720  $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1721  if ($result < 0) {
1722  $this->db->rollback();
1723  $ret = -1;
1724  } else {
1725  $ret = 1;
1726  }
1727  // End call triggers
1728  } else {
1729  $ret = 1;
1730  }
1731  } else {
1732  $this->error = $this->db->lasterror();
1733  $this->db->rollback();
1734  $ret = -1;
1735  }
1736 
1737  if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM))) {
1738  if ($this->timespent_old_duration != $this->timespent_duration) {
1739  // Recalculate amount of time spent for task and update denormalized field
1740  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1741  $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM " . MAIN_DB_PREFIX . "projet_task_time as ptt where ptt.fk_task = " . ((int) $this->id) . ")";
1742  if (isset($this->progress)) {
1743  $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1744  }
1745  $sql .= " WHERE rowid = " . ((int) $this->id);
1746 
1747  dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1748  if (!$this->db->query($sql)) {
1749  $this->error = $this->db->lasterror();
1750  $this->db->rollback();
1751  $ret = -2;
1752  }
1753  }
1754 
1755  // Update hourly rate of this time spent entry, but only if it was not set initialy
1756  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
1757  $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user
1758  $sql .= " WHERE rowid = ".((int) $this->timespent_id);
1759  if (empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM)) { // then if not empty we always update, in case of new thm for user, or change user of task time line
1760  $sql .= " AND (thm IS NULL OR thm = 0)";
1761  }
1762 
1763  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1764  if (!$this->db->query($sql)) {
1765  $this->error = $this->db->lasterror();
1766  $ret = -2;
1767  }
1768  }
1769 
1770  if ($ret >= 0) {
1771  $this->db->commit();
1772  }
1773  return $ret;
1774  }
1775 
1783  public function delTimeSpent($user, $notrigger = 0)
1784  {
1785  global $conf, $langs;
1786 
1787  $error = 0;
1788 
1789  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1790  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1791  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1792 
1793  if ($this->timespent_date < $restrictBefore) {
1794  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1795  $this->errors[] = $this->error;
1796  return -1;
1797  }
1798  }
1799 
1800  $this->db->begin();
1801 
1802  if (!$notrigger) {
1803  // Call trigger
1804  $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
1805  if ($result < 0) {
1806  $error++;
1807  }
1808  // End call triggers
1809  }
1810 
1811  if (!$error) {
1812  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
1813  $sql .= " WHERE rowid = ".((int) $this->timespent_id);
1814 
1815  dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1816  $resql = $this->db->query($sql);
1817  if (!$resql) {
1818  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1819  }
1820  }
1821 
1822  if (!$error) {
1823  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1824  $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
1825  $sql .= " WHERE rowid = ".((int) $this->id);
1826 
1827  dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1828  if ($this->db->query($sql)) {
1829  $result = 0;
1830  } else {
1831  $this->error = $this->db->lasterror();
1832  $result = -2;
1833  }
1834  }
1835 
1836  // Commit or rollback
1837  if ($error) {
1838  foreach ($this->errors as $errmsg) {
1839  dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
1840  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1841  }
1842  $this->db->rollback();
1843  return -1 * $error;
1844  } else {
1845  $this->db->commit();
1846  return 1;
1847  }
1848  }
1849 
1864  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)
1865  {
1866  global $langs, $conf;
1867 
1868  $error = 0;
1869 
1870  //Use 00:00 of today if time is use on task.
1871  $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
1872 
1873  $datec = $now;
1874 
1875  $clone_task = new Task($this->db);
1876  $origin_task = new Task($this->db);
1877 
1878  $clone_task->context['createfromclone'] = 'createfromclone';
1879 
1880  $this->db->begin();
1881 
1882  // Load source object
1883  $clone_task->fetch($fromid);
1884  $clone_task->fetch_optionals();
1885  //var_dump($clone_task->array_options);exit;
1886 
1887  $origin_task->fetch($fromid);
1888 
1889  $defaultref = '';
1890  $obj = empty($conf->global->PROJECT_TASK_ADDON) ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
1891  if (!empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.".php")) {
1892  require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.'.php';
1893  $modTask = new $obj;
1894  $defaultref = $modTask->getNextValue(0, $clone_task);
1895  }
1896 
1897  $ori_project_id = $clone_task->fk_project;
1898 
1899  $clone_task->id = 0;
1900  $clone_task->ref = $defaultref;
1901  $clone_task->fk_project = $project_id;
1902  $clone_task->fk_task_parent = $parent_task_id;
1903  $clone_task->date_c = $datec;
1904  $clone_task->planned_workload = $origin_task->planned_workload;
1905  $clone_task->rang = $origin_task->rang;
1906 
1907  //Manage Task Date
1908  if ($clone_change_dt) {
1909  $projectstatic = new Project($this->db);
1910  $projectstatic->fetch($ori_project_id);
1911 
1912  //Origin project strat date
1913  $orign_project_dt_start = $projectstatic->date_start;
1914 
1915  //Calcultate new task start date with difference between origin proj start date and origin task start date
1916  if (!empty($clone_task->date_start)) {
1917  $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
1918  }
1919 
1920  //Calcultate new task end date with difference between origin proj end date and origin task end date
1921  if (!empty($clone_task->date_end)) {
1922  $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
1923  }
1924  }
1925 
1926  if (!$clone_prog) {
1927  $clone_task->progress = 0;
1928  }
1929 
1930  // Create clone
1931  $result = $clone_task->create($user);
1932 
1933  // Other options
1934  if ($result < 0) {
1935  $this->error = $clone_task->error;
1936  $error++;
1937  }
1938 
1939  // End
1940  if (!$error) {
1941  $clone_task_id = $clone_task->id;
1942  $clone_task_ref = $clone_task->ref;
1943 
1944  //Note Update
1945  if (!$clone_note) {
1946  $clone_task->note_private = '';
1947  $clone_task->note_public = '';
1948  } else {
1949  $this->db->begin();
1950  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
1951  if ($res < 0) {
1952  $this->error .= $clone_task->error;
1953  $error++;
1954  $this->db->rollback();
1955  } else {
1956  $this->db->commit();
1957  }
1958 
1959  $this->db->begin();
1960  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
1961  if ($res < 0) {
1962  $this->error .= $clone_task->error;
1963  $error++;
1964  $this->db->rollback();
1965  } else {
1966  $this->db->commit();
1967  }
1968  }
1969 
1970  //Duplicate file
1971  if ($clone_file) {
1972  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1973 
1974  //retrieve project origin ref to know folder to copy
1975  $projectstatic = new Project($this->db);
1976  $projectstatic->fetch($ori_project_id);
1977  $ori_project_ref = $projectstatic->ref;
1978 
1979  if ($ori_project_id != $project_id) {
1980  $projectstatic->fetch($project_id);
1981  $clone_project_ref = $projectstatic->ref;
1982  } else {
1983  $clone_project_ref = $ori_project_ref;
1984  }
1985 
1986  $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
1987  $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
1988 
1989  $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1990  foreach ($filearray as $key => $file) {
1991  if (!file_exists($clone_task_dir)) {
1992  if (dol_mkdir($clone_task_dir) < 0) {
1993  $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1994  $error++;
1995  }
1996  }
1997 
1998  $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
1999  if (is_numeric($rescopy) && $rescopy < 0) {
2000  $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
2001  $error++;
2002  }
2003  }
2004  }
2005 
2006  // clone affectation
2007  if ($clone_affectation) {
2008  $origin_task = new Task($this->db);
2009  $origin_task->fetch($fromid);
2010 
2011  foreach (array('internal', 'external') as $source) {
2012  $tab = $origin_task->liste_contact(-1, $source);
2013  $num = count($tab);
2014  $i = 0;
2015  while ($i < $num) {
2016  $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2017  if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2018  $langs->load("errors");
2019  $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2020  $error++;
2021  } else {
2022  if ($clone_task->error != '') {
2023  $this->error .= $clone_task->error;
2024  $error++;
2025  }
2026  }
2027  $i++;
2028  }
2029  }
2030  }
2031 
2032  if ($clone_time) {
2033  //TODO clone time of affectation
2034  }
2035  }
2036 
2037  unset($clone_task->context['createfromclone']);
2038 
2039  if (!$error) {
2040  $this->db->commit();
2041  return $clone_task_id;
2042  } else {
2043  $this->db->rollback();
2044  dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
2045  return -1;
2046  }
2047  }
2048 
2049 
2056  public function getLibStatut($mode = 0)
2057  {
2058  return $this->LibStatut($this->fk_statut, $mode);
2059  }
2060 
2061  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2069  public function LibStatut($status, $mode = 0)
2070  {
2071  // phpcs:enable
2072  global $langs;
2073 
2074  // list of Statut of the task
2075  $this->statuts[0] = 'Draft';
2076  $this->statuts[1] = 'ToDo';
2077  $this->statuts[2] = 'Running';
2078  $this->statuts[3] = 'Finish';
2079  $this->statuts[4] = 'Transfered';
2080  $this->statuts_short[0] = 'Draft';
2081  $this->statuts_short[1] = 'ToDo';
2082  $this->statuts_short[2] = 'Running';
2083  $this->statuts_short[3] = 'Completed';
2084  $this->statuts_short[4] = 'Transfered';
2085 
2086  if ($mode == 0) {
2087  return $langs->trans($this->statuts[$status]);
2088  } elseif ($mode == 1) {
2089  return $langs->trans($this->statuts_short[$status]);
2090  } elseif ($mode == 2) {
2091  if ($status == 0) {
2092  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts_short[$status]);
2093  } elseif ($status == 1) {
2094  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts_short[$status]);
2095  } elseif ($status == 2) {
2096  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts_short[$status]);
2097  } elseif ($status == 3) {
2098  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
2099  } elseif ($status == 4) {
2100  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
2101  } elseif ($status == 5) {
2102  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts_short[$status]);
2103  }
2104  } elseif ($mode == 3) {
2105  if ($status == 0) {
2106  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0');
2107  } elseif ($status == 1) {
2108  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1');
2109  } elseif ($status == 2) {
2110  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3');
2111  } elseif ($status == 3) {
2112  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
2113  } elseif ($status == 4) {
2114  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
2115  } elseif ($status == 5) {
2116  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5');
2117  }
2118  } elseif ($mode == 4) {
2119  if ($status == 0) {
2120  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts[$status]);
2121  } elseif ($status == 1) {
2122  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts[$status]);
2123  } elseif ($status == 2) {
2124  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts[$status]);
2125  } elseif ($status == 3) {
2126  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
2127  } elseif ($status == 4) {
2128  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
2129  } elseif ($status == 5) {
2130  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts[$status]);
2131  }
2132  } elseif ($mode == 5) {
2133  /*if ($status==0) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
2134  elseif ($status==1) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
2135  elseif ($status==2) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
2136  elseif ($status==3) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2137  elseif ($status==4) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2138  elseif ($status==5) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
2139  */
2140  //else return $this->progress.' %';
2141  return '&nbsp;';
2142  } elseif ($mode == 6) {
2143  /*if ($status==0) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
2144  elseif ($status==1) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
2145  elseif ($status==2) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
2146  elseif ($status==3) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2147  elseif ($status==4) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2148  elseif ($status==5) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
2149  */
2150  //else return $this->progress.' %';
2151  return '&nbsp;';
2152  }
2153  }
2154 
2165  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2166  {
2167  global $conf;
2168 
2169  $outputlangs->load("projects");
2170 
2171  if (!dol_strlen($modele)) {
2172  $modele = 'nodefault';
2173 
2174  if (!empty($this->model_pdf)) {
2175  $modele = $this->model_pdf;
2176  } elseif (!empty($this->modelpdf)) { // deprecated
2177  $modele = $this->modelpdf;
2178  } elseif (!empty($conf->global->PROJECT_TASK_ADDON_PDF)) {
2179  $modele = $conf->global->PROJECT_TASK_ADDON_PDF;
2180  }
2181  }
2182 
2183  $modelpath = "core/modules/project/task/doc/";
2184 
2185  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2186  }
2187 
2188 
2189  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2196  public function load_board($user)
2197  {
2198  // phpcs:enable
2199  global $conf, $langs;
2200 
2201  // For external user, no check is done on company because readability is managed by public status of project and assignement.
2202  //$socid = $user->socid;
2203  $socid = 0;
2204 
2205  $projectstatic = new Project($this->db);
2206  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2207 
2208  // List of tasks (does not care about permissions. Filtering will be done later)
2209  $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2210  $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2211  $sql .= " t.dateo as date_start, t.datee as datee";
2212  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2213  //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2214  //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2215  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2216  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2217  $sql .= " AND p.fk_statut = 1";
2218  $sql .= " AND t.fk_projet = p.rowid";
2219  $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2220  if (empty($user->rights->projet->all->lire)) {
2221  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2222  }
2223  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2224  //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).")";
2225  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2226  // 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))";
2227 
2228  //print $sql;
2229  $resql = $this->db->query($sql);
2230  if ($resql) {
2231  $task_static = new Task($this->db);
2232 
2233  $response = new WorkboardResponse();
2234  $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2235  $response->label = $langs->trans("OpenedTasks");
2236  if ($user->hasRight("projet", "all", "lire")) {
2237  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2238  } else {
2239  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2240  }
2241  $response->img = img_object('', "task");
2242 
2243  // This assignment in condition is not a bug. It allows walking the results.
2244  while ($obj = $this->db->fetch_object($resql)) {
2245  $response->nbtodo++;
2246 
2247  $task_static->projectstatus = $obj->projectstatus;
2248  $task_static->progress = $obj->progress;
2249  $task_static->fk_statut = $obj->status;
2250  $task_static->date_end = $this->db->jdate($obj->datee);
2251 
2252  if ($task_static->hasDelay()) {
2253  $response->nbtodolate++;
2254  }
2255  }
2256 
2257  return $response;
2258  } else {
2259  $this->error = $this->db->error();
2260  return -1;
2261  }
2262  }
2263 
2264 
2265  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2271  public function load_state_board()
2272  {
2273  // phpcs:enable
2274  global $user;
2275 
2276  $mine = 0; $socid = $user->socid;
2277 
2278  $projectstatic = new Project($this->db);
2279  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2280 
2281  // List of tasks (does not care about permissions. Filtering will be done later)
2282  $sql = "SELECT count(p.rowid) as nb";
2283  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2284  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2285  if (empty($user->rights->societe->client->voir) && !$socid) {
2286  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2287  }
2288  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2289  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2290  $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2291  if ($mine || empty($user->rights->projet->all->lire)) {
2292  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2293  }
2294  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2295  //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).")";
2296  if ($socid) {
2297  $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2298  }
2299  if (empty($user->rights->societe->client->voir) && !$socid) {
2300  $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2301  }
2302 
2303  $resql = $this->db->query($sql);
2304  if ($resql) {
2305  // This assignment in condition is not a bug. It allows walking the results.
2306  while ($obj = $this->db->fetch_object($resql)) {
2307  $this->nb["tasks"] = $obj->nb;
2308  }
2309  $this->db->free($resql);
2310  return 1;
2311  } else {
2312  dol_print_error($this->db);
2313  $this->error = $this->db->error();
2314  return -1;
2315  }
2316  }
2317 
2323  public function hasDelay()
2324  {
2325  global $conf;
2326 
2327  if (!($this->progress >= 0 && $this->progress < 100)) {
2328  return false;
2329  }
2330 
2331  $now = dol_now();
2332 
2333  $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
2334 
2335  return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2336  }
2337 }
$object ref
Definition: info.php:78
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...
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
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:38
create($user, $notrigger=0)
Create into database.
Definition: task.class.php:165
fetch($id, $ref='', $loadparentdata=0)
Load object in memory from database.
Definition: task.class.php:271
getLibStatut($mode=0)
Return status label of object.
getSumOfAmount($fuser='', $dates='', $datee='')
Calculate quantity and value of time consumed using the thm (hourly amount value of work for user ent...
__construct($db)
Constructor.
Definition: task.class.php:152
fetchTimeSpentOnTask($morewherefilter='')
Fetch records of time spent of this task.
hasTimeSpent()
Return nb of time spent.
Definition: task.class.php:672
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:713
getListContactId($source='internal')
Return list of id of contacts of task.
delTimeSpent($user, $notrigger=0)
Delete time spent.
getTasksArray($usert=null, $userp=null, $projectid=0, $socid=0, $mode=0, $filteronproj='', $filteronprojstatus='-1', $morewherefilter='', $filteronprojuser=0, $filterontaskuser=0, $extrafields=array(), $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:821
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:373
getSummaryOfTimeSpent($userobj=null, $morewherefilter='')
Calculate total of time spent for task.
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.
initAsSpecimen()
Initialise an instance with random values.
Definition: task.class.php:783
load_state_board()
Charge indicateurs this->nb de tableau de bord.
addTimeSpent($user, $notrigger=0)
Add time spent.
hasDelay()
Is the task delayed?
LibStatut($status, $mode=0)
Return status label for an object.
hasChildren()
Return nb of children.
Definition: task.class.php:639
Class to manage Dolibarr users.
Definition: user.class.php:47
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:121
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:1402
dol_copy($srcfile, $destfile, $newmask=0, $overwriteifexists=1)
Copy a file to another file.
Definition: files.lib.php:713
dol_move_dir($srcdir, $destdir, $overwriteifexists=1, $indexdatabase=1, $renamedircontent=1)
Move a directory into another name.
Definition: files.lib.php:999
dol_dir_list($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:61
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 informations (by default a local PHP server timestamp) Re...
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
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
Buy price without taxes.
Definition: style.css.php:913
$conf db
API class for accounts.
Definition: inc.php:41