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