dolibarr  18.0.6
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-2023 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  * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
30 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
32 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
33 require_once DOL_DOCUMENT_ROOT.'/core/class/timespent.class.php';
34 
35 
39 class Task extends CommonObjectLine
40 {
44  public $element = 'project_task';
45 
49  public $table_element = 'projet_task';
50 
54  public $fk_element = 'fk_element';
55 
59  public $picto = 'projecttask';
60 
64  protected $childtables = array(
65  'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task')
66  );
67 
71  public $fk_task_parent = 0;
72 
76  public $label;
77 
81  public $description;
82 
83  public $duration_effective; // total of time spent on this task
84  public $planned_workload;
85  public $date_c;
86  public $date_start;
87  public $date_end;
88  public $progress;
89 
93  public $datee;
94 
98  public $fk_statut;
99 
100  public $priority;
101 
105  public $fk_user_creat;
106 
110  public $fk_user_valid;
111 
112  public $rang;
113 
114  public $timespent_min_date;
115  public $timespent_max_date;
116  public $timespent_total_duration;
117  public $timespent_total_amount;
118  public $timespent_nblinesnull;
119  public $timespent_nblines;
120  // For detail of lines of timespent record, there is the property ->lines in common
121 
122  // Var used to call method addTimeSpent(). Bad practice.
123  public $timespent_id;
124  public $timespent_duration;
125  public $timespent_old_duration;
126  public $timespent_date;
127  public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
128  public $timespent_withhour; // 1 = we entered also start hours for timesheet line
129  public $timespent_fk_user;
130  public $timespent_thm;
131  public $timespent_note;
132  public $timespent_fk_product;
133  public $timespent_invoiceid;
134  public $timespent_invoicelineid;
135 
136  public $comments = array();
137 
141  public $statuts;
142 
146  public $statuts_short;
147 
148  // Properties calculated from sum of llx_element_time linked to task
149  public $tobill;
150  public $billed;
151 
152  // Properties to store project informations
153  public $projectref;
154  public $projectstatus;
155  public $projectlabel;
156  public $opp_amount;
157  public $opp_percent;
158  public $fk_opp_status;
159  public $usage_bill_time;
160  public $public;
161  public $array_options_project;
162 
163  // Properties to store thirdparty of project information
164  public $socid;
165  public $thirdparty_id;
166  public $thirdparty_name;
167  public $thirdparty_email;
168 
169 
173  public $budget_amount;
174 
178  public $project_budget_amount;
179 
180 
181 
187  public function __construct($db)
188  {
189  $this->db = $db;
190  }
191 
192 
200  public function create($user, $notrigger = 0)
201  {
202  global $conf, $langs;
203 
204  //For the date
205  $now = dol_now();
206 
207  $error = 0;
208 
209  // Clean parameters
210  $this->label = trim($this->label);
211  $this->description = trim($this->description);
212  $this->note_public = trim($this->note_public);
213  $this->note_private = trim($this->note_private);
214 
215  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
216  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
217  return -1;
218  }
219 
220  // Check parameters
221  // Put here code to add control on parameters values
222 
223  // Insert request
224  $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
225  $sql .= "entity";
226  $sql .= ", fk_projet";
227  $sql .= ", ref";
228  $sql .= ", fk_task_parent";
229  $sql .= ", label";
230  $sql .= ", description";
231  $sql .= ", note_public";
232  $sql .= ", note_private";
233  $sql .= ", datec";
234  $sql .= ", fk_user_creat";
235  $sql .= ", dateo";
236  $sql .= ", datee";
237  $sql .= ", planned_workload";
238  $sql .= ", progress";
239  $sql .= ", budget_amount";
240  $sql .= ", priority";
241  $sql .= ") VALUES (";
242  $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
243  $sql .= ", ".((int) $this->fk_project);
244  $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
245  $sql .= ", ".((int) $this->fk_task_parent);
246  $sql .= ", '".$this->db->escape($this->label)."'";
247  $sql .= ", '".$this->db->escape($this->description)."'";
248  $sql .= ", '".$this->db->escape($this->note_public)."'";
249  $sql .= ", '".$this->db->escape($this->note_private)."'";
250  $sql .= ", '".$this->db->idate($now)."'";
251  $sql .= ", ".((int) $user->id);
252  $sql .= ", ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : 'null');
253  $sql .= ", ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : 'null');
254  $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
255  $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
256  $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
257  $sql .= ", ".(($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null');
258  $sql .= ")";
259 
260  $this->db->begin();
261 
262  dol_syslog(get_class($this)."::create", LOG_DEBUG);
263  $resql = $this->db->query($sql);
264  if (!$resql) {
265  $error++; $this->errors[] = "Error ".$this->db->lasterror();
266  }
267 
268  if (!$error) {
269  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
270 
271  if (!$notrigger) {
272  // Call trigger
273  $result = $this->call_trigger('TASK_CREATE', $user);
274  if ($result < 0) {
275  $error++;
276  }
277  // End call triggers
278  }
279  }
280 
281  // Update extrafield
282  if (!$error) {
283  if (!$error) {
284  $result = $this->insertExtraFields();
285  if ($result < 0) {
286  $error++;
287  }
288  }
289  }
290 
291  // Commit or rollback
292  if ($error) {
293  foreach ($this->errors as $errmsg) {
294  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
295  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
296  }
297  $this->db->rollback();
298  return -1 * $error;
299  } else {
300  $this->db->commit();
301  return $this->id;
302  }
303  }
304 
305 
314  public function fetch($id, $ref = '', $loadparentdata = 0)
315  {
316  global $langs, $conf;
317 
318  $sql = "SELECT";
319  $sql .= " t.rowid,";
320  $sql .= " t.ref,";
321  $sql .= " t.entity,";
322  $sql .= " t.fk_projet as fk_project,";
323  $sql .= " t.fk_task_parent,";
324  $sql .= " t.label,";
325  $sql .= " t.description,";
326  $sql .= " t.duration_effective,";
327  $sql .= " t.planned_workload,";
328  $sql .= " t.datec,";
329  $sql .= " t.dateo,";
330  $sql .= " t.datee,";
331  $sql .= " t.fk_user_creat,";
332  $sql .= " t.fk_user_valid,";
333  $sql .= " t.fk_statut,";
334  $sql .= " t.progress,";
335  $sql .= " t.budget_amount,";
336  $sql .= " t.priority,";
337  $sql .= " t.note_private,";
338  $sql .= " t.note_public,";
339  $sql .= " t.rang";
340  if (!empty($loadparentdata)) {
341  $sql .= ", t2.ref as task_parent_ref";
342  $sql .= ", t2.rang as task_parent_position";
343  }
344  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
345  if (!empty($loadparentdata)) {
346  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
347  }
348  $sql .= " WHERE ";
349  if (!empty($ref)) {
350  $sql .= "entity IN (".getEntity('project').")";
351  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
352  } else {
353  $sql .= "t.rowid = ".((int) $id);
354  }
355 
356  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
357  $resql = $this->db->query($sql);
358  if ($resql) {
359  $num_rows = $this->db->num_rows($resql);
360 
361  if ($num_rows) {
362  $obj = $this->db->fetch_object($resql);
363 
364  $this->id = $obj->rowid;
365  $this->ref = $obj->ref;
366  $this->entity = $obj->entity;
367  $this->fk_project = $obj->fk_project;
368  $this->fk_task_parent = $obj->fk_task_parent;
369  $this->label = $obj->label;
370  $this->description = $obj->description;
371  $this->duration_effective = $obj->duration_effective;
372  $this->planned_workload = $obj->planned_workload;
373  $this->date_c = $this->db->jdate($obj->datec);
374  $this->date_start = $this->db->jdate($obj->dateo);
375  $this->date_end = $this->db->jdate($obj->datee);
376  $this->fk_user_creat = $obj->fk_user_creat;
377  $this->fk_user_valid = $obj->fk_user_valid;
378  $this->fk_statut = $obj->fk_statut;
379  $this->progress = $obj->progress;
380  $this->budget_amount = $obj->budget_amount;
381  $this->priority = $obj->priority;
382  $this->note_private = $obj->note_private;
383  $this->note_public = $obj->note_public;
384  $this->rang = $obj->rang;
385 
386  if (!empty($loadparentdata)) {
387  $this->task_parent_ref = $obj->task_parent_ref;
388  $this->task_parent_position = $obj->task_parent_position;
389  }
390 
391  // Retrieve all extrafield
392  $this->fetch_optionals();
393  }
394 
395  $this->db->free($resql);
396 
397  if ($num_rows) {
398  return 1;
399  } else {
400  return 0;
401  }
402  } else {
403  $this->error = "Error ".$this->db->lasterror();
404  return -1;
405  }
406  }
407 
408 
416  public function update($user = null, $notrigger = 0)
417  {
418  global $conf, $langs;
419  $error = 0;
420 
421  // Clean parameters
422  if (isset($this->fk_project)) {
423  $this->fk_project = trim($this->fk_project);
424  }
425  if (isset($this->ref)) {
426  $this->ref = trim($this->ref);
427  }
428  if (isset($this->fk_task_parent)) {
429  $this->fk_task_parent = (int) $this->fk_task_parent;
430  }
431  if (isset($this->label)) {
432  $this->label = trim($this->label);
433  }
434  if (isset($this->description)) {
435  $this->description = trim($this->description);
436  }
437  if (isset($this->note_public)) {
438  $this->note_public = trim($this->note_public);
439  }
440  if (isset($this->note_private)) {
441  $this->note_private = trim($this->note_private);
442  }
443  if (isset($this->duration_effective)) {
444  $this->duration_effective = trim($this->duration_effective);
445  }
446  if (isset($this->planned_workload)) {
447  $this->planned_workload = trim($this->planned_workload);
448  }
449  if (isset($this->budget_amount)) {
450  $this->budget_amount = trim($this->budget_amount);
451  }
452 
453  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
454  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
455  return -1;
456  }
457 
458  // Check parameters
459  // Put here code to add control on parameters values
460 
461  // Update request
462  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
463  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
464  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
465  $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
466  $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
467  $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
468  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
469  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
470  $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
471  $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
472  $sql .= " dateo=".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
473  $sql .= " datee=".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
474  $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
475  $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
476  $sql .= " rang=".((!empty($this->rang)) ? ((int) $this->rang) : "0").",";
477  $sql .= " priority=".((!empty($this->priority)) ? ((int) $this->priority) : "0");
478  $sql .= " WHERE rowid=".((int) $this->id);
479 
480  $this->db->begin();
481 
482  dol_syslog(get_class($this)."::update", LOG_DEBUG);
483  $resql = $this->db->query($sql);
484  if (!$resql) {
485  $error++; $this->errors[] = "Error ".$this->db->lasterror();
486  }
487 
488  // Update extrafield
489  if (!$error) {
490  $result = $this->insertExtraFields();
491  if ($result < 0) {
492  $error++;
493  }
494  }
495 
496  if (!$error && !empty($conf->global->PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE)) {
497  // Close the parent project if it is open (validated) and its tasks are 100% completed
498  $project = new Project($this->db);
499  if ($project->fetch($this->fk_project) > 0) {
500  if ($project->statut == Project::STATUS_VALIDATED) {
501  $project->getLinesArray(null); // this method does not return <= 0 if fails
502  $projectCompleted = array_reduce(
503  $project->lines,
504  function ($allTasksCompleted, $task) {
505  return $allTasksCompleted && $task->progress >= 100;
506  },
507  1
508  );
509  if ($projectCompleted) {
510  if ($project->setClose($user) <= 0) {
511  $error++;
512  }
513  }
514  }
515  } else {
516  $error++;
517  }
518  if ($error) {
519  $this->errors[] = $project->error;
520  }
521  }
522 
523  if (!$error) {
524  if (!$notrigger) {
525  // Call trigger
526  $result = $this->call_trigger('TASK_MODIFY', $user);
527  if ($result < 0) {
528  $error++;
529  }
530  // End call triggers
531  }
532  }
533 
534  if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
535  // We remove directory
536  if ($conf->project->dir_output) {
537  $project = new Project($this->db);
538  $project->fetch($this->fk_project);
539 
540  $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
541  $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
542  if (file_exists($olddir)) {
543  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
544  $res = dol_move_dir($olddir, $newdir);
545  if (!$res) {
546  $langs->load("errors");
547  $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
548  $error++;
549  }
550  }
551  }
552  }
553 
554  // Commit or rollback
555  if ($error) {
556  foreach ($this->errors as $errmsg) {
557  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
558  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
559  }
560  $this->db->rollback();
561  return -1 * $error;
562  } else {
563  $this->db->commit();
564  return 1;
565  }
566  }
567 
568 
576  public function delete($user, $notrigger = 0)
577  {
578 
579  global $conf, $langs;
580  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
581 
582  $error = 0;
583 
584  $this->db->begin();
585 
586  if ($this->hasChildren() > 0) {
587  dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
588  $this->error = 'ErrorRecordHasSubTasks';
589  $this->db->rollback();
590  return 0;
591  }
592 
593  $objectisused = $this->isObjectUsed($this->id);
594  if (!empty($objectisused)) {
595  dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
596  $this->error = 'ErrorRecordHasChildren';
597  $this->db->rollback();
598  return 0;
599  }
600 
601  if (!$error) {
602  // Delete linked contacts
603  $res = $this->delete_linked_contact();
604  if ($res < 0) {
605  $this->error = 'ErrorFailToDeleteLinkedContact';
606  //$error++;
607  $this->db->rollback();
608  return 0;
609  }
610  }
611 
612  if (!$error) {
613  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_time";
614  $sql .= " WHERE fk_element = ".((int) $this->id)." AND elementtype = 'task'";
615 
616  $resql = $this->db->query($sql);
617  if (!$resql) {
618  $error++; $this->errors[] = "Error ".$this->db->lasterror();
619  }
620  }
621 
622  if (!$error) {
623  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
624  $sql .= " WHERE fk_object = ".((int) $this->id);
625 
626  $resql = $this->db->query($sql);
627  if (!$resql) {
628  $error++; $this->errors[] = "Error ".$this->db->lasterror();
629  }
630  }
631 
632  if (!$error) {
633  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
634  $sql .= " WHERE rowid=".((int) $this->id);
635 
636  $resql = $this->db->query($sql);
637  if (!$resql) {
638  $error++; $this->errors[] = "Error ".$this->db->lasterror();
639  }
640  }
641 
642  if (!$error) {
643  if (!$notrigger) {
644  // Call trigger
645  $result = $this->call_trigger('TASK_DELETE', $user);
646  if ($result < 0) {
647  $error++;
648  }
649  // End call triggers
650  }
651  }
652 
653  // Commit or rollback
654  if ($error) {
655  foreach ($this->errors as $errmsg) {
656  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
657  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
658  }
659  $this->db->rollback();
660  return -1 * $error;
661  } else {
662  //Delete associated link file
663  if ($conf->project->dir_output) {
664  $projectstatic = new Project($this->db);
665  $projectstatic->fetch($this->fk_project);
666 
667  $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
668  dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
669  if (file_exists($dir)) {
670  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
671  $res = @dol_delete_dir_recursive($dir);
672  if (!$res) {
673  $this->error = 'ErrorFailToDeleteDir';
674  $this->db->rollback();
675  return 0;
676  }
677  }
678  }
679 
680  $this->db->commit();
681 
682  return 1;
683  }
684  }
685 
691  public function hasChildren()
692  {
693  $error = 0;
694  $ret = 0;
695 
696  $sql = "SELECT COUNT(*) as nb";
697  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
698  $sql .= " WHERE fk_task_parent = ".((int) $this->id);
699 
700  dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
701  $resql = $this->db->query($sql);
702  if (!$resql) {
703  $error++; $this->errors[] = "Error ".$this->db->lasterror();
704  } else {
705  $obj = $this->db->fetch_object($resql);
706  if ($obj) {
707  $ret = $obj->nb;
708  }
709  $this->db->free($resql);
710  }
711 
712  if (!$error) {
713  return $ret;
714  } else {
715  return -1;
716  }
717  }
718 
724  public function hasTimeSpent()
725  {
726  $error = 0;
727  $ret = 0;
728 
729  $sql = "SELECT COUNT(*) as nb";
730  $sql .= " FROM ".MAIN_DB_PREFIX."element_time";
731  $sql .= " WHERE fk_element = ".((int) $this->id);
732  $sql .= " AND elementtype = 'task'";
733 
734  dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
735  $resql = $this->db->query($sql);
736  if (!$resql) {
737  $error++; $this->errors[] = "Error ".$this->db->lasterror();
738  } else {
739  $obj = $this->db->fetch_object($resql);
740  if ($obj) {
741  $ret = $obj->nb;
742  }
743  $this->db->free($resql);
744  }
745 
746  if (!$error) {
747  return $ret;
748  } else {
749  return -1;
750  }
751  }
752 
753 
761  public function getTooltipContentArray($params)
762  {
763  global $langs;
764 
765  $langs->load('projects');
766 
767  $datas = [];
768  $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
769  if (!empty($this->ref)) {
770  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
771  }
772  if (!empty($this->label)) {
773  $datas['label'] = '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
774  }
775  if ($this->date_start || $this->date_end) {
776  $datas['range'] = "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
777  }
778 
779  return $datas;
780  }
781 
794  public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
795  {
796  global $conf, $langs, $user;
797 
798  if (!empty($conf->dol_no_mouse_hover)) {
799  $notooltip = 1; // Force disable tooltips
800  }
801 
802  $result = '';
803  $params = [
804  'id' => $this->id,
805  'objecttype' => $this->element,
806  ];
807  $classfortooltip = 'classfortooltip';
808  $dataparams = '';
809  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
810  $classfortooltip = 'classforajaxtooltip';
811  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
812  $label = '';
813  } else {
814  $label = implode($this->getTooltipContentArray($params));
815  }
816 
817  $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
818  // Add param to save lastsearch_values or not
819  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
820  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
821  $add_save_lastsearch_values = 1;
822  }
823  if ($add_save_lastsearch_values) {
824  $url .= '&save_lastsearch_values=1';
825  }
826 
827  $linkclose = '';
828  if (empty($notooltip)) {
829  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
830  $label = $langs->trans("ShowTask");
831  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
832  }
833  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
834  $linkclose .= $dataparams.' class="'.$classfortooltip.' nowraponall"';
835  } else {
836  $linkclose .= ' class="nowraponall"';
837  }
838 
839  $linkstart = '<a href="'.$url.'"';
840  $linkstart .= $linkclose.'>';
841  $linkend = '</a>';
842 
843  $picto = 'projecttask';
844 
845  $result .= $linkstart;
846  if ($withpicto) {
847  $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
848  }
849  if ($withpicto != 2) {
850  $result .= $this->ref;
851  }
852  $result .= $linkend;
853  if ($withpicto != 2) {
854  $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
855  }
856 
857  return $result;
858  }
859 
867  public function initAsSpecimen()
868  {
869  $this->id = 0;
870 
871  $this->fk_project = '';
872  $this->ref = 'TK01';
873  $this->fk_task_parent = null;
874  $this->label = 'Specimen task TK01';
875  $this->duration_effective = '';
876  $this->fk_user_creat = null;
877  $this->progress = '25';
878  $this->priority = 0;
879  $this->fk_statut = null;
880  $this->note = 'This is a specimen task not';
881  }
882 
906  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 = '')
907  {
908  global $conf, $hookmanager;
909 
910  $tasks = array();
911 
912  //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
913 
914  // List of tasks (does not care about permissions. Filtering will be done later)
915  $sql = "SELECT ";
916  if ($filteronprojuser > 0 || $filterontaskuser > 0) {
917  $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
918  }
919  $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
920  $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,";
921  $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
922  $sql .= " t.budget_amount,";
923  $sql .= " t.note_public, t.note_private,";
924  $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
925  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
926  if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
927  if (!empty($extrafields->attributes['projet']['label'])) {
928  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
929  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
930  }
931  }
932  if (!empty($extrafields->attributes['projet_task']['label'])) {
933  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
934  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
935  }
936  }
937  }
938  if ($includebilltime) {
939  $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";
940  }
941 
942  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
943  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
944  if ($loadextras) {
945  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
946  }
947 
948  if ($mode == 0) {
949  if ($filteronprojuser > 0) {
950  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
951  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
952  }
953  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
954  if ($loadextras) {
955  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
956  }
957  if ($includebilltime) {
958  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
959  }
960  if ($filterontaskuser > 0) {
961  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
962  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
963  }
964  $sql .= " WHERE p.entity IN (".getEntity('project').")";
965  $sql .= " AND t.fk_projet = p.rowid";
966  } elseif ($mode == 1) {
967  if ($filteronprojuser > 0) {
968  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
969  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
970  }
971  if ($filterontaskuser > 0) {
972  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
973  if ($includebilltime) {
974  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
975  }
976  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
977  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
978  } else {
979  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
980  if ($includebilltime) {
981  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
982  }
983  }
984  $sql .= " WHERE p.entity IN (".getEntity('project').")";
985  } else {
986  return 'BadValueForParameterMode';
987  }
988 
989  if ($filteronprojuser > 0) {
990  $sql .= " AND p.rowid = ec.element_id";
991  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
992  $sql .= " AND ctc.element = 'project'";
993  $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
994  $sql .= " AND ec.statut = 4";
995  $sql .= " AND ctc.source = 'internal'";
996  }
997  if ($filterontaskuser > 0) {
998  $sql .= " AND t.fk_projet = p.rowid";
999  $sql .= " AND p.rowid = ec2.element_id";
1000  $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
1001  $sql .= " AND ctc2.element = 'project_task'";
1002  $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
1003  $sql .= " AND ec2.statut = 4";
1004  $sql .= " AND ctc2.source = 'internal'";
1005  }
1006  if ($socid) {
1007  $sql .= " AND p.fk_soc = ".((int) $socid);
1008  }
1009  if ($projectid) {
1010  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
1011  }
1012  if ($filteronproj) {
1013  $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
1014  }
1015  if ($filteronprojstatus && $filteronprojstatus != '-1') {
1016  $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
1017  }
1018  if ($morewherefilter) {
1019  $sql .= $morewherefilter;
1020  }
1021  // Add where from extra fields
1022  $extrafieldsobjectkey = 'projet_task';
1023  $extrafieldsobjectprefix = 'efpt.';
1024  global $db; // needed for extrafields_list_search_sql.tpl
1025  include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
1026  // Add where from hooks
1027  $parameters = array();
1028  $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
1029  $sql .= $hookmanager->resPrint;
1030  if ($includebilltime) {
1031  $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
1032  $sql .= " t.datec, t.dateo, t.datee, t.tms,";
1033  $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
1034  $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
1035  $sql .= " t.budget_amount,";
1036  $sql .= " t.note_public, t.note_private,";
1037  $sql .= " s.rowid, s.nom, s.email,";
1038  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
1039  if ($loadextras) {
1040  if (!empty($extrafields->attributes['projet']['label'])) {
1041  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1042  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
1043  }
1044  }
1045  if (!empty($extrafields->attributes['projet_task']['label'])) {
1046  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1047  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
1048  }
1049  }
1050  }
1051  }
1052 
1053  if ($sortfield && $sortorder) {
1054  $sql .= $this->db->order($sortfield, $sortorder);
1055  } else {
1056  $sql .= " ORDER BY p.ref, t.rang, t.dateo";
1057  }
1058 
1059  //print $sql;exit;
1060  dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
1061  $resql = $this->db->query($sql);
1062  if ($resql) {
1063  $num = $this->db->num_rows($resql);
1064  $i = 0;
1065  // Loop on each record found, so each couple (project id, task id)
1066  while ($i < $num) {
1067  $error = 0;
1068 
1069  $obj = $this->db->fetch_object($resql);
1070 
1071  if ($loadRoleMode) {
1072  if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
1073  if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
1074  $error++;
1075  }
1076  }
1077  if (is_object($usert)) { // If we ask a filter on a user affected to a task
1078  if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
1079  $error++;
1080  }
1081  }
1082  }
1083 
1084  if (!$error) {
1085  $tasks[$i] = new Task($this->db);
1086  $tasks[$i]->id = $obj->taskid;
1087  $tasks[$i]->ref = $obj->taskref;
1088  $tasks[$i]->fk_project = $obj->projectid;
1089 
1090  // Data from project
1091  $tasks[$i]->projectref = $obj->ref;
1092  $tasks[$i]->projectlabel = $obj->plabel;
1093  $tasks[$i]->projectstatus = $obj->projectstatus;
1094  $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1095  $tasks[$i]->opp_amount = $obj->opp_amount;
1096  $tasks[$i]->opp_percent = $obj->opp_percent;
1097  $tasks[$i]->budget_amount = $obj->budget_amount;
1098  $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1099  $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1100 
1101  $tasks[$i]->label = $obj->label;
1102  $tasks[$i]->description = $obj->description;
1103 
1104  $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1105  $tasks[$i]->note_public = $obj->note_public;
1106  $tasks[$i]->note_private = $obj->note_private;
1107  $tasks[$i]->duration_effective = $obj->duration_effective;
1108  $tasks[$i]->planned_workload = $obj->planned_workload;
1109 
1110  if ($includebilltime) {
1111  // Data summed from element_time linked to task
1112  $tasks[$i]->tobill = $obj->tobill;
1113  $tasks[$i]->billed = $obj->billed;
1114  }
1115 
1116  $tasks[$i]->progress = $obj->progress;
1117  $tasks[$i]->fk_statut = $obj->status;
1118  $tasks[$i]->public = $obj->public;
1119  $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1120  $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1121  $tasks[$i]->rang = $obj->rang;
1122  $tasks[$i]->priority = $obj->priority;
1123 
1124  $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1125  $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1126  $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1127  $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1128 
1129  if ($loadextras) {
1130  if (!empty($extrafields->attributes['projet']['label'])) {
1131  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1132  if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1133  $tmpvar = 'options_'.$key;
1134  $tasks[$i]->array_options_project['options_'.$key] = $obj->$tmpvar;
1135  }
1136  }
1137  }
1138  }
1139 
1140  if ($loadextras) {
1141  $tasks[$i]->fetch_optionals();
1142  }
1143 
1144  $tasks[$i]->obj = $obj; // Needed for tpl/extrafields_list_print
1145  }
1146 
1147  $i++;
1148  }
1149  $this->db->free($resql);
1150  } else {
1151  dol_print_error($this->db);
1152  }
1153 
1154  return $tasks;
1155  }
1156 
1167  public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1168  {
1169  $arrayroles = array();
1170 
1171  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".is_object($userp)." usert=".is_object($usert)." projectid=".$projectid." taskid=".$taskid);
1172 
1173  // We want role of user for a projet or role of user for a task. Both are not possible.
1174  if (empty($userp) && empty($usert)) {
1175  $this->error = "CallWithWrongParameters";
1176  return -1;
1177  }
1178  if (!empty($userp) && !empty($usert)) {
1179  $this->error = "CallWithWrongParameters";
1180  return -1;
1181  }
1182 
1183  /* Liste des taches et role sur les projets ou taches */
1184  $sql = "SELECT pt.rowid as pid, ec.element_id, ctc.code, ctc.source";
1185  if ($userp) {
1186  $sql .= " FROM ".MAIN_DB_PREFIX."projet as pt";
1187  }
1188  if ($usert && $filteronprojstatus > -1) {
1189  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1190  }
1191  if ($usert && $filteronprojstatus <= -1) {
1192  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1193  }
1194  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1195  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1196  $sql .= " WHERE pt.rowid = ec.element_id";
1197  if ($userp && $filteronprojstatus > -1) {
1198  $sql .= " AND pt.fk_statut = ".((int) $filteronprojstatus);
1199  }
1200  if ($usert && $filteronprojstatus > -1) {
1201  $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1202  }
1203  if ($userp) {
1204  $sql .= " AND ctc.element = 'project'";
1205  }
1206  if ($usert) {
1207  $sql .= " AND ctc.element = 'project_task'";
1208  }
1209  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1210  if ($userp) {
1211  $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1212  }
1213  if ($usert) {
1214  $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1215  }
1216  $sql .= " AND ec.statut = 4";
1217  $sql .= " AND ctc.source = 'internal'";
1218  if ($projectid) {
1219  if ($userp) {
1220  $sql .= " AND pt.rowid IN (".$this->db->sanitize($projectid).")";
1221  }
1222  if ($usert) {
1223  $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1224  }
1225  }
1226  if ($taskid) {
1227  if ($userp) {
1228  $sql .= " ERROR SHOULD NOT HAPPENS";
1229  }
1230  if ($usert) {
1231  $sql .= " AND pt.rowid = ".((int) $taskid);
1232  }
1233  }
1234  //print $sql;
1235 
1236  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1237  $resql = $this->db->query($sql);
1238  if ($resql) {
1239  $num = $this->db->num_rows($resql);
1240  $i = 0;
1241  while ($i < $num) {
1242  $obj = $this->db->fetch_object($resql);
1243  if (empty($arrayroles[$obj->pid])) {
1244  $arrayroles[$obj->pid] = $obj->code;
1245  } else {
1246  $arrayroles[$obj->pid] .= ','.$obj->code;
1247  }
1248  $i++;
1249  }
1250  $this->db->free($resql);
1251  } else {
1252  dol_print_error($this->db);
1253  }
1254 
1255  return $arrayroles;
1256  }
1257 
1258 
1265  public function getListContactId($source = 'internal')
1266  {
1267  $contactAlreadySelected = array();
1268  $tab = $this->liste_contact(-1, $source);
1269  //var_dump($tab);
1270  $num = count($tab);
1271  $i = 0;
1272  while ($i < $num) {
1273  if ($source == 'thirdparty') {
1274  $contactAlreadySelected[$i] = $tab[$i]['socid'];
1275  } else {
1276  $contactAlreadySelected[$i] = $tab[$i]['id'];
1277  }
1278  $i++;
1279  }
1280  return $contactAlreadySelected;
1281  }
1282 
1283 
1291  public function addTimeSpent($user, $notrigger = 0)
1292  {
1293  global $conf, $langs;
1294 
1295  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1296 
1297  $ret = 0;
1298  $now = dol_now();
1299 
1300  // Check parameters
1301  if (!is_object($user)) {
1302  dol_print_error('', "Method addTimeSpent was called with wrong parameter user");
1303  return -1;
1304  }
1305 
1306  // Clean parameters
1307  if (isset($this->timespent_note)) {
1308  $this->timespent_note = trim($this->timespent_note);
1309  }
1310  if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1311  $this->timespent_datehour = $this->timespent_date;
1312  }
1313 
1314  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1315  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1316  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1317 
1318  if ($this->timespent_date < $restrictBefore) {
1319  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1320  $this->errors[] = $this->error;
1321  return -1;
1322  }
1323  }
1324 
1325 
1326  $this->db->begin();
1327 
1328  $timespent = new TimeSpent($this->db);
1329  $timespent->fk_element = $this->id;
1330  $timespent->elementtype = 'task';
1331  $timespent->element_date = $this->timespent_date;
1332  $timespent->element_datehour = $this->timespent_datehour;
1333  $timespent->element_date_withhour = $this->timespent_withhour;
1334  $timespent->element_duration = $this->timespent_duration;
1335  $timespent->fk_user = $this->timespent_fk_user;
1336  $timespent->fk_product = $this->timespent_fk_product;
1337  $timespent->note = $this->timespent_note;
1338  $timespent->datec = $this->db->idate($now);
1339 
1340  $result = $timespent->create($user);
1341 
1342  if ($result > 0) {
1343  $ret = $result;
1344  $this->timespent_id = $result;
1345 
1346  if (!$notrigger) {
1347  // Call trigger
1348  $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1349  if ($result < 0) {
1350  $ret = -1;
1351  }
1352  // End call triggers
1353  }
1354  } else {
1355  $this->error = $this->db->lasterror();
1356  $ret = -1;
1357  }
1358 
1359  if ($ret > 0) {
1360  // Recalculate amount of time spent for task and update denormalized field
1361  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1362  $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).")";
1363  if (isset($this->progress)) {
1364  $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1365  }
1366  $sql .= " WHERE rowid = ".((int) $this->id);
1367 
1368  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1369  if (!$this->db->query($sql)) {
1370  $this->error = $this->db->lasterror();
1371  $ret = -2;
1372  }
1373 
1374  // Update hourly rate of this time spent entry
1375  $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1376  if (!empty($resql_thm_user)) {
1377  $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1378  $timespent->thm = $obj_thm_user->thm;
1379  }
1380  $res_update = $timespent->update($user);
1381 
1382  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1383  if ($res_update <= 0) {
1384  $this->error = $this->db->lasterror();
1385  $ret = -2;
1386  }
1387  }
1388 
1389  if ($ret > 0) {
1390  $this->db->commit();
1391  } else {
1392  $this->db->rollback();
1393  }
1394  return $ret;
1395  }
1396 
1403  public function fetchTimeSpentOnTask($morewherefilter = '')
1404  {
1405  global $langs;
1406 
1407  $arrayres = array();
1408 
1409  $sql = "SELECT";
1410  $sql .= " s.rowid as socid,";
1411  $sql .= " s.nom as thirdparty_name,";
1412  $sql .= " s.email as thirdparty_email,";
1413  $sql .= " ptt.rowid,";
1414  $sql .= " ptt.ref_ext,";
1415  $sql .= " ptt.fk_element as fk_task,";
1416  $sql .= " ptt.element_date as task_date,";
1417  $sql .= " ptt.element_datehour as task_datehour,";
1418  $sql .= " ptt.element_date_withhour as task_date_withhour,";
1419  $sql .= " ptt.element_duration as task_duration,";
1420  $sql .= " ptt.fk_user,";
1421  $sql .= " ptt.note,";
1422  $sql .= " ptt.thm,";
1423  $sql .= " pt.rowid as task_id,";
1424  $sql .= " pt.ref as task_ref,";
1425  $sql .= " pt.label as task_label,";
1426  $sql .= " p.rowid as project_id,";
1427  $sql .= " p.ref as project_ref,";
1428  $sql .= " p.title as project_label,";
1429  $sql .= " p.public as public";
1430  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1431  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1432  $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1433  $sql .= " AND ptt.elementtype = 'task'";
1434  $sql .= " AND pt.rowid = ".((int) $this->id);
1435  $sql .= " AND pt.entity IN (".getEntity('project').")";
1436  if ($morewherefilter) {
1437  $sql .= $morewherefilter;
1438  }
1439 
1440  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1441  $resql = $this->db->query($sql);
1442  if ($resql) {
1443  $num = $this->db->num_rows($resql);
1444 
1445  $i = 0;
1446  while ($i < $num) {
1447  $obj = $this->db->fetch_object($resql);
1448 
1449  $newobj = new stdClass();
1450 
1451  $newobj->socid = $obj->socid;
1452  $newobj->thirdparty_name = $obj->thirdparty_name;
1453  $newobj->thirdparty_email = $obj->thirdparty_email;
1454 
1455  $newobj->fk_project = $obj->project_id;
1456  $newobj->project_ref = $obj->project_ref;
1457  $newobj->project_label = $obj->project_label;
1458  $newobj->public = $obj->project_public;
1459 
1460  $newobj->fk_task = $obj->task_id;
1461  $newobj->task_ref = $obj->task_ref;
1462  $newobj->task_label = $obj->task_label;
1463 
1464  $newobj->timespent_line_id = $obj->rowid;
1465  $newobj->timespent_line_ref_ext = $obj->ref_ext;
1466  $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1467  $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1468  $newobj->timespent_line_withhour = $obj->task_date_withhour;
1469  $newobj->timespent_line_duration = $obj->task_duration;
1470  $newobj->timespent_line_fk_user = $obj->fk_user;
1471  $newobj->timespent_line_thm = $obj->thm; // hourly rate
1472  $newobj->timespent_line_note = $obj->note;
1473 
1474  $arrayres[] = $newobj;
1475 
1476  $i++;
1477  }
1478 
1479  $this->db->free($resql);
1480 
1481  $this->lines = $arrayres;
1482  return 1;
1483  } else {
1484  dol_print_error($this->db);
1485  $this->error = "Error ".$this->db->lasterror();
1486  return -1;
1487  }
1488  }
1489 
1490 
1498  public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1499  {
1500  if (is_object($userobj)) {
1501  $userid = $userobj->id;
1502  } else {
1503  $userid = $userobj; // old method
1504  }
1505 
1506  $id = $this->id;
1507  if (empty($id) && empty($userid)) {
1508  dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1509  return -1;
1510  }
1511 
1512  $result = array();
1513 
1514  $sql = "SELECT";
1515  $sql .= " MIN(t.element_datehour) as min_date,";
1516  $sql .= " MAX(t.element_datehour) as max_date,";
1517  $sql .= " SUM(t.element_duration) as total_duration,";
1518  $sql .= " SUM(t.element_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1519  $sql .= " COUNT(t.rowid) as nblines,";
1520  $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1521  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1522  $sql .= " WHERE t.elementtype='task'";
1523  if ($morewherefilter) {
1524  $sql .= $morewherefilter;
1525  }
1526  if ($id > 0) {
1527  $sql .= " AND t.fk_element = ".((int) $id);
1528  }
1529  if ($userid > 0) {
1530  $sql .= " AND t.fk_user = ".((int) $userid);
1531  }
1532 
1533  dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1534  $resql = $this->db->query($sql);
1535  if ($resql) {
1536  $obj = $this->db->fetch_object($resql);
1537 
1538  $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1539  $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1540  $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1541 
1542  $this->timespent_min_date = $this->db->jdate($obj->min_date);
1543  $this->timespent_max_date = $this->db->jdate($obj->max_date);
1544  $this->timespent_total_duration = $obj->total_duration;
1545  $this->timespent_total_amount = $obj->total_amount;
1546  $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1547  $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1548 
1549  $this->db->free($resql);
1550  } else {
1551  dol_print_error($this->db);
1552  }
1553  return $result;
1554  }
1555 
1564  public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1565  {
1566  global $langs;
1567 
1568  if (empty($id)) {
1569  $id = $this->id;
1570  }
1571 
1572  $result = array();
1573 
1574  $sql = "SELECT";
1575  $sql .= " SUM(t.element_duration) as nbseconds,";
1576  $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";
1577  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as t";
1578  $sql .= " WHERE t.elementtype='task' AND t.fk_element = ".((int) $id);
1579  if (is_object($fuser) && $fuser->id > 0) {
1580  $sql .= " AND fk_user = ".((int) $fuser->id);
1581  }
1582  if ($dates > 0) {
1583  $datefieldname = "element_datehour";
1584  $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1585  }
1586  if ($datee > 0) {
1587  $datefieldname = "element_datehour";
1588  $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1589  }
1590  //print $sql;
1591 
1592  dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1593  $resql = $this->db->query($sql);
1594  if ($resql) {
1595  $obj = $this->db->fetch_object($resql);
1596 
1597  $result['amount'] = $obj->amount;
1598  $result['nbseconds'] = $obj->nbseconds;
1599  $result['nblinesnull'] = $obj->nblinesnull;
1600 
1601  $this->db->free($resql);
1602  return $result;
1603  } else {
1604  dol_print_error($this->db);
1605  return $result;
1606  }
1607  }
1608 
1615  public function fetchTimeSpent($id)
1616  {
1617  global $langs;
1618 
1619  $timespent = new TimeSpent($this->db);
1620  $timespent->fetch($id);
1621 
1622  dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1623 
1624  if ($timespent->id > 0) {
1625  $this->timespent_id = $timespent->id;
1626  $this->id = $timespent->fk_element;
1627  $this->timespent_date = $timespent->element_date;
1628  $this->timespent_datehour = $timespent->element_datehour;
1629  $this->timespent_withhour = $timespent->element_date_withhour;
1630  $this->timespent_duration = $timespent->element_duration;
1631  $this->timespent_fk_user = $timespent->fk_user;
1632  $this->timespent_fk_product = $timespent->fk_product;
1633  $this->timespent_thm = $timespent->thm; // hourly rate
1634  $this->timespent_note = $timespent->note;
1635 
1636  return 1;
1637  }
1638 
1639  return 0;
1640  }
1641 
1649  public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1650  {
1651  $arrayres = array();
1652 
1653  $sql = "SELECT";
1654  $sql .= " s.rowid as socid,";
1655  $sql .= " s.nom as thirdparty_name,";
1656  $sql .= " s.email as thirdparty_email,";
1657  $sql .= " ptt.rowid,";
1658  $sql .= " ptt.fk_element as fk_task,";
1659  $sql .= " ptt.element_date as task_date,";
1660  $sql .= " ptt.element_datehour as task_datehour,";
1661  $sql .= " ptt.element_date_withhour as task_date_withhour,";
1662  $sql .= " ptt.element_duration as task_duration,";
1663  $sql .= " ptt.fk_user,";
1664  $sql .= " ptt.note,";
1665  $sql .= " ptt.thm,";
1666  $sql .= " pt.rowid as task_id,";
1667  $sql .= " pt.ref as task_ref,";
1668  $sql .= " pt.label as task_label,";
1669  $sql .= " p.rowid as project_id,";
1670  $sql .= " p.ref as project_ref,";
1671  $sql .= " p.title as project_label,";
1672  $sql .= " p.public as public";
1673  $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1674  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1675  $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1676  $sql .= " AND ptt.elementtype = 'task'";
1677  $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1678  $sql .= " AND pt.entity IN (".getEntity('project').")";
1679  if ($morewherefilter) {
1680  $sql .= $morewherefilter;
1681  }
1682 
1683  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1684  $resql = $this->db->query($sql);
1685  if ($resql) {
1686  $num = $this->db->num_rows($resql);
1687 
1688  $i = 0;
1689  while ($i < $num) {
1690  $obj = $this->db->fetch_object($resql);
1691 
1692  $newobj = new stdClass();
1693 
1694  $newobj->socid = $obj->socid;
1695  $newobj->thirdparty_name = $obj->thirdparty_name;
1696  $newobj->thirdparty_email = $obj->thirdparty_email;
1697 
1698  $newobj->fk_project = $obj->project_id;
1699  $newobj->project_ref = $obj->project_ref;
1700  $newobj->project_label = $obj->project_label;
1701  $newobj->public = $obj->project_public;
1702 
1703  $newobj->fk_task = $obj->task_id;
1704  $newobj->task_ref = $obj->task_ref;
1705  $newobj->task_label = $obj->task_label;
1706 
1707  $newobj->timespent_id = $obj->rowid;
1708  $newobj->timespent_date = $this->db->jdate($obj->task_date);
1709  $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1710  $newobj->timespent_withhour = $obj->task_date_withhour;
1711  $newobj->timespent_duration = $obj->task_duration;
1712  $newobj->timespent_fk_user = $obj->fk_user;
1713  $newobj->timespent_thm = $obj->thm; // hourly rate
1714  $newobj->timespent_note = $obj->note;
1715 
1716  $arrayres[] = $newobj;
1717 
1718  $i++;
1719  }
1720 
1721  $this->db->free($resql);
1722  } else {
1723  dol_print_error($this->db);
1724  $this->error = "Error ".$this->db->lasterror();
1725  return -1;
1726  }
1727 
1728  return $arrayres;
1729  }
1730 
1738  public function updateTimeSpent($user, $notrigger = 0)
1739  {
1740  global $conf, $langs;
1741 
1742  $ret = 0;
1743 
1744  // Check parameters
1745  if ($this->timespent_date == '') {
1746  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1747  return -1;
1748  }
1749  if (!($this->timespent_fk_user > 0)) {
1750  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1751  return -1;
1752  }
1753 
1754  // Clean parameters
1755  if (empty($this->timespent_datehour)) {
1756  $this->timespent_datehour = $this->timespent_date;
1757  }
1758  if (isset($this->timespent_note)) {
1759  $this->timespent_note = trim($this->timespent_note);
1760  }
1761 
1762  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1763  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1764  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1765 
1766  if ($this->timespent_date < $restrictBefore) {
1767  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1768  $this->errors[] = $this->error;
1769  return -1;
1770  }
1771  }
1772 
1773  $this->db->begin();
1774 
1775  $timespent = new TimeSpent($this->db);
1776  $timespent->fetch($this->timespent_id);
1777  $timespent->element_date = $this->timespent_date;
1778  $timespent->element_datehour = $this->timespent_datehour;
1779  $timespent->element_date_withhour = $this->timespent_withhour;
1780  $timespent->element_duration = $this->timespent_duration;
1781  $timespent->fk_user = $this->timespent_fk_user;
1782  $timespent->fk_product = $this->timespent_fk_product;
1783  $timespent->note = $this->timespent_note;
1784  $timespent->invoice_id = $this->timespent_invoiceid;
1785  $timespent->invoice_line_id = $this->timespent_invoicelineid;
1786 
1787  dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1788  if ($timespent->update($user) > 0) {
1789  if (!$notrigger) {
1790  // Call trigger
1791  $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1792  if ($result < 0) {
1793  $this->db->rollback();
1794  $ret = -1;
1795  } else {
1796  $ret = 1;
1797  }
1798  // End call triggers
1799  } else {
1800  $ret = 1;
1801  }
1802  } else {
1803  $this->error = $this->db->lasterror();
1804  $this->db->rollback();
1805  $ret = -1;
1806  }
1807 
1808  if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM))) {
1809  if ($this->timespent_old_duration != $this->timespent_duration) {
1810  // Recalculate amount of time spent for task and update denormalized field
1811  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1812  $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) . ")";
1813  if (isset($this->progress)) {
1814  $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1815  }
1816  $sql .= " WHERE rowid = " . ((int) $this->id);
1817 
1818  dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1819  if (!$this->db->query($sql)) {
1820  $this->error = $this->db->lasterror();
1821  $this->db->rollback();
1822  $ret = -2;
1823  }
1824  }
1825 
1826  // Update hourly rate of this time spent entry, but only if it was not set initialy
1827  $res_update = 1;
1828  if (empty($timespent->thm) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM)) {
1829  $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1830  if (!empty($resql_thm_user)) {
1831  $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1832  $timespent->thm = $obj_thm_user->thm;
1833  }
1834  $res_update = $timespent->update($user);
1835  }
1836 
1837  dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1838  if ($res_update <= 0) {
1839  $this->error = $this->db->lasterror();
1840  $ret = -2;
1841  }
1842  }
1843 
1844  if ($ret >= 0) {
1845  $this->db->commit();
1846  }
1847  return $ret;
1848  }
1849 
1857  public function delTimeSpent($user, $notrigger = 0)
1858  {
1859  global $conf, $langs;
1860 
1861  $error = 0;
1862 
1863  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1864  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1865  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1866 
1867  if ($this->timespent_date < $restrictBefore) {
1868  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1869  $this->errors[] = $this->error;
1870  return -1;
1871  }
1872  }
1873 
1874  $this->db->begin();
1875 
1876  if (!$notrigger) {
1877  // Call trigger
1878  $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
1879  if ($result < 0) {
1880  $error++;
1881  }
1882  // End call triggers
1883  }
1884 
1885  if (!$error) {
1886  $timespent = new TimeSpent($this->db);
1887  $timespent->fetch($this->timespent_id);
1888 
1889  $res_del = $timespent->delete($user);
1890 
1891  if ($res_del < 0) {
1892  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1893  }
1894  }
1895 
1896  if (!$error) {
1897  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1898  $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
1899  $sql .= " WHERE rowid = ".((int) $this->id);
1900 
1901  dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1902  if ($this->db->query($sql)) {
1903  $result = 0;
1904  } else {
1905  $this->error = $this->db->lasterror();
1906  $result = -2;
1907  }
1908  }
1909 
1910  // Commit or rollback
1911  if ($error) {
1912  foreach ($this->errors as $errmsg) {
1913  dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
1914  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1915  }
1916  $this->db->rollback();
1917  return -1 * $error;
1918  } else {
1919  $this->db->commit();
1920  return 1;
1921  }
1922  }
1923 
1938  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)
1939  {
1940  global $langs, $conf;
1941 
1942  $error = 0;
1943 
1944  //Use 00:00 of today if time is use on task.
1945  $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
1946 
1947  $datec = $now;
1948 
1949  $clone_task = new Task($this->db);
1950  $origin_task = new Task($this->db);
1951 
1952  $clone_task->context['createfromclone'] = 'createfromclone';
1953 
1954  $this->db->begin();
1955 
1956  // Load source object
1957  $clone_task->fetch($fromid);
1958  $clone_task->fetch_optionals();
1959  //var_dump($clone_task->array_options);exit;
1960 
1961  $origin_task->fetch($fromid);
1962 
1963  $defaultref = '';
1964  $obj = empty($conf->global->PROJECT_TASK_ADDON) ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
1965  if (!empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.".php")) {
1966  require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.'.php';
1967  $modTask = new $obj;
1968  $defaultref = $modTask->getNextValue(0, $clone_task);
1969  }
1970 
1971  $ori_project_id = $clone_task->fk_project;
1972 
1973  $clone_task->id = 0;
1974  $clone_task->ref = $defaultref;
1975  $clone_task->fk_project = $project_id;
1976  $clone_task->fk_task_parent = $parent_task_id;
1977  $clone_task->date_c = $datec;
1978  $clone_task->planned_workload = $origin_task->planned_workload;
1979  $clone_task->rang = $origin_task->rang;
1980  $clone_task->priority = $origin_task->priority;
1981 
1982  //Manage Task Date
1983  if ($clone_change_dt) {
1984  $projectstatic = new Project($this->db);
1985  $projectstatic->fetch($ori_project_id);
1986 
1987  //Origin project strat date
1988  $orign_project_dt_start = $projectstatic->date_start;
1989 
1990  //Calcultate new task start date with difference between origin proj start date and origin task start date
1991  if (!empty($clone_task->date_start)) {
1992  $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
1993  }
1994 
1995  //Calcultate new task end date with difference between origin proj end date and origin task end date
1996  if (!empty($clone_task->date_end)) {
1997  $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
1998  }
1999  }
2000 
2001  if (!$clone_prog) {
2002  $clone_task->progress = 0;
2003  }
2004 
2005  // Create clone
2006  $result = $clone_task->create($user);
2007 
2008  // Other options
2009  if ($result < 0) {
2010  $this->error = $clone_task->error;
2011  $error++;
2012  }
2013 
2014  // End
2015  if (!$error) {
2016  $clone_task_id = $clone_task->id;
2017  $clone_task_ref = $clone_task->ref;
2018 
2019  //Note Update
2020  if (!$clone_note) {
2021  $clone_task->note_private = '';
2022  $clone_task->note_public = '';
2023  } else {
2024  $this->db->begin();
2025  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
2026  if ($res < 0) {
2027  $this->error .= $clone_task->error;
2028  $error++;
2029  $this->db->rollback();
2030  } else {
2031  $this->db->commit();
2032  }
2033 
2034  $this->db->begin();
2035  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
2036  if ($res < 0) {
2037  $this->error .= $clone_task->error;
2038  $error++;
2039  $this->db->rollback();
2040  } else {
2041  $this->db->commit();
2042  }
2043  }
2044 
2045  //Duplicate file
2046  if ($clone_file) {
2047  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2048 
2049  //retrieve project origin ref to know folder to copy
2050  $projectstatic = new Project($this->db);
2051  $projectstatic->fetch($ori_project_id);
2052  $ori_project_ref = $projectstatic->ref;
2053 
2054  if ($ori_project_id != $project_id) {
2055  $projectstatic->fetch($project_id);
2056  $clone_project_ref = $projectstatic->ref;
2057  } else {
2058  $clone_project_ref = $ori_project_ref;
2059  }
2060 
2061  $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
2062  $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
2063 
2064  $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
2065  foreach ($filearray as $key => $file) {
2066  if (!file_exists($clone_task_dir)) {
2067  if (dol_mkdir($clone_task_dir) < 0) {
2068  $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
2069  $error++;
2070  }
2071  }
2072 
2073  $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
2074  if (is_numeric($rescopy) && $rescopy < 0) {
2075  $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
2076  $error++;
2077  }
2078  }
2079  }
2080 
2081  // clone affectation
2082  if ($clone_affectation) {
2083  $origin_task = new Task($this->db);
2084  $origin_task->fetch($fromid);
2085 
2086  foreach (array('internal', 'external') as $source) {
2087  $tab = $origin_task->liste_contact(-1, $source);
2088  $num = count($tab);
2089  $i = 0;
2090  while ($i < $num) {
2091  $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2092  if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2093  $langs->load("errors");
2094  $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2095  $error++;
2096  } else {
2097  if ($clone_task->error != '') {
2098  $this->error .= $clone_task->error;
2099  $error++;
2100  }
2101  }
2102  $i++;
2103  }
2104  }
2105  }
2106 
2107  if ($clone_time) {
2108  //TODO clone time of affectation
2109  }
2110  }
2111 
2112  unset($clone_task->context['createfromclone']);
2113 
2114  if (!$error) {
2115  $this->db->commit();
2116  return $clone_task_id;
2117  } else {
2118  $this->db->rollback();
2119  dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
2120  return -1;
2121  }
2122  }
2123 
2124 
2131  public function getLibStatut($mode = 0)
2132  {
2133  return $this->LibStatut($this->fk_statut, $mode);
2134  }
2135 
2136  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2144  public function LibStatut($status, $mode = 0)
2145  {
2146  // phpcs:enable
2147  global $langs;
2148 
2149  // list of Statut of the task
2150  $this->statuts[0] = 'Draft';
2151  $this->statuts[1] = 'ToDo';
2152  $this->statuts[2] = 'Running';
2153  $this->statuts[3] = 'Finish';
2154  $this->statuts[4] = 'Transfered';
2155  $this->statuts_short[0] = 'Draft';
2156  $this->statuts_short[1] = 'ToDo';
2157  $this->statuts_short[2] = 'Running';
2158  $this->statuts_short[3] = 'Completed';
2159  $this->statuts_short[4] = 'Transfered';
2160 
2161  if ($mode == 0) {
2162  return $langs->trans($this->statuts[$status]);
2163  } elseif ($mode == 1) {
2164  return $langs->trans($this->statuts_short[$status]);
2165  } elseif ($mode == 2) {
2166  if ($status == 0) {
2167  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts_short[$status]);
2168  } elseif ($status == 1) {
2169  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts_short[$status]);
2170  } elseif ($status == 2) {
2171  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts_short[$status]);
2172  } elseif ($status == 3) {
2173  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
2174  } elseif ($status == 4) {
2175  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
2176  } elseif ($status == 5) {
2177  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts_short[$status]);
2178  }
2179  } elseif ($mode == 3) {
2180  if ($status == 0) {
2181  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0');
2182  } elseif ($status == 1) {
2183  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1');
2184  } elseif ($status == 2) {
2185  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3');
2186  } elseif ($status == 3) {
2187  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
2188  } elseif ($status == 4) {
2189  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
2190  } elseif ($status == 5) {
2191  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5');
2192  }
2193  } elseif ($mode == 4) {
2194  if ($status == 0) {
2195  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts[$status]);
2196  } elseif ($status == 1) {
2197  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts[$status]);
2198  } elseif ($status == 2) {
2199  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts[$status]);
2200  } elseif ($status == 3) {
2201  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
2202  } elseif ($status == 4) {
2203  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
2204  } elseif ($status == 5) {
2205  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts[$status]);
2206  }
2207  } elseif ($mode == 5) {
2208  /*if ($status==0) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
2209  elseif ($status==1) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
2210  elseif ($status==2) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
2211  elseif ($status==3) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2212  elseif ($status==4) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2213  elseif ($status==5) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
2214  */
2215  //else return $this->progress.' %';
2216  return '&nbsp;';
2217  } elseif ($mode == 6) {
2218  /*if ($status==0) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
2219  elseif ($status==1) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
2220  elseif ($status==2) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
2221  elseif ($status==3) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2222  elseif ($status==4) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2223  elseif ($status==5) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
2224  */
2225  //else return $this->progress.' %';
2226  return '&nbsp;';
2227  }
2228  return "";
2229  }
2230 
2241  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2242  {
2243  global $conf;
2244 
2245  $outputlangs->load("projects");
2246 
2247  if (!dol_strlen($modele)) {
2248  $modele = 'nodefault';
2249 
2250  if (!empty($this->model_pdf)) {
2251  $modele = $this->model_pdf;
2252  } elseif (!empty($this->modelpdf)) { // deprecated
2253  $modele = $this->modelpdf;
2254  } elseif (!empty($conf->global->PROJECT_TASK_ADDON_PDF)) {
2255  $modele = $conf->global->PROJECT_TASK_ADDON_PDF;
2256  }
2257  }
2258 
2259  $modelpath = "core/modules/project/task/doc/";
2260 
2261  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2262  }
2263 
2264 
2265  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2272  public function load_board($user)
2273  {
2274  // phpcs:enable
2275  global $conf, $langs;
2276 
2277  // For external user, no check is done on company because readability is managed by public status of project and assignement.
2278  //$socid = $user->socid;
2279  $socid = 0;
2280 
2281  $projectstatic = new Project($this->db);
2282  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2283 
2284  // List of tasks (does not care about permissions. Filtering will be done later)
2285  $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2286  $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2287  $sql .= " t.dateo as date_start, t.datee as datee";
2288  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2289  //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2290  //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2291  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2292  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2293  $sql .= " AND p.fk_statut = 1";
2294  $sql .= " AND t.fk_projet = p.rowid";
2295  $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2296  if (empty($user->rights->projet->all->lire)) {
2297  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2298  }
2299  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2300  //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).")";
2301  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2302  // 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))";
2303 
2304  //print $sql;
2305  $resql = $this->db->query($sql);
2306  if ($resql) {
2307  $task_static = new Task($this->db);
2308 
2309  $response = new WorkboardResponse();
2310  $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2311  $response->label = $langs->trans("OpenedTasks");
2312  if ($user->hasRight("projet", "all", "lire")) {
2313  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2314  } else {
2315  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2316  }
2317  $response->img = img_object('', "task");
2318 
2319  // This assignment in condition is not a bug. It allows walking the results.
2320  while ($obj = $this->db->fetch_object($resql)) {
2321  $response->nbtodo++;
2322 
2323  $task_static->projectstatus = $obj->projectstatus;
2324  $task_static->progress = $obj->progress;
2325  $task_static->fk_statut = $obj->status;
2326  $task_static->date_end = $this->db->jdate($obj->datee);
2327 
2328  if ($task_static->hasDelay()) {
2329  $response->nbtodolate++;
2330  }
2331  }
2332 
2333  return $response;
2334  } else {
2335  $this->error = $this->db->error();
2336  return -1;
2337  }
2338  }
2339 
2340 
2341  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2347  public function load_state_board()
2348  {
2349  // phpcs:enable
2350  global $user;
2351 
2352  $mine = 0; $socid = $user->socid;
2353 
2354  $projectstatic = new Project($this->db);
2355  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2356 
2357  // List of tasks (does not care about permissions. Filtering will be done later)
2358  $sql = "SELECT count(p.rowid) as nb";
2359  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2360  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2361  if (empty($user->rights->societe->client->voir) && !$socid) {
2362  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2363  }
2364  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2365  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2366  $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2367  if ($mine || empty($user->rights->projet->all->lire)) {
2368  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2369  }
2370  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2371  //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).")";
2372  if ($socid) {
2373  $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2374  }
2375  if (empty($user->rights->societe->client->voir) && !$socid) {
2376  $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2377  }
2378 
2379  $resql = $this->db->query($sql);
2380  if ($resql) {
2381  // This assignment in condition is not a bug. It allows walking the results.
2382  while ($obj = $this->db->fetch_object($resql)) {
2383  $this->nb["tasks"] = $obj->nb;
2384  }
2385  $this->db->free($resql);
2386  return 1;
2387  } else {
2388  dol_print_error($this->db);
2389  $this->error = $this->db->error();
2390  return -1;
2391  }
2392  }
2393 
2399  public function hasDelay()
2400  {
2401  global $conf;
2402 
2403  if (!($this->progress >= 0 && $this->progress < 100)) {
2404  return false;
2405  }
2406 
2407  $now = dol_now();
2408 
2409  $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
2410 
2411  return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2412  }
2413 
2421  public function getKanbanView($option = '', $arraydata = null)
2422  {
2423  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2424 
2425  $return = '<div class="box-flex-item box-flex-grow-zero">';
2426  $return .= '<div class="info-box info-box-sm info-box-kanban">';
2427  $return .= '<span class="info-box-icon bg-infobox-action">';
2428  $return .= img_picto('', $this->picto);
2429  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2430  $return .= '</span>';
2431  $return .= '<div class="info-box-content">';
2432  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2433  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2434  if (!empty($arraydata['projectlink'])) {
2435  //$tmpproject = $arraydata['project'];
2436  //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
2437  $return .= '<br><span class="info-box-status ">'.$arraydata['projectlink'].'</span>';
2438  }
2439  if (property_exists($this, 'budget_amount')) {
2440  //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
2441  }
2442  if (property_exists($this, 'duration_effective')) {
2443  $return .= '<br><br><div class="info-box-label progressinkanban">'.getTaskProgressView($this, false, true).'</div>';
2444  }
2445  $return .= '</div>';
2446  $return .= '</div>';
2447  $return .= '</div>';
2448  return $return;
2449  }
2450 }
$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...
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).
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
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:40
create($user, $notrigger=0)
Create into database.
Definition: task.class.php:200
fetch($id, $ref='', $loadparentdata=0)
Load object in memory from database.
Definition: task.class.php:314
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:187
fetchTimeSpentOnTask($morewherefilter='')
Fetch records of time spent of this task.
hasTimeSpent()
Return nb of time spent.
Definition: task.class.php:724
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:794
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.
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:906
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:416
getSummaryOfTimeSpent($userobj=null, $morewherefilter='')
Calculate total of time spent for task.
getTooltipContentArray($params)
getTooltipContentArray
Definition: task.class.php:761
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:867
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:691
Class for TimeSpent.
Class to manage Dolibarr users.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('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') && $user->hasRight('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)) $sql
Social contributions to pay.
Definition: index.php:746
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:122
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:1507
dol_move_dir($srcdir, $destdir, $overwriteifexists=1, $indexdatabase=1, $renamedircontent=1)
Move a directory into another name.
Definition: files.lib.php:1105
dol_copy($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
Definition: files.lib.php:717
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:62
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.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
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:926