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  $this->note_public = trim($this->note_public);
178  $this->note_private = trim($this->note_private);
179 
180  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
181  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
182  return -1;
183  }
184 
185  // Check parameters
186  // Put here code to add control on parameters values
187 
188  // Insert request
189  $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task (";
190  $sql .= "entity";
191  $sql .= ", fk_projet";
192  $sql .= ", ref";
193  $sql .= ", fk_task_parent";
194  $sql .= ", label";
195  $sql .= ", description";
196  $sql .= ", note_public";
197  $sql .= ", note_private";
198  $sql .= ", datec";
199  $sql .= ", fk_user_creat";
200  $sql .= ", dateo";
201  $sql .= ", datee";
202  $sql .= ", planned_workload";
203  $sql .= ", progress";
204  $sql .= ", budget_amount";
205  $sql .= ", priority";
206  $sql .= ") VALUES (";
207  $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
208  $sql .= ", ".((int) $this->fk_project);
209  $sql .= ", ".(!empty($this->ref) ? "'".$this->db->escape($this->ref)."'" : 'null');
210  $sql .= ", ".((int) $this->fk_task_parent);
211  $sql .= ", '".$this->db->escape($this->label)."'";
212  $sql .= ", '".$this->db->escape($this->description)."'";
213  $sql .= ", '".$this->db->escape($this->note_public)."'";
214  $sql .= ", '".$this->db->escape($this->note_private)."'";
215  $sql .= ", '".$this->db->idate($now)."'";
216  $sql .= ", ".((int) $user->id);
217  $sql .= ", ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : 'null');
218  $sql .= ", ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : 'null');
219  $sql .= ", ".(($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
220  $sql .= ", ".(($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
221  $sql .= ", ".(($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
222  $sql .= ", ".(($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null');
223  $sql .= ")";
224 
225  $this->db->begin();
226 
227  dol_syslog(get_class($this)."::create", LOG_DEBUG);
228  $resql = $this->db->query($sql);
229  if (!$resql) {
230  $error++; $this->errors[] = "Error ".$this->db->lasterror();
231  }
232 
233  if (!$error) {
234  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task");
235 
236  if (!$notrigger) {
237  // Call trigger
238  $result = $this->call_trigger('TASK_CREATE', $user);
239  if ($result < 0) {
240  $error++;
241  }
242  // End call triggers
243  }
244  }
245 
246  // Update extrafield
247  if (!$error) {
248  if (!$error) {
249  $result = $this->insertExtraFields();
250  if ($result < 0) {
251  $error++;
252  }
253  }
254  }
255 
256  // Commit or rollback
257  if ($error) {
258  foreach ($this->errors as $errmsg) {
259  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
260  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
261  }
262  $this->db->rollback();
263  return -1 * $error;
264  } else {
265  $this->db->commit();
266  return $this->id;
267  }
268  }
269 
270 
279  public function fetch($id, $ref = '', $loadparentdata = 0)
280  {
281  global $langs, $conf;
282 
283  $sql = "SELECT";
284  $sql .= " t.rowid,";
285  $sql .= " t.ref,";
286  $sql .= " t.entity,";
287  $sql .= " t.fk_projet as fk_project,";
288  $sql .= " t.fk_task_parent,";
289  $sql .= " t.label,";
290  $sql .= " t.description,";
291  $sql .= " t.duration_effective,";
292  $sql .= " t.planned_workload,";
293  $sql .= " t.datec,";
294  $sql .= " t.dateo,";
295  $sql .= " t.datee,";
296  $sql .= " t.fk_user_creat,";
297  $sql .= " t.fk_user_valid,";
298  $sql .= " t.fk_statut,";
299  $sql .= " t.progress,";
300  $sql .= " t.budget_amount,";
301  $sql .= " t.priority,";
302  $sql .= " t.note_private,";
303  $sql .= " t.note_public,";
304  $sql .= " t.rang";
305  if (!empty($loadparentdata)) {
306  $sql .= ", t2.ref as task_parent_ref";
307  $sql .= ", t2.rang as task_parent_position";
308  }
309  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as t";
310  if (!empty($loadparentdata)) {
311  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t2 ON t.fk_task_parent = t2.rowid";
312  }
313  $sql .= " WHERE ";
314  if (!empty($ref)) {
315  $sql .= "entity IN (".getEntity('project').")";
316  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
317  } else {
318  $sql .= "t.rowid = ".((int) $id);
319  }
320 
321  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
322  $resql = $this->db->query($sql);
323  if ($resql) {
324  $num_rows = $this->db->num_rows($resql);
325 
326  if ($num_rows) {
327  $obj = $this->db->fetch_object($resql);
328 
329  $this->id = $obj->rowid;
330  $this->ref = $obj->ref;
331  $this->entity = $obj->entity;
332  $this->fk_project = $obj->fk_project;
333  $this->fk_task_parent = $obj->fk_task_parent;
334  $this->label = $obj->label;
335  $this->description = $obj->description;
336  $this->duration_effective = $obj->duration_effective;
337  $this->planned_workload = $obj->planned_workload;
338  $this->date_c = $this->db->jdate($obj->datec);
339  $this->date_start = $this->db->jdate($obj->dateo);
340  $this->date_end = $this->db->jdate($obj->datee);
341  $this->fk_user_creat = $obj->fk_user_creat;
342  $this->fk_user_valid = $obj->fk_user_valid;
343  $this->fk_statut = $obj->fk_statut;
344  $this->progress = $obj->progress;
345  $this->budget_amount = $obj->budget_amount;
346  $this->priority = $obj->priority;
347  $this->note_private = $obj->note_private;
348  $this->note_public = $obj->note_public;
349  $this->rang = $obj->rang;
350 
351  if (!empty($loadparentdata)) {
352  $this->task_parent_ref = $obj->task_parent_ref;
353  $this->task_parent_position = $obj->task_parent_position;
354  }
355 
356  // Retrieve all extrafield
357  $this->fetch_optionals();
358  }
359 
360  $this->db->free($resql);
361 
362  if ($num_rows) {
363  return 1;
364  } else {
365  return 0;
366  }
367  } else {
368  $this->error = "Error ".$this->db->lasterror();
369  return -1;
370  }
371  }
372 
373 
381  public function update($user = null, $notrigger = 0)
382  {
383  global $conf, $langs;
384  $error = 0;
385 
386  // Clean parameters
387  if (isset($this->fk_project)) {
388  $this->fk_project = trim($this->fk_project);
389  }
390  if (isset($this->ref)) {
391  $this->ref = trim($this->ref);
392  }
393  if (isset($this->fk_task_parent)) {
394  $this->fk_task_parent = (int) $this->fk_task_parent;
395  }
396  if (isset($this->label)) {
397  $this->label = trim($this->label);
398  }
399  if (isset($this->description)) {
400  $this->description = trim($this->description);
401  }
402  if (isset($this->note_public)) {
403  $this->note_public = trim($this->note_public);
404  }
405  if (isset($this->note_private)) {
406  $this->note_private = trim($this->note_private);
407  }
408  if (isset($this->duration_effective)) {
409  $this->duration_effective = trim($this->duration_effective);
410  }
411  if (isset($this->planned_workload)) {
412  $this->planned_workload = trim($this->planned_workload);
413  }
414  if (isset($this->budget_amount)) {
415  $this->budget_amount = trim($this->budget_amount);
416  }
417 
418  if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
419  $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
420  return -1;
421  }
422 
423  // Check parameters
424  // Put here code to add control on parameters values
425 
426  // Update request
427  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET";
428  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
429  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "'".$this->db->escape($this->id)."'").",";
430  $sql .= " fk_task_parent=".(isset($this->fk_task_parent) ? $this->fk_task_parent : "null").",";
431  $sql .= " label=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
432  $sql .= " description=".(isset($this->description) ? "'".$this->db->escape($this->description)."'" : "null").",";
433  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
434  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
435  $sql .= " duration_effective=".(isset($this->duration_effective) ? $this->duration_effective : "null").",";
436  $sql .= " planned_workload=".((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null").",";
437  $sql .= " dateo=".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null').",";
438  $sql .= " datee=".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null').",";
439  $sql .= " progress=".(($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null').",";
440  $sql .= " budget_amount=".(($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null').",";
441  $sql .= " rang=".((!empty($this->rang)) ? ((int) $this->rang) : "0").",";
442  $sql .= " priority=".((!empty($this->priority)) ? ((int) $this->priority) : "0");
443  $sql .= " WHERE rowid=".((int) $this->id);
444 
445  $this->db->begin();
446 
447  dol_syslog(get_class($this)."::update", LOG_DEBUG);
448  $resql = $this->db->query($sql);
449  if (!$resql) {
450  $error++; $this->errors[] = "Error ".$this->db->lasterror();
451  }
452 
453  // Update extrafield
454  if (!$error) {
455  $result = $this->insertExtraFields();
456  if ($result < 0) {
457  $error++;
458  }
459  }
460 
461  if (!$error && !empty($conf->global->PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE)) {
462  // Close the parent project if it is open (validated) and its tasks are 100% completed
463  $project = new Project($this->db);
464  if ($project->fetch($this->fk_project) > 0) {
465  if ($project->statut == Project::STATUS_VALIDATED) {
466  $project->getLinesArray(null); // this method does not return <= 0 if fails
467  $projectCompleted = array_reduce(
468  $project->lines,
469  function ($allTasksCompleted, $task) {
470  return $allTasksCompleted && $task->progress >= 100;
471  },
472  1
473  );
474  if ($projectCompleted) {
475  if ($project->setClose($user) <= 0) {
476  $error++;
477  }
478  }
479  }
480  } else {
481  $error++;
482  }
483  if ($error) {
484  $this->errors[] = $project->error;
485  }
486  }
487 
488  if (!$error) {
489  if (!$notrigger) {
490  // Call trigger
491  $result = $this->call_trigger('TASK_MODIFY', $user);
492  if ($result < 0) {
493  $error++;
494  }
495  // End call triggers
496  }
497  }
498 
499  if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
500  // We remove directory
501  if ($conf->project->dir_output) {
502  $project = new Project($this->db);
503  $project->fetch($this->fk_project);
504 
505  $olddir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->oldcopy->ref);
506  $newdir = $conf->project->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($this->ref);
507  if (file_exists($olddir)) {
508  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
509  $res = dol_move_dir($olddir, $newdir);
510  if (!$res) {
511  $langs->load("errors");
512  $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
513  $error++;
514  }
515  }
516  }
517  }
518 
519  // Commit or rollback
520  if ($error) {
521  foreach ($this->errors as $errmsg) {
522  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
523  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
524  }
525  $this->db->rollback();
526  return -1 * $error;
527  } else {
528  $this->db->commit();
529  return 1;
530  }
531  }
532 
533 
541  public function delete($user, $notrigger = 0)
542  {
543 
544  global $conf, $langs;
545  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
546 
547  $error = 0;
548 
549  $this->db->begin();
550 
551  if ($this->hasChildren() > 0) {
552  dol_syslog(get_class($this)."::delete Can't delete record as it has some sub tasks", LOG_WARNING);
553  $this->error = 'ErrorRecordHasSubTasks';
554  $this->db->rollback();
555  return 0;
556  }
557 
558  $objectisused = $this->isObjectUsed($this->id);
559  if (!empty($objectisused)) {
560  dol_syslog(get_class($this)."::delete Can't delete record as it has some child", LOG_WARNING);
561  $this->error = 'ErrorRecordHasChildren';
562  $this->db->rollback();
563  return 0;
564  }
565 
566  if (!$error) {
567  // Delete linked contacts
568  $res = $this->delete_linked_contact();
569  if ($res < 0) {
570  $this->error = 'ErrorFailToDeleteLinkedContact';
571  //$error++;
572  $this->db->rollback();
573  return 0;
574  }
575  }
576 
577  if (!$error) {
578  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
579  $sql .= " WHERE fk_task = ".((int) $this->id);
580 
581  $resql = $this->db->query($sql);
582  if (!$resql) {
583  $error++; $this->errors[] = "Error ".$this->db->lasterror();
584  }
585  }
586 
587  if (!$error) {
588  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_extrafields";
589  $sql .= " WHERE fk_object = ".((int) $this->id);
590 
591  $resql = $this->db->query($sql);
592  if (!$resql) {
593  $error++; $this->errors[] = "Error ".$this->db->lasterror();
594  }
595  }
596 
597  if (!$error) {
598  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task";
599  $sql .= " WHERE rowid=".((int) $this->id);
600 
601  $resql = $this->db->query($sql);
602  if (!$resql) {
603  $error++; $this->errors[] = "Error ".$this->db->lasterror();
604  }
605  }
606 
607  if (!$error) {
608  if (!$notrigger) {
609  // Call trigger
610  $result = $this->call_trigger('TASK_DELETE', $user);
611  if ($result < 0) {
612  $error++;
613  }
614  // End call triggers
615  }
616  }
617 
618  // Commit or rollback
619  if ($error) {
620  foreach ($this->errors as $errmsg) {
621  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
622  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
623  }
624  $this->db->rollback();
625  return -1 * $error;
626  } else {
627  //Delete associated link file
628  if ($conf->project->dir_output) {
629  $projectstatic = new Project($this->db);
630  $projectstatic->fetch($this->fk_project);
631 
632  $dir = $conf->project->dir_output."/".dol_sanitizeFileName($projectstatic->ref).'/'.dol_sanitizeFileName($this->id);
633  dol_syslog(get_class($this)."::delete dir=".$dir, LOG_DEBUG);
634  if (file_exists($dir)) {
635  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
636  $res = @dol_delete_dir_recursive($dir);
637  if (!$res) {
638  $this->error = 'ErrorFailToDeleteDir';
639  $this->db->rollback();
640  return 0;
641  }
642  }
643  }
644 
645  $this->db->commit();
646 
647  return 1;
648  }
649  }
650 
656  public function hasChildren()
657  {
658  $error = 0;
659  $ret = 0;
660 
661  $sql = "SELECT COUNT(*) as nb";
662  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task";
663  $sql .= " WHERE fk_task_parent = ".((int) $this->id);
664 
665  dol_syslog(get_class($this)."::hasChildren", LOG_DEBUG);
666  $resql = $this->db->query($sql);
667  if (!$resql) {
668  $error++; $this->errors[] = "Error ".$this->db->lasterror();
669  } else {
670  $obj = $this->db->fetch_object($resql);
671  if ($obj) {
672  $ret = $obj->nb;
673  }
674  $this->db->free($resql);
675  }
676 
677  if (!$error) {
678  return $ret;
679  } else {
680  return -1;
681  }
682  }
683 
689  public function hasTimeSpent()
690  {
691  $error = 0;
692  $ret = 0;
693 
694  $sql = "SELECT COUNT(*) as nb";
695  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time";
696  $sql .= " WHERE fk_task = ".((int) $this->id);
697 
698  dol_syslog(get_class($this)."::hasTimeSpent", LOG_DEBUG);
699  $resql = $this->db->query($sql);
700  if (!$resql) {
701  $error++; $this->errors[] = "Error ".$this->db->lasterror();
702  } else {
703  $obj = $this->db->fetch_object($resql);
704  if ($obj) {
705  $ret = $obj->nb;
706  }
707  $this->db->free($resql);
708  }
709 
710  if (!$error) {
711  return $ret;
712  } else {
713  return -1;
714  }
715  }
716 
717 
730  public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
731  {
732  global $conf, $langs, $user;
733 
734  if (!empty($conf->dol_no_mouse_hover)) {
735  $notooltip = 1; // Force disable tooltips
736  }
737 
738  $result = '';
739  $label = img_picto('', $this->picto).' <u>'.$langs->trans("Task").'</u>';
740  if (!empty($this->ref)) {
741  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
742  }
743  if (!empty($this->label)) {
744  $label .= '<br><b>'.$langs->trans('LabelTask').':</b> '.$this->label;
745  }
746  if ($this->date_start || $this->date_end) {
747  $label .= "<br>".get_date_range($this->date_start, $this->date_end, '', $langs, 0);
748  }
749 
750  $url = DOL_URL_ROOT.'/projet/tasks/'.$mode.'.php?id='.$this->id.($option == 'withproject' ? '&withproject=1' : '');
751  // Add param to save lastsearch_values or not
752  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
753  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
754  $add_save_lastsearch_values = 1;
755  }
756  if ($add_save_lastsearch_values) {
757  $url .= '&save_lastsearch_values=1';
758  }
759 
760  $linkclose = '';
761  if (empty($notooltip)) {
762  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
763  $label = $langs->trans("ShowTask");
764  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
765  }
766  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
767  $linkclose .= ' class="classfortooltip nowraponall"';
768  } else {
769  $linkclose .= ' class="nowraponall"';
770  }
771 
772  $linkstart = '<a href="'.$url.'"';
773  $linkstart .= $linkclose.'>';
774  $linkend = '</a>';
775 
776  $picto = 'projecttask';
777 
778  $result .= $linkstart;
779  if ($withpicto) {
780  $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
781  }
782  if ($withpicto != 2) {
783  $result .= $this->ref;
784  }
785  $result .= $linkend;
786  if ($withpicto != 2) {
787  $result .= (($addlabel && $this->label) ? $sep.dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
788  }
789 
790  return $result;
791  }
792 
800  public function initAsSpecimen()
801  {
802  $this->id = 0;
803 
804  $this->fk_project = '';
805  $this->ref = 'TK01';
806  $this->fk_task_parent = null;
807  $this->label = 'Specimen task TK01';
808  $this->duration_effective = '';
809  $this->fk_user_creat = null;
810  $this->progress = '25';
811  $this->priority = 0;
812  $this->fk_statut = null;
813  $this->note = 'This is a specimen task not';
814  }
815 
839  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 = '')
840  {
841  global $conf, $hookmanager;
842 
843  $tasks = array();
844 
845  //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
846 
847  // List of tasks (does not care about permissions. Filtering will be done later)
848  $sql = "SELECT ";
849  if ($filteronprojuser > 0 || $filterontaskuser > 0) {
850  $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
851  }
852  $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
853  $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,";
854  $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
855  $sql .= " t.budget_amount,";
856  $sql .= " t.note_public, t.note_private,";
857  $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
858  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
859  if (!empty($extrafields->attributes['projet']['label'])) {
860  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
861  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key." as options_".$key : '');
862  }
863  }
864  if (!empty($extrafields->attributes['projet_task']['label'])) {
865  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
866  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key." as options_".$key : '');
867  }
868  }
869  if ($includebilltime) {
870  $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";
871  }
872 
873  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
874  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
875  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_extrafields as efp ON (p.rowid = efp.fk_object)";
876 
877  if ($mode == 0) {
878  if ($filteronprojuser > 0) {
879  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
880  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
881  }
882  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
883  if ($includebilltime) {
884  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
885  }
886  if ($filterontaskuser > 0) {
887  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
888  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
889  }
890  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
891  $sql .= " WHERE p.entity IN (".getEntity('project').")";
892  $sql .= " AND t.fk_projet = p.rowid";
893  } elseif ($mode == 1) {
894  if ($filteronprojuser > 0) {
895  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
896  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
897  }
898  if ($filterontaskuser > 0) {
899  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
900  if ($includebilltime) {
901  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
902  }
903  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec2";
904  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc2";
905  } else {
906  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task as t on t.fk_projet = p.rowid";
907  if ($includebilltime) {
908  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_time as tt ON tt.fk_task = t.rowid";
909  }
910  }
911  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
912  $sql .= " WHERE p.entity IN (".getEntity('project').")";
913  } else {
914  return 'BadValueForParameterMode';
915  }
916 
917  if ($filteronprojuser > 0) {
918  $sql .= " AND p.rowid = ec.element_id";
919  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
920  $sql .= " AND ctc.element = 'project'";
921  $sql .= " AND ec.fk_socpeople = ".((int) $filteronprojuser);
922  $sql .= " AND ec.statut = 4";
923  $sql .= " AND ctc.source = 'internal'";
924  }
925  if ($filterontaskuser > 0) {
926  $sql .= " AND t.fk_projet = p.rowid";
927  $sql .= " AND p.rowid = ec2.element_id";
928  $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
929  $sql .= " AND ctc2.element = 'project_task'";
930  $sql .= " AND ec2.fk_socpeople = ".((int) $filterontaskuser);
931  $sql .= " AND ec2.statut = 4";
932  $sql .= " AND ctc2.source = 'internal'";
933  }
934  if ($socid) {
935  $sql .= " AND p.fk_soc = ".((int) $socid);
936  }
937  if ($projectid) {
938  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectid).")";
939  }
940  if ($filteronproj) {
941  $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
942  }
943  if ($filteronprojstatus && $filteronprojstatus != '-1') {
944  $sql .= " AND p.fk_statut IN (".$this->db->sanitize($filteronprojstatus).")";
945  }
946  if ($morewherefilter) {
947  $sql .= $morewherefilter;
948  }
949  // Add where from extra fields
950  $extrafieldsobjectkey = 'projet_task';
951  $extrafieldsobjectprefix = 'efpt.';
952  global $db; // needed for extrafields_list_search_sql.tpl
953  include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
954  // Add where from hooks
955  $parameters = array();
956  $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
957  $sql .= $hookmanager->resPrint;
958  if ($includebilltime) {
959  $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
960  $sql .= " t.datec, t.dateo, t.datee, t.tms,";
961  $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
962  $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
963  $sql .= " t.budget_amount,";
964  $sql .= " t.note_public, t.note_private,";
965  $sql .= " s.rowid, s.nom, s.email,";
966  $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
967  if (!empty($extrafields->attributes['projet']['label'])) {
968  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
969  $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp.".$key : '');
970  }
971  }
972  if (!empty($extrafields->attributes['projet_task']['label'])) {
973  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
974  $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt.".$key : '');
975  }
976  }
977  }
978 
979  if ($sortfield && $sortorder) {
980  $sql .= $this->db->order($sortfield, $sortorder);
981  } else {
982  $sql .= " ORDER BY p.ref, t.rang, t.dateo";
983  }
984 
985  //print $sql;exit;
986  dol_syslog(get_class($this)."::getTasksArray", LOG_DEBUG);
987  $resql = $this->db->query($sql);
988  if ($resql) {
989  $num = $this->db->num_rows($resql);
990  $i = 0;
991  // Loop on each record found, so each couple (project id, task id)
992  while ($i < $num) {
993  $error = 0;
994 
995  $obj = $this->db->fetch_object($resql);
996 
997  if ($loadRoleMode) {
998  if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user
999  if (!$this->getUserRolesForProjectsOrTasks($userp, 0, $obj->projectid, 0)) {
1000  $error++;
1001  }
1002  }
1003  if (is_object($usert)) { // If we ask a filter on a user affected to a task
1004  if (!$this->getUserRolesForProjectsOrTasks(0, $usert, $obj->projectid, $obj->taskid)) {
1005  $error++;
1006  }
1007  }
1008  }
1009 
1010  if (!$error) {
1011  $tasks[$i] = new Task($this->db);
1012  $tasks[$i]->id = $obj->taskid;
1013  $tasks[$i]->ref = $obj->taskref;
1014  $tasks[$i]->fk_project = $obj->projectid;
1015  $tasks[$i]->projectref = $obj->ref;
1016  $tasks[$i]->projectlabel = $obj->plabel;
1017  $tasks[$i]->projectstatus = $obj->projectstatus;
1018 
1019  $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1020  $tasks[$i]->opp_amount = $obj->opp_amount;
1021  $tasks[$i]->opp_percent = $obj->opp_percent;
1022  $tasks[$i]->budget_amount = $obj->budget_amount;
1023  $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1024  $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1025 
1026  $tasks[$i]->label = $obj->label;
1027  $tasks[$i]->description = $obj->description;
1028  $tasks[$i]->fk_parent = $obj->fk_task_parent; // deprecated
1029  $tasks[$i]->note_public = $obj->note_public;
1030  $tasks[$i]->note_private = $obj->note_private;
1031  $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1032  $tasks[$i]->duration = $obj->duration_effective;
1033  $tasks[$i]->planned_workload = $obj->planned_workload;
1034 
1035  if ($includebilltime) {
1036  $tasks[$i]->tobill = $obj->tobill;
1037  $tasks[$i]->billed = $obj->billed;
1038  }
1039 
1040  $tasks[$i]->progress = $obj->progress;
1041  $tasks[$i]->fk_statut = $obj->status;
1042  $tasks[$i]->public = $obj->public;
1043  $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1044  $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
1045  $tasks[$i]->rang = $obj->rang;
1046  $tasks[$i]->priority = $obj->priority;
1047 
1048  $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
1049  $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1050  $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1051  $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1052 
1053  if (!empty($extrafields->attributes['projet']['label'])) {
1054  foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1055  if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1056  $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
1057  }
1058  }
1059  }
1060 
1061  if (!empty($extrafields->attributes['projet_task']['label'])) {
1062  foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1063  if ($extrafields->attributes['projet_task']['type'][$key] != 'separate') {
1064  $tasks[$i]->{'options_'.$key} = $obj->{'options_'.$key};
1065  }
1066  }
1067  }
1068 
1069  if ($loadextras) {
1070  $tasks[$i]->fetch_optionals();
1071  }
1072  }
1073 
1074  $i++;
1075  }
1076  $this->db->free($resql);
1077  } else {
1078  dol_print_error($this->db);
1079  }
1080 
1081  return $tasks;
1082  }
1083 
1094  public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1095  {
1096  $arrayroles = array();
1097 
1098  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks userp=".is_object($userp)." usert=".is_object($usert)." projectid=".$projectid." taskid=".$taskid);
1099 
1100  // We want role of user for a projet or role of user for a task. Both are not possible.
1101  if (empty($userp) && empty($usert)) {
1102  $this->error = "CallWithWrongParameters";
1103  return -1;
1104  }
1105  if (!empty($userp) && !empty($usert)) {
1106  $this->error = "CallWithWrongParameters";
1107  return -1;
1108  }
1109 
1110  /* Liste des taches et role sur les projets ou taches */
1111  $sql = "SELECT pt.rowid as pid, ec.element_id, ctc.code, ctc.source";
1112  if ($userp) {
1113  $sql .= " FROM ".MAIN_DB_PREFIX."projet as pt";
1114  }
1115  if ($usert && $filteronprojstatus > -1) {
1116  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p, ".MAIN_DB_PREFIX."projet_task as pt";
1117  }
1118  if ($usert && $filteronprojstatus <= -1) {
1119  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task as pt";
1120  }
1121  $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1122  $sql .= ", ".MAIN_DB_PREFIX."c_type_contact as ctc";
1123  $sql .= " WHERE pt.rowid = ec.element_id";
1124  if ($userp && $filteronprojstatus > -1) {
1125  $sql .= " AND pt.fk_statut = ".((int) $filteronprojstatus);
1126  }
1127  if ($usert && $filteronprojstatus > -1) {
1128  $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = ".((int) $filteronprojstatus);
1129  }
1130  if ($userp) {
1131  $sql .= " AND ctc.element = 'project'";
1132  }
1133  if ($usert) {
1134  $sql .= " AND ctc.element = 'project_task'";
1135  }
1136  $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1137  if ($userp) {
1138  $sql .= " AND ec.fk_socpeople = ".((int) $userp->id);
1139  }
1140  if ($usert) {
1141  $sql .= " AND ec.fk_socpeople = ".((int) $usert->id);
1142  }
1143  $sql .= " AND ec.statut = 4";
1144  $sql .= " AND ctc.source = 'internal'";
1145  if ($projectid) {
1146  if ($userp) {
1147  $sql .= " AND pt.rowid IN (".$this->db->sanitize($projectid).")";
1148  }
1149  if ($usert) {
1150  $sql .= " AND pt.fk_projet IN (".$this->db->sanitize($projectid).")";
1151  }
1152  }
1153  if ($taskid) {
1154  if ($userp) {
1155  $sql .= " ERROR SHOULD NOT HAPPENS";
1156  }
1157  if ($usert) {
1158  $sql .= " AND pt.rowid = ".((int) $taskid);
1159  }
1160  }
1161  //print $sql;
1162 
1163  dol_syslog(get_class($this)."::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1164  $resql = $this->db->query($sql);
1165  if ($resql) {
1166  $num = $this->db->num_rows($resql);
1167  $i = 0;
1168  while ($i < $num) {
1169  $obj = $this->db->fetch_object($resql);
1170  if (empty($arrayroles[$obj->pid])) {
1171  $arrayroles[$obj->pid] = $obj->code;
1172  } else {
1173  $arrayroles[$obj->pid] .= ','.$obj->code;
1174  }
1175  $i++;
1176  }
1177  $this->db->free($resql);
1178  } else {
1179  dol_print_error($this->db);
1180  }
1181 
1182  return $arrayroles;
1183  }
1184 
1185 
1192  public function getListContactId($source = 'internal')
1193  {
1194  $contactAlreadySelected = array();
1195  $tab = $this->liste_contact(-1, $source);
1196  //var_dump($tab);
1197  $num = count($tab);
1198  $i = 0;
1199  while ($i < $num) {
1200  if ($source == 'thirdparty') {
1201  $contactAlreadySelected[$i] = $tab[$i]['socid'];
1202  } else {
1203  $contactAlreadySelected[$i] = $tab[$i]['id'];
1204  }
1205  $i++;
1206  }
1207  return $contactAlreadySelected;
1208  }
1209 
1210 
1218  public function addTimeSpent($user, $notrigger = 0)
1219  {
1220  global $conf, $langs;
1221 
1222  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1223 
1224  $ret = 0;
1225  $now = dol_now();
1226 
1227  // Check parameters
1228  if (!is_object($user)) {
1229  dol_print_error('', "Method addTimeSpent was called with wrong parameter user");
1230  return -1;
1231  }
1232 
1233  // Clean parameters
1234  if (isset($this->timespent_note)) {
1235  $this->timespent_note = trim($this->timespent_note);
1236  }
1237  if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1238  $this->timespent_datehour = $this->timespent_date;
1239  }
1240 
1241  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1242  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1243  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1244 
1245  if ($this->timespent_date < $restrictBefore) {
1246  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1247  $this->errors[] = $this->error;
1248  return -1;
1249  }
1250  }
1251 
1252 
1253  $this->db->begin();
1254 
1255  $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet_task_time (";
1256  $sql .= "fk_task";
1257  $sql .= ", task_date";
1258  $sql .= ", task_datehour";
1259  $sql .= ", task_date_withhour";
1260  $sql .= ", task_duration";
1261  $sql .= ", fk_user";
1262  $sql .= ", fk_product";
1263  $sql .= ", note";
1264  $sql .= ", datec";
1265  $sql .= ") VALUES (";
1266  $sql .= ((int) $this->id);
1267  $sql .= ", '".$this->db->idate($this->timespent_date)."'";
1268  $sql .= ", '".$this->db->idate($this->timespent_datehour)."'";
1269  $sql .= ", ".(empty($this->timespent_withhour) ? 0 : 1);
1270  $sql .= ", ".((int) $this->timespent_duration);
1271  $sql .= ", ".((int) $this->timespent_fk_user);
1272  $sql .= ", ".((int) $this->timespent_fk_product);
1273  $sql .= ", ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null");
1274  $sql .= ", '".$this->db->idate($now)."'";
1275  $sql .= ")";
1276 
1277  $resql = $this->db->query($sql);
1278  if ($resql) {
1279  $tasktime_id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet_task_time");
1280  $ret = $tasktime_id;
1281  $this->timespent_id = $ret;
1282 
1283  if (!$notrigger) {
1284  // Call trigger
1285  $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1286  if ($result < 0) {
1287  $ret = -1;
1288  }
1289  // End call triggers
1290  }
1291  } else {
1292  $this->error = $this->db->lasterror();
1293  $ret = -1;
1294  }
1295 
1296  if ($ret > 0) {
1297  // Recalculate amount of time spent for task and update denormalized field
1298  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1299  $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".((int) $this->id).")";
1300  if (isset($this->progress)) {
1301  $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided
1302  }
1303  $sql .= " WHERE rowid = ".((int) $this->id);
1304 
1305  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1306  if (!$this->db->query($sql)) {
1307  $this->error = $this->db->lasterror();
1308  $ret = -2;
1309  }
1310 
1311  // Update hourly rate of this time spent entry
1312  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
1313  $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user
1314  $sql .= " WHERE rowid = ".((int) $tasktime_id);
1315 
1316  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1317  if (!$this->db->query($sql)) {
1318  $this->error = $this->db->lasterror();
1319  $ret = -2;
1320  }
1321  }
1322 
1323  if ($ret > 0) {
1324  $this->db->commit();
1325  } else {
1326  $this->db->rollback();
1327  }
1328  return $ret;
1329  }
1330 
1337  public function fetchTimeSpentOnTask($morewherefilter = '')
1338  {
1339  global $langs;
1340 
1341  $arrayres = array();
1342 
1343  $sql = "SELECT";
1344  $sql .= " s.rowid as socid,";
1345  $sql .= " s.nom as thirdparty_name,";
1346  $sql .= " s.email as thirdparty_email,";
1347  $sql .= " ptt.rowid,";
1348  $sql .= " ptt.fk_task,";
1349  $sql .= " ptt.task_date,";
1350  $sql .= " ptt.task_datehour,";
1351  $sql .= " ptt.task_date_withhour,";
1352  $sql .= " ptt.task_duration,";
1353  $sql .= " ptt.fk_user,";
1354  $sql .= " ptt.note,";
1355  $sql .= " ptt.thm,";
1356  $sql .= " pt.rowid as task_id,";
1357  $sql .= " pt.ref as task_ref,";
1358  $sql .= " pt.label as task_label,";
1359  $sql .= " p.rowid as project_id,";
1360  $sql .= " p.ref as project_ref,";
1361  $sql .= " p.title as project_label,";
1362  $sql .= " p.public as public";
1363  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1364  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1365  $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
1366  $sql .= " AND pt.rowid = ".((int) $this->id);
1367  $sql .= " AND pt.entity IN (".getEntity('project').")";
1368  if ($morewherefilter) {
1369  $sql .= $morewherefilter;
1370  }
1371 
1372  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1373  $resql = $this->db->query($sql);
1374  if ($resql) {
1375  $num = $this->db->num_rows($resql);
1376 
1377  $i = 0;
1378  while ($i < $num) {
1379  $obj = $this->db->fetch_object($resql);
1380 
1381  $newobj = new stdClass();
1382 
1383  $newobj->socid = $obj->socid;
1384  $newobj->thirdparty_name = $obj->thirdparty_name;
1385  $newobj->thirdparty_email = $obj->thirdparty_email;
1386 
1387  $newobj->fk_project = $obj->project_id;
1388  $newobj->project_ref = $obj->project_ref;
1389  $newobj->project_label = $obj->project_label;
1390  $newobj->public = $obj->project_public;
1391 
1392  $newobj->fk_task = $obj->task_id;
1393  $newobj->task_ref = $obj->task_ref;
1394  $newobj->task_label = $obj->task_label;
1395 
1396  $newobj->timespent_line_id = $obj->rowid;
1397  $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1398  $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1399  $newobj->timespent_line_withhour = $obj->task_date_withhour;
1400  $newobj->timespent_line_duration = $obj->task_duration;
1401  $newobj->timespent_line_fk_user = $obj->fk_user;
1402  $newobj->timespent_line_thm = $obj->thm; // hourly rate
1403  $newobj->timespent_line_note = $obj->note;
1404 
1405  $arrayres[] = $newobj;
1406 
1407  $i++;
1408  }
1409 
1410  $this->db->free($resql);
1411 
1412  $this->lines = $arrayres;
1413  return 1;
1414  } else {
1415  dol_print_error($this->db);
1416  $this->error = "Error ".$this->db->lasterror();
1417  return -1;
1418  }
1419  }
1420 
1421 
1429  public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1430  {
1431  global $langs;
1432 
1433  if (is_object($userobj)) {
1434  $userid = $userobj->id;
1435  } else {
1436  $userid = $userobj; // old method
1437  }
1438 
1439  $id = $this->id;
1440  if (empty($id) && empty($userid)) {
1441  dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1442  return -1;
1443  }
1444 
1445  $result = array();
1446 
1447  $sql = "SELECT";
1448  $sql .= " MIN(t.task_datehour) as min_date,";
1449  $sql .= " MAX(t.task_datehour) as max_date,";
1450  $sql .= " SUM(t.task_duration) as total_duration,";
1451  $sql .= " SUM(t.task_duration / 3600 * ".$this->db->ifsql("t.thm IS NULL", 0, "t.thm").") as total_amount,";
1452  $sql .= " COUNT(t.rowid) as nblines,";
1453  $sql .= " SUM(".$this->db->ifsql("t.thm IS NULL", 1, 0).") as nblinesnull";
1454  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1455  $sql .= " WHERE 1 = 1";
1456  if ($morewherefilter) {
1457  $sql .= $morewherefilter;
1458  }
1459  if ($id > 0) {
1460  $sql .= " AND t.fk_task = ".((int) $id);
1461  }
1462  if ($userid > 0) {
1463  $sql .= " AND t.fk_user = ".((int) $userid);
1464  }
1465 
1466  dol_syslog(get_class($this)."::getSummaryOfTimeSpent", LOG_DEBUG);
1467  $resql = $this->db->query($sql);
1468  if ($resql) {
1469  $obj = $this->db->fetch_object($resql);
1470 
1471  $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1472  $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1473  $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1474 
1475  $this->timespent_min_date = $this->db->jdate($obj->min_date);
1476  $this->timespent_max_date = $this->db->jdate($obj->max_date);
1477  $this->timespent_total_duration = $obj->total_duration;
1478  $this->timespent_total_amount = $obj->total_amount;
1479  $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1480  $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1481 
1482  $this->db->free($resql);
1483  } else {
1484  dol_print_error($this->db);
1485  }
1486  return $result;
1487  }
1488 
1497  public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1498  {
1499  global $langs;
1500 
1501  if (empty($id)) {
1502  $id = $this->id;
1503  }
1504 
1505  $result = array();
1506 
1507  $sql = "SELECT";
1508  $sql .= " SUM(t.task_duration) as nbseconds,";
1509  $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";
1510  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1511  $sql .= " WHERE t.fk_task = ".((int) $id);
1512  if (is_object($fuser) && $fuser->id > 0) {
1513  $sql .= " AND fk_user = ".((int) $fuser->id);
1514  }
1515  if ($dates > 0) {
1516  $datefieldname = "task_datehour";
1517  $sql .= " AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
1518  }
1519  if ($datee > 0) {
1520  $datefieldname = "task_datehour";
1521  $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
1522  }
1523  //print $sql;
1524 
1525  dol_syslog(get_class($this)."::getSumOfAmount", LOG_DEBUG);
1526  $resql = $this->db->query($sql);
1527  if ($resql) {
1528  $obj = $this->db->fetch_object($resql);
1529 
1530  $result['amount'] = $obj->amount;
1531  $result['nbseconds'] = $obj->nbseconds;
1532  $result['nblinesnull'] = $obj->nblinesnull;
1533 
1534  $this->db->free($resql);
1535  return $result;
1536  } else {
1537  dol_print_error($this->db);
1538  return $result;
1539  }
1540  }
1541 
1548  public function fetchTimeSpent($id)
1549  {
1550  global $langs;
1551 
1552  $sql = "SELECT";
1553  $sql .= " t.rowid,";
1554  $sql .= " t.fk_task,";
1555  $sql .= " t.task_date,";
1556  $sql .= " t.task_datehour,";
1557  $sql .= " t.task_date_withhour,";
1558  $sql .= " t.task_duration,";
1559  $sql .= " t.fk_user,";
1560  $sql .= " t.fk_product,";
1561  $sql .= " t.thm,";
1562  $sql .= " t.note";
1563  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t";
1564  $sql .= " WHERE t.rowid = ".((int) $id);
1565 
1566  dol_syslog(get_class($this)."::fetchTimeSpent", LOG_DEBUG);
1567  $resql = $this->db->query($sql);
1568  if ($resql) {
1569  if ($this->db->num_rows($resql)) {
1570  $obj = $this->db->fetch_object($resql);
1571 
1572  $this->timespent_id = $obj->rowid;
1573  $this->id = $obj->fk_task;
1574  $this->timespent_date = $this->db->jdate($obj->task_date);
1575  $this->timespent_datehour = $this->db->jdate($obj->task_datehour);
1576  $this->timespent_withhour = $obj->task_date_withhour;
1577  $this->timespent_duration = $obj->task_duration;
1578  $this->timespent_fk_user = $obj->fk_user;
1579  $this->timespent_fk_product = $obj->fk_product;
1580  $this->timespent_thm = $obj->thm; // hourly rate
1581  $this->timespent_note = $obj->note;
1582  }
1583 
1584  $this->db->free($resql);
1585 
1586  return 1;
1587  } else {
1588  $this->error = "Error ".$this->db->lasterror();
1589  return -1;
1590  }
1591  }
1592 
1600  public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1601  {
1602  global $langs;
1603 
1604  $arrayres = array();
1605 
1606  $sql = "SELECT";
1607  $sql .= " s.rowid as socid,";
1608  $sql .= " s.nom as thirdparty_name,";
1609  $sql .= " s.email as thirdparty_email,";
1610  $sql .= " ptt.rowid,";
1611  $sql .= " ptt.fk_task,";
1612  $sql .= " ptt.task_date,";
1613  $sql .= " ptt.task_datehour,";
1614  $sql .= " ptt.task_date_withhour,";
1615  $sql .= " ptt.task_duration,";
1616  $sql .= " ptt.fk_user,";
1617  $sql .= " ptt.note,";
1618  $sql .= " ptt.thm,";
1619  $sql .= " pt.rowid as task_id,";
1620  $sql .= " pt.ref as task_ref,";
1621  $sql .= " pt.label as task_label,";
1622  $sql .= " p.rowid as project_id,";
1623  $sql .= " p.ref as project_ref,";
1624  $sql .= " p.title as project_label,";
1625  $sql .= " p.public as public";
1626  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p";
1627  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
1628  $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid";
1629  $sql .= " AND ptt.fk_user = ".((int) $userobj->id);
1630  $sql .= " AND pt.entity IN (".getEntity('project').")";
1631  if ($morewherefilter) {
1632  $sql .= $morewherefilter;
1633  }
1634 
1635  dol_syslog(get_class($this)."::fetchAllTimeSpent", LOG_DEBUG);
1636  $resql = $this->db->query($sql);
1637  if ($resql) {
1638  $num = $this->db->num_rows($resql);
1639 
1640  $i = 0;
1641  while ($i < $num) {
1642  $obj = $this->db->fetch_object($resql);
1643 
1644  $newobj = new stdClass();
1645 
1646  $newobj->socid = $obj->socid;
1647  $newobj->thirdparty_name = $obj->thirdparty_name;
1648  $newobj->thirdparty_email = $obj->thirdparty_email;
1649 
1650  $newobj->fk_project = $obj->project_id;
1651  $newobj->project_ref = $obj->project_ref;
1652  $newobj->project_label = $obj->project_label;
1653  $newobj->public = $obj->project_public;
1654 
1655  $newobj->fk_task = $obj->task_id;
1656  $newobj->task_ref = $obj->task_ref;
1657  $newobj->task_label = $obj->task_label;
1658 
1659  $newobj->timespent_id = $obj->rowid;
1660  $newobj->timespent_date = $this->db->jdate($obj->task_date);
1661  $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1662  $newobj->timespent_withhour = $obj->task_date_withhour;
1663  $newobj->timespent_duration = $obj->task_duration;
1664  $newobj->timespent_fk_user = $obj->fk_user;
1665  $newobj->timespent_thm = $obj->thm; // hourly rate
1666  $newobj->timespent_note = $obj->note;
1667 
1668  $arrayres[] = $newobj;
1669 
1670  $i++;
1671  }
1672 
1673  $this->db->free($resql);
1674  } else {
1675  dol_print_error($this->db);
1676  $this->error = "Error ".$this->db->lasterror();
1677  return -1;
1678  }
1679 
1680  return $arrayres;
1681  }
1682 
1690  public function updateTimeSpent($user, $notrigger = 0)
1691  {
1692  global $conf, $langs;
1693 
1694  $ret = 0;
1695 
1696  // Check parameters
1697  if ($this->timespent_date == '') {
1698  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1699  return -1;
1700  }
1701  if (!($this->timespent_fk_user > 0)) {
1702  $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1703  return -1;
1704  }
1705 
1706  // Clean parameters
1707  if (empty($this->timespent_datehour)) {
1708  $this->timespent_datehour = $this->timespent_date;
1709  }
1710  if (isset($this->timespent_note)) {
1711  $this->timespent_note = trim($this->timespent_note);
1712  }
1713 
1714  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1715  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1716  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1717 
1718  if ($this->timespent_date < $restrictBefore) {
1719  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1720  $this->errors[] = $this->error;
1721  return -1;
1722  }
1723  }
1724 
1725  $this->db->begin();
1726 
1727  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time SET";
1728  $sql .= " task_date = '".$this->db->idate($this->timespent_date)."',";
1729  $sql .= " task_datehour = '".$this->db->idate($this->timespent_datehour)."',";
1730  $sql .= " task_date_withhour = ".(empty($this->timespent_withhour) ? 0 : 1).",";
1731  $sql .= " task_duration = ".((int) $this->timespent_duration).",";
1732  $sql .= " fk_user = ".((int) $this->timespent_fk_user).",";
1733  $sql .= " fk_product = ".((int) $this->timespent_fk_product).",";
1734  $sql .= " note = ".(isset($this->timespent_note) ? "'".$this->db->escape($this->timespent_note)."'" : "null");
1735  $sql .= " WHERE rowid = ".((int) $this->timespent_id);
1736 
1737  dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG);
1738  if ($this->db->query($sql)) {
1739  if (!$notrigger) {
1740  // Call trigger
1741  $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1742  if ($result < 0) {
1743  $this->db->rollback();
1744  $ret = -1;
1745  } else {
1746  $ret = 1;
1747  }
1748  // End call triggers
1749  } else {
1750  $ret = 1;
1751  }
1752  } else {
1753  $this->error = $this->db->lasterror();
1754  $this->db->rollback();
1755  $ret = -1;
1756  }
1757 
1758  if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || !empty($conf->global->TIMESPENT_ALWAYS_UPDATE_THM))) {
1759  if ($this->timespent_old_duration != $this->timespent_duration) {
1760  // Recalculate amount of time spent for task and update denormalized field
1761  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1762  $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM " . MAIN_DB_PREFIX . "projet_task_time as ptt where ptt.fk_task = " . ((int) $this->id) . ")";
1763  if (isset($this->progress)) {
1764  $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1765  }
1766  $sql .= " WHERE rowid = " . ((int) $this->id);
1767 
1768  dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1769  if (!$this->db->query($sql)) {
1770  $this->error = $this->db->lasterror();
1771  $this->db->rollback();
1772  $ret = -2;
1773  }
1774  }
1775 
1776  // Update hourly rate of this time spent entry, but only if it was not set initialy
1777  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time";
1778  $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user
1779  $sql .= " WHERE rowid = ".((int) $this->timespent_id);
1780  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
1781  $sql .= " AND (thm IS NULL OR thm = 0)";
1782  }
1783 
1784  dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG);
1785  if (!$this->db->query($sql)) {
1786  $this->error = $this->db->lasterror();
1787  $ret = -2;
1788  }
1789  }
1790 
1791  if ($ret >= 0) {
1792  $this->db->commit();
1793  }
1794  return $ret;
1795  }
1796 
1804  public function delTimeSpent($user, $notrigger = 0)
1805  {
1806  global $conf, $langs;
1807 
1808  $error = 0;
1809 
1810  if (!empty($conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS)) {
1811  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1812  $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1813 
1814  if ($this->timespent_date < $restrictBefore) {
1815  $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS);
1816  $this->errors[] = $this->error;
1817  return -1;
1818  }
1819  }
1820 
1821  $this->db->begin();
1822 
1823  if (!$notrigger) {
1824  // Call trigger
1825  $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
1826  if ($result < 0) {
1827  $error++;
1828  }
1829  // End call triggers
1830  }
1831 
1832  if (!$error) {
1833  $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_task_time";
1834  $sql .= " WHERE rowid = ".((int) $this->timespent_id);
1835 
1836  dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1837  $resql = $this->db->query($sql);
1838  if (!$resql) {
1839  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1840  }
1841  }
1842 
1843  if (!$error) {
1844  $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task";
1845  $sql .= " SET duration_effective = duration_effective - ".$this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
1846  $sql .= " WHERE rowid = ".((int) $this->id);
1847 
1848  dol_syslog(get_class($this)."::delTimeSpent", LOG_DEBUG);
1849  if ($this->db->query($sql)) {
1850  $result = 0;
1851  } else {
1852  $this->error = $this->db->lasterror();
1853  $result = -2;
1854  }
1855  }
1856 
1857  // Commit or rollback
1858  if ($error) {
1859  foreach ($this->errors as $errmsg) {
1860  dol_syslog(get_class($this)."::delTimeSpent ".$errmsg, LOG_ERR);
1861  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1862  }
1863  $this->db->rollback();
1864  return -1 * $error;
1865  } else {
1866  $this->db->commit();
1867  return 1;
1868  }
1869  }
1870 
1885  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)
1886  {
1887  global $langs, $conf;
1888 
1889  $error = 0;
1890 
1891  //Use 00:00 of today if time is use on task.
1892  $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
1893 
1894  $datec = $now;
1895 
1896  $clone_task = new Task($this->db);
1897  $origin_task = new Task($this->db);
1898 
1899  $clone_task->context['createfromclone'] = 'createfromclone';
1900 
1901  $this->db->begin();
1902 
1903  // Load source object
1904  $clone_task->fetch($fromid);
1905  $clone_task->fetch_optionals();
1906  //var_dump($clone_task->array_options);exit;
1907 
1908  $origin_task->fetch($fromid);
1909 
1910  $defaultref = '';
1911  $obj = empty($conf->global->PROJECT_TASK_ADDON) ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
1912  if (!empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.".php")) {
1913  require_once DOL_DOCUMENT_ROOT."/core/modules/project/task/".$conf->global->PROJECT_TASK_ADDON.'.php';
1914  $modTask = new $obj;
1915  $defaultref = $modTask->getNextValue(0, $clone_task);
1916  }
1917 
1918  $ori_project_id = $clone_task->fk_project;
1919 
1920  $clone_task->id = 0;
1921  $clone_task->ref = $defaultref;
1922  $clone_task->fk_project = $project_id;
1923  $clone_task->fk_task_parent = $parent_task_id;
1924  $clone_task->date_c = $datec;
1925  $clone_task->planned_workload = $origin_task->planned_workload;
1926  $clone_task->rang = $origin_task->rang;
1927  $clone_task->priority = $origin_task->priority;
1928 
1929  //Manage Task Date
1930  if ($clone_change_dt) {
1931  $projectstatic = new Project($this->db);
1932  $projectstatic->fetch($ori_project_id);
1933 
1934  //Origin project strat date
1935  $orign_project_dt_start = $projectstatic->date_start;
1936 
1937  //Calcultate new task start date with difference between origin proj start date and origin task start date
1938  if (!empty($clone_task->date_start)) {
1939  $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
1940  }
1941 
1942  //Calcultate new task end date with difference between origin proj end date and origin task end date
1943  if (!empty($clone_task->date_end)) {
1944  $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
1945  }
1946  }
1947 
1948  if (!$clone_prog) {
1949  $clone_task->progress = 0;
1950  }
1951 
1952  // Create clone
1953  $result = $clone_task->create($user);
1954 
1955  // Other options
1956  if ($result < 0) {
1957  $this->error = $clone_task->error;
1958  $error++;
1959  }
1960 
1961  // End
1962  if (!$error) {
1963  $clone_task_id = $clone_task->id;
1964  $clone_task_ref = $clone_task->ref;
1965 
1966  //Note Update
1967  if (!$clone_note) {
1968  $clone_task->note_private = '';
1969  $clone_task->note_public = '';
1970  } else {
1971  $this->db->begin();
1972  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
1973  if ($res < 0) {
1974  $this->error .= $clone_task->error;
1975  $error++;
1976  $this->db->rollback();
1977  } else {
1978  $this->db->commit();
1979  }
1980 
1981  $this->db->begin();
1982  $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
1983  if ($res < 0) {
1984  $this->error .= $clone_task->error;
1985  $error++;
1986  $this->db->rollback();
1987  } else {
1988  $this->db->commit();
1989  }
1990  }
1991 
1992  //Duplicate file
1993  if ($clone_file) {
1994  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1995 
1996  //retrieve project origin ref to know folder to copy
1997  $projectstatic = new Project($this->db);
1998  $projectstatic->fetch($ori_project_id);
1999  $ori_project_ref = $projectstatic->ref;
2000 
2001  if ($ori_project_id != $project_id) {
2002  $projectstatic->fetch($project_id);
2003  $clone_project_ref = $projectstatic->ref;
2004  } else {
2005  $clone_project_ref = $ori_project_ref;
2006  }
2007 
2008  $clone_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($clone_project_ref)."/".dol_sanitizeFileName($clone_task_ref);
2009  $ori_task_dir = $conf->project->dir_output."/".dol_sanitizeFileName($ori_project_ref)."/".dol_sanitizeFileName($fromid);
2010 
2011  $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
2012  foreach ($filearray as $key => $file) {
2013  if (!file_exists($clone_task_dir)) {
2014  if (dol_mkdir($clone_task_dir) < 0) {
2015  $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
2016  $error++;
2017  }
2018  }
2019 
2020  $rescopy = dol_copy($ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name'], 0, 1);
2021  if (is_numeric($rescopy) && $rescopy < 0) {
2022  $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir.'/'.$file['name'], $clone_task_dir.'/'.$file['name']);
2023  $error++;
2024  }
2025  }
2026  }
2027 
2028  // clone affectation
2029  if ($clone_affectation) {
2030  $origin_task = new Task($this->db);
2031  $origin_task->fetch($fromid);
2032 
2033  foreach (array('internal', 'external') as $source) {
2034  $tab = $origin_task->liste_contact(-1, $source);
2035  $num = count($tab);
2036  $i = 0;
2037  while ($i < $num) {
2038  $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2039  if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2040  $langs->load("errors");
2041  $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2042  $error++;
2043  } else {
2044  if ($clone_task->error != '') {
2045  $this->error .= $clone_task->error;
2046  $error++;
2047  }
2048  }
2049  $i++;
2050  }
2051  }
2052  }
2053 
2054  if ($clone_time) {
2055  //TODO clone time of affectation
2056  }
2057  }
2058 
2059  unset($clone_task->context['createfromclone']);
2060 
2061  if (!$error) {
2062  $this->db->commit();
2063  return $clone_task_id;
2064  } else {
2065  $this->db->rollback();
2066  dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
2067  return -1;
2068  }
2069  }
2070 
2071 
2078  public function getLibStatut($mode = 0)
2079  {
2080  return $this->LibStatut($this->fk_statut, $mode);
2081  }
2082 
2083  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2091  public function LibStatut($status, $mode = 0)
2092  {
2093  // phpcs:enable
2094  global $langs;
2095 
2096  // list of Statut of the task
2097  $this->statuts[0] = 'Draft';
2098  $this->statuts[1] = 'ToDo';
2099  $this->statuts[2] = 'Running';
2100  $this->statuts[3] = 'Finish';
2101  $this->statuts[4] = 'Transfered';
2102  $this->statuts_short[0] = 'Draft';
2103  $this->statuts_short[1] = 'ToDo';
2104  $this->statuts_short[2] = 'Running';
2105  $this->statuts_short[3] = 'Completed';
2106  $this->statuts_short[4] = 'Transfered';
2107 
2108  if ($mode == 0) {
2109  return $langs->trans($this->statuts[$status]);
2110  } elseif ($mode == 1) {
2111  return $langs->trans($this->statuts_short[$status]);
2112  } elseif ($mode == 2) {
2113  if ($status == 0) {
2114  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts_short[$status]);
2115  } elseif ($status == 1) {
2116  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts_short[$status]);
2117  } elseif ($status == 2) {
2118  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts_short[$status]);
2119  } elseif ($status == 3) {
2120  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
2121  } elseif ($status == 4) {
2122  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts_short[$status]);
2123  } elseif ($status == 5) {
2124  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts_short[$status]);
2125  }
2126  } elseif ($mode == 3) {
2127  if ($status == 0) {
2128  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0');
2129  } elseif ($status == 1) {
2130  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1');
2131  } elseif ($status == 2) {
2132  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3');
2133  } elseif ($status == 3) {
2134  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
2135  } elseif ($status == 4) {
2136  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6');
2137  } elseif ($status == 5) {
2138  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5');
2139  }
2140  } elseif ($mode == 4) {
2141  if ($status == 0) {
2142  return img_picto($langs->trans($this->statuts_short[$status]), 'statut0').' '.$langs->trans($this->statuts[$status]);
2143  } elseif ($status == 1) {
2144  return img_picto($langs->trans($this->statuts_short[$status]), 'statut1').' '.$langs->trans($this->statuts[$status]);
2145  } elseif ($status == 2) {
2146  return img_picto($langs->trans($this->statuts_short[$status]), 'statut3').' '.$langs->trans($this->statuts[$status]);
2147  } elseif ($status == 3) {
2148  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
2149  } elseif ($status == 4) {
2150  return img_picto($langs->trans($this->statuts_short[$status]), 'statut6').' '.$langs->trans($this->statuts[$status]);
2151  } elseif ($status == 5) {
2152  return img_picto($langs->trans($this->statuts_short[$status]), 'statut5').' '.$langs->trans($this->statuts[$status]);
2153  }
2154  } elseif ($mode == 5) {
2155  /*if ($status==0) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
2156  elseif ($status==1) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
2157  elseif ($status==2) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
2158  elseif ($status==3) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2159  elseif ($status==4) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2160  elseif ($status==5) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
2161  */
2162  //else return $this->progress.' %';
2163  return '&nbsp;';
2164  } elseif ($mode == 6) {
2165  /*if ($status==0) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut0');
2166  elseif ($status==1) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut1');
2167  elseif ($status==2) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut3');
2168  elseif ($status==3) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2169  elseif ($status==4) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut6');
2170  elseif ($status==5) return $langs->trans($this->statuts[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut5');
2171  */
2172  //else return $this->progress.' %';
2173  return '&nbsp;';
2174  }
2175  }
2176 
2187  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2188  {
2189  global $conf;
2190 
2191  $outputlangs->load("projects");
2192 
2193  if (!dol_strlen($modele)) {
2194  $modele = 'nodefault';
2195 
2196  if (!empty($this->model_pdf)) {
2197  $modele = $this->model_pdf;
2198  } elseif (!empty($this->modelpdf)) { // deprecated
2199  $modele = $this->modelpdf;
2200  } elseif (!empty($conf->global->PROJECT_TASK_ADDON_PDF)) {
2201  $modele = $conf->global->PROJECT_TASK_ADDON_PDF;
2202  }
2203  }
2204 
2205  $modelpath = "core/modules/project/task/doc/";
2206 
2207  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2208  }
2209 
2210 
2211  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2218  public function load_board($user)
2219  {
2220  // phpcs:enable
2221  global $conf, $langs;
2222 
2223  // For external user, no check is done on company because readability is managed by public status of project and assignement.
2224  //$socid = $user->socid;
2225  $socid = 0;
2226 
2227  $projectstatic = new Project($this->db);
2228  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2229 
2230  // List of tasks (does not care about permissions. Filtering will be done later)
2231  $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2232  $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2233  $sql .= " t.dateo as date_start, t.datee as datee";
2234  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2235  //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2236  //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2237  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2238  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2239  $sql .= " AND p.fk_statut = 1";
2240  $sql .= " AND t.fk_projet = p.rowid";
2241  $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2242  if (empty($user->rights->projet->all->lire)) {
2243  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2244  }
2245  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2246  //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).")";
2247  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2248  // 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))";
2249 
2250  //print $sql;
2251  $resql = $this->db->query($sql);
2252  if ($resql) {
2253  $task_static = new Task($this->db);
2254 
2255  $response = new WorkboardResponse();
2256  $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2257  $response->label = $langs->trans("OpenedTasks");
2258  if ($user->hasRight("projet", "all", "lire")) {
2259  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mainmenu=project';
2260  } else {
2261  $response->url = DOL_URL_ROOT.'/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2262  }
2263  $response->img = img_object('', "task");
2264 
2265  // This assignment in condition is not a bug. It allows walking the results.
2266  while ($obj = $this->db->fetch_object($resql)) {
2267  $response->nbtodo++;
2268 
2269  $task_static->projectstatus = $obj->projectstatus;
2270  $task_static->progress = $obj->progress;
2271  $task_static->fk_statut = $obj->status;
2272  $task_static->date_end = $this->db->jdate($obj->datee);
2273 
2274  if ($task_static->hasDelay()) {
2275  $response->nbtodolate++;
2276  }
2277  }
2278 
2279  return $response;
2280  } else {
2281  $this->error = $this->db->error();
2282  return -1;
2283  }
2284  }
2285 
2286 
2287  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2293  public function load_state_board()
2294  {
2295  // phpcs:enable
2296  global $user;
2297 
2298  $mine = 0; $socid = $user->socid;
2299 
2300  $projectstatic = new Project($this->db);
2301  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2302 
2303  // List of tasks (does not care about permissions. Filtering will be done later)
2304  $sql = "SELECT count(p.rowid) as nb";
2305  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2306  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2307  if (empty($user->rights->societe->client->voir) && !$socid) {
2308  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2309  }
2310  $sql .= ", ".MAIN_DB_PREFIX."projet_task as t";
2311  $sql .= " WHERE p.entity IN (".getEntity('project', 0).')';
2312  $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2313  if ($mine || empty($user->rights->projet->all->lire)) {
2314  $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2315  }
2316  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2317  //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).")";
2318  if ($socid) {
2319  $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
2320  }
2321  if (empty($user->rights->societe->client->voir) && !$socid) {
2322  $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))";
2323  }
2324 
2325  $resql = $this->db->query($sql);
2326  if ($resql) {
2327  // This assignment in condition is not a bug. It allows walking the results.
2328  while ($obj = $this->db->fetch_object($resql)) {
2329  $this->nb["tasks"] = $obj->nb;
2330  }
2331  $this->db->free($resql);
2332  return 1;
2333  } else {
2334  dol_print_error($this->db);
2335  $this->error = $this->db->error();
2336  return -1;
2337  }
2338  }
2339 
2345  public function hasDelay()
2346  {
2347  global $conf;
2348 
2349  if (!($this->progress >= 0 && $this->progress < 100)) {
2350  return false;
2351  }
2352 
2353  $now = dol_now();
2354 
2355  $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
2356 
2357  return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2358  }
2359 }
$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:279
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:689
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:730
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:839
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:381
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:800
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:656
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