dolibarr  9.0.0
project.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2002-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2005-2016 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005-2010 Regis Houssin <regis.houssin@inodbox.com>
5  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
6  * Copyright (C) 2014-2017 Marcos GarcĂ­a <marcosgdf@gmail.com>
7  * Copyright (C) 2017 Ferran Marcet <fmarcet@2byte.es>
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 <http://www.gnu.org/licenses/>.
21  */
22 
28 require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
29 
33 class Project extends CommonObject
34 {
35 
39  public $element = 'project';
40 
44  public $table_element = 'projet';
45 
49  public $table_element_line = 'projet_task';
50 
54  public $fk_element = 'fk_projet';
55 
60  public $ismultientitymanaged = 1;
61 
62  public $picto = 'projectpub';
63 
67  protected $table_ref_field = 'ref';
68 
72  public $description;
73 
79  public $titre;
80 
81  public $title;
82  public $date_start;
83  public $date_end;
84  public $date_close;
85 
86  public $socid; // To store id of thirdparty
87  public $thirdparty_name; // To store name of thirdparty (defined only in some cases)
88 
89  public $user_author_id;
90  public $user_close_id;
91  public $public;
92  public $budget_amount;
93  public $bill_time; // Is the time spent on project must be invoiced or not
94 
95  public $statuts_short;
96  public $statuts_long;
97 
98  public $statut; // 0=draft, 1=opened, 2=closed
99  public $opp_status; // opportunity status, into table llx_c_lead_status
100  public $opp_percent; // opportunity probability
101 
102  public $oldcopy;
103 
104  public $weekWorkLoad; // Used to store workload details of a projet
105  public $weekWorkLoadPerTask; // Used to store workload details of tasks of a projet
106 
112  public $datec;
113 
117  public $date_c;
118 
124  public $datem;
125 
129  public $date_m;
130 
134  public $lines;
135 
139  const STATUS_DRAFT = 0;
140 
144  const STATUS_VALIDATED = 1;
145 
149  const STATUS_CLOSED = 2;
150 
151 
152 
158  function __construct($db)
159  {
160  $this->db = $db;
161 
162  $this->statuts_short = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
163  $this->statuts_long = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
164  }
165 
173  function create($user, $notrigger=0)
174  {
175  global $conf, $langs;
176 
177  $error = 0;
178  $ret = 0;
179 
180  $now=dol_now();
181 
182  // Clean parameters
183  $this->note_private = dol_substr($this->note_private, 0, 65535);
184  $this->note_public = dol_substr($this->note_public, 0, 65535);
185 
186  // Check parameters
187  if (!trim($this->ref))
188  {
189  $this->error = 'ErrorFieldsRequired';
190  dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
191  return -1;
192  }
193  if (! empty($conf->global->PROJECT_THIRDPARTY_REQUIRED) && ! $this->socid > 0)
194  {
195  $this->error = 'ErrorFieldsRequired';
196  dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
197  return -1;
198  }
199 
200  // Create project
201  $this->db->begin();
202 
203  $sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet (";
204  $sql.= "ref";
205  $sql.= ", title";
206  $sql.= ", description";
207  $sql.= ", fk_soc";
208  $sql.= ", fk_user_creat";
209  $sql.= ", fk_statut";
210  $sql.= ", fk_opp_status";
211  $sql.= ", opp_percent";
212  $sql.= ", public";
213  $sql.= ", datec";
214  $sql.= ", dateo";
215  $sql.= ", datee";
216  $sql.= ", opp_amount";
217  $sql.= ", budget_amount";
218  $sql.= ", bill_time";
219  $sql.= ", note_private";
220  $sql.= ", note_public";
221  $sql.= ", entity";
222  $sql.= ") VALUES (";
223  $sql.= "'" . $this->db->escape($this->ref) . "'";
224  $sql.= ", '" . $this->db->escape($this->title) . "'";
225  $sql.= ", '" . $this->db->escape($this->description) . "'";
226  $sql.= ", " . ($this->socid > 0 ? $this->socid : "null");
227  $sql.= ", " . $user->id;
228  $sql.= ", ".(is_numeric($this->statut) ? $this->statut : '0');
229  $sql.= ", ".(is_numeric($this->opp_status) ? $this->opp_status : 'NULL');
230  $sql.= ", ".(is_numeric($this->opp_percent) ? $this->opp_percent : 'NULL');
231  $sql.= ", " . ($this->public ? 1 : 0);
232  $sql.= ", '".$this->db->idate($now)."'";
233  $sql.= ", " . ($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
234  $sql.= ", " . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
235  $sql.= ", " . (strcmp($this->opp_amount,'') ? price2num($this->opp_amount) : 'null');
236  $sql.= ", " . (strcmp($this->budget_amount,'') ? price2num($this->budget_amount) : 'null');
237  $sql.= ", " . ($this->bill_time ? 1 : 0);
238  $sql.= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null');
239  $sql.= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null');
240  $sql.= ", ".$conf->entity;
241  $sql.= ")";
242 
243  dol_syslog(get_class($this)."::create", LOG_DEBUG);
244  $resql = $this->db->query($sql);
245  if ($resql)
246  {
247  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "projet");
248  $ret = $this->id;
249 
250  if (!$notrigger)
251  {
252  // Call trigger
253  $result=$this->call_trigger('PROJECT_CREATE',$user);
254  if ($result < 0) { $error++; }
255  // End call triggers
256  }
257  }
258  else
259  {
260  $this->error = $this->db->lasterror();
261  $this->errno = $this->db->lasterrno();
262  $error++;
263  }
264 
265  // Update extrafield
266  if (! $error) {
267  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
268  {
269  $result=$this->insertExtraFields();
270  if ($result < 0)
271  {
272  $error++;
273  }
274  }
275  }
276 
277  if (! $error && !empty($conf->global->MAIN_DISABLEDRAFTSTATUS))
278  {
279  $res = $this->setValid($user);
280  if ($res < 0) $error++;
281  }
282 
283  if (! $error)
284  {
285  $this->db->commit();
286  return $ret;
287  }
288  else
289  {
290  $this->db->rollback();
291  return -1;
292  }
293  }
294 
302  function update($user, $notrigger=0)
303  {
304  global $langs, $conf;
305 
306  $error=0;
307 
308  // Clean parameters
309  $this->title = trim($this->title);
310  $this->description = trim($this->description);
311  if ($this->opp_amount < 0) $this->opp_amount='';
312  if ($this->opp_percent < 0) $this->opp_percent='';
313  if ($this->date_end && $this->date_end < $this->date_start)
314  {
315  $this->error = $langs->trans("ErrorDateEndLowerThanDateStart");
316  $this->errors[] = $this->error;
317  $this->db->rollback();
318  dol_syslog(get_class($this)."::update error -3 " . $this->error, LOG_ERR);
319  return -3;
320  }
321 
322  if (dol_strlen(trim($this->ref)) > 0)
323  {
324  $this->db->begin();
325 
326  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet SET";
327  $sql.= " ref='" . $this->db->escape($this->ref) . "'";
328  $sql.= ", title = '" . $this->db->escape($this->title) . "'";
329  $sql.= ", description = '" . $this->db->escape($this->description) . "'";
330  $sql.= ", fk_soc = " . ($this->socid > 0 ? $this->socid : "null");
331  $sql.= ", fk_statut = " . $this->statut;
332  $sql.= ", fk_opp_status = " . ((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null');
333  $sql.= ", opp_percent = " . ((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null');
334  $sql.= ", public = " . ($this->public ? 1 : 0);
335  $sql.= ", datec=" . ($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null');
336  $sql.= ", dateo=" . ($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
337  $sql.= ", datee=" . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
338  $sql.= ", date_close=" . ($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null');
339  $sql.= ", fk_user_close=" . ($this->fk_user_close > 0 ? $this->fk_user_close : "null");
340  $sql.= ", opp_amount = " . (strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null");
341  $sql.= ", budget_amount = " . (strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null");
342  $sql.= ", fk_user_modif = " . $user->id;
343  $sql.= ", bill_time = " . ($this->bill_time ? 1 : 0);
344  $sql.= " WHERE rowid = " . $this->id;
345 
346  dol_syslog(get_class($this)."::update", LOG_DEBUG);
347  $resql=$this->db->query($sql);
348  if ($resql)
349  {
350  // Update extrafield
351  if (! $error)
352  {
353  if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
354  {
355  $result=$this->insertExtraFields();
356  if ($result < 0)
357  {
358  $error++;
359  }
360  }
361  }
362 
363  if (! $error && ! $notrigger)
364  {
365  // Call trigger
366  $result=$this->call_trigger('PROJECT_MODIFY',$user);
367  if ($result < 0) { $error++; }
368  // End call triggers
369  }
370 
371  if (! $error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref))
372  {
373  // We remove directory
374  if ($conf->projet->dir_output)
375  {
376  $olddir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($this->oldcopy->ref);
377  $newdir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($this->ref);
378  if (file_exists($olddir))
379  {
380  include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
381  $res=dol_move($olddir, $newdir);
382  if (! $res)
383  {
384  $langs->load("errors");
385  $this->error=$langs->trans('ErrorFailToRenameDir',$olddir,$newdir);
386  $error++;
387  }
388  }
389  }
390  }
391  if (! $error )
392  {
393  $this->db->commit();
394  $result = 1;
395  }
396  else
397  {
398  $this->db->rollback();
399  $result = -1;
400  }
401  }
402  else
403  {
404  $this->error = $this->db->lasterror();
405  $this->errors[] = $this->error;
406  $this->db->rollback();
407  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
408  {
409  $result = -4;
410  }
411  else
412  {
413  $result = -2;
414  }
415  dol_syslog(get_class($this)."::update error " . $result . " " . $this->error, LOG_ERR);
416  }
417  }
418  else
419  {
420  dol_syslog(get_class($this)."::update ref null");
421  $result = -1;
422  }
423 
424  return $result;
425  }
426 
434  function fetch($id, $ref='')
435  {
436  global $conf;
437 
438  if (empty($id) && empty($ref)) return -1;
439 
440  $sql = "SELECT rowid, ref, title, description, public, datec, opp_amount, budget_amount,";
441  $sql.= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent,";
442  $sql.= " note_private, note_public, model_pdf, bill_time";
443  $sql.= " FROM " . MAIN_DB_PREFIX . "projet";
444  if (! empty($id))
445  {
446  $sql.= " WHERE rowid=".$id;
447  }
448  else if (! empty($ref))
449  {
450  $sql.= " WHERE ref='".$this->db->escape($ref)."'";
451  $sql.= " AND entity IN (".getEntity('project').")";
452  }
453 
454  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
455  $resql = $this->db->query($sql);
456  if ($resql)
457  {
458  $num_rows = $this->db->num_rows($resql);
459 
460  if ($num_rows)
461  {
462  $obj = $this->db->fetch_object($resql);
463 
464  $this->id = $obj->rowid;
465  $this->ref = $obj->ref;
466  $this->title = $obj->title;
467  $this->titre = $obj->title; // TODO deprecated
468  $this->description = $obj->description;
469  $this->date_c = $this->db->jdate($obj->datec);
470  $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
471  $this->date_m = $this->db->jdate($obj->tms);
472  $this->datem = $this->db->jdate($obj->tms); // TODO deprecated
473  $this->date_start = $this->db->jdate($obj->dateo);
474  $this->date_end = $this->db->jdate($obj->datee);
475  $this->date_close = $this->db->jdate($obj->date_close);
476  $this->note_private = $obj->note_private;
477  $this->note_public = $obj->note_public;
478  $this->socid = $obj->fk_soc;
479  $this->user_author_id = $obj->fk_user_creat;
480  $this->user_modification_id = $obj->fk_user_modif;
481  $this->user_close_id = $obj->fk_user_close;
482  $this->public = $obj->public;
483  $this->statut = $obj->fk_statut;
484  $this->opp_status = $obj->fk_opp_status;
485  $this->opp_amount = $obj->opp_amount;
486  $this->opp_percent = $obj->opp_percent;
487  $this->budget_amount = $obj->budget_amount;
488  $this->modelpdf = $obj->model_pdf;
489  $this->bill_time = (int) $obj->bill_time;
490 
491  $this->db->free($resql);
492 
493  // Retreive all extrafield
494  // fetch optionals attributes and labels
495  $this->fetch_optionals();
496 
497  return 1;
498  }
499 
500  $this->db->free($resql);
501 
502  return 0;
503  }
504  else
505  {
506  $this->error = $this->db->lasterror();
507  return -1;
508  }
509  }
510 
511  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
518  function liste_array($socid='')
519  {
520  // phpcs:enable
521  global $conf;
522 
523  $projects = array();
524 
525  $sql = "SELECT rowid, title";
526  $sql.= " FROM " . MAIN_DB_PREFIX . "projet";
527  $sql.= " WHERE entity = " . $conf->entity;
528  if (! empty($socid)) $sql.= " AND fk_soc = " . $socid;
529 
530  $resql = $this->db->query($sql);
531  if ($resql)
532  {
533  $nump = $this->db->num_rows($resql);
534 
535  if ($nump)
536  {
537  $i = 0;
538  while ($i < $nump)
539  {
540  $obj = $this->db->fetch_object($resql);
541 
542  $projects[$obj->rowid] = $obj->title;
543  $i++;
544  }
545  }
546  return $projects;
547  }
548  else
549  {
550  print $this->db->lasterror();
551  }
552  }
553 
554  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
566  function get_element_list($type, $tablename, $datefieldname='', $dates='', $datee='', $projectkey='fk_projet')
567  {
568  // phpcs:enable
569  $elements = array();
570 
571  if ($this->id <= 0) return $elements;
572 
573  $ids = $this->id;
574 
575  if ($type == 'agenda')
576  {
577  $sql = "SELECT id as rowid FROM " . MAIN_DB_PREFIX . "actioncomm WHERE fk_project IN (". $ids .") AND entity IN (".getEntity('agenda').")";
578  }
579  elseif ($type == 'expensereport')
580  {
581  $sql = "SELECT ed.rowid FROM " . MAIN_DB_PREFIX . "expensereport as e, " . MAIN_DB_PREFIX . "expensereport_det as ed WHERE e.rowid = ed.fk_expensereport AND e.entity IN (".getEntity('expensereport').") AND ed.fk_projet IN (". $ids .")";
582  }
583  elseif ($type == 'project_task')
584  {
585  $sql = "SELECT DISTINCT pt.rowid FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (". $ids .")";
586  }
587  elseif ($type == 'project_task_time') // Case we want to duplicate line foreach user
588  {
589  $sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (". $ids .")";
590  }
591  elseif ($type == 'stock_mouvement')
592  {
593  $sql = 'SELECT ms.rowid, ms.fk_user_author as fk_user FROM ' . MAIN_DB_PREFIX . "stock_mouvement as ms, " . MAIN_DB_PREFIX . "entrepot as e WHERE e.rowid = ms.fk_entrepot AND e.entity IN (".getEntity('stock').") AND ms.origintype = 'project' AND ms.fk_origin IN (". $ids .") AND ms.type_mouvement = 1";
594  }
595  else
596  {
597  $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $tablename." WHERE ".$projectkey." IN (". $ids .") AND entity IN (".getEntity($type).")";
598  }
599 
600  if ($dates > 0)
601  {
602  if (empty($datefieldname) && ! empty($this->table_element_date)) $datefieldname=$this->table_element_date;
603  if (empty($datefieldname)) return 'Error this object has no date field defined';
604  $sql.=" AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
605  }
606  if ($datee > 0)
607  {
608  if (empty($datefieldname) && ! empty($this->table_element_date)) $datefieldname=$this->table_element_date;
609  if (empty($datefieldname)) return 'Error this object has no date field defined';
610  $sql.=" AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
611  }
612  if (! $sql) return -1;
613 
614  //print $sql;
615  dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG);
616  $result = $this->db->query($sql);
617  if ($result)
618  {
619  $nump = $this->db->num_rows($result);
620  if ($nump)
621  {
622  $i = 0;
623  while ($i < $nump)
624  {
625  $obj = $this->db->fetch_object($result);
626 
627  $elements[$i] = $obj->rowid.(empty($obj->fk_user)?'':'_'.$obj->fk_user);
628 
629  $i++;
630  }
631  $this->db->free($result);
632  }
633 
634  /* Return array even if empty*/
635  return $elements;
636  }
637  else
638  {
639  dol_print_error($this->db);
640  }
641  }
642 
650  function delete($user, $notrigger=0)
651  {
652  global $langs, $conf;
653  require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
654 
655  $error = 0;
656 
657  $this->db->begin();
658 
659  if (!$error)
660  {
661  // Delete linked contacts
662  $res = $this->delete_linked_contact();
663  if ($res < 0)
664  {
665  $this->error = 'ErrorFailToDeleteLinkedContact';
666  //$error++;
667  $this->db->rollback();
668  return 0;
669  }
670  }
671 
672  // Set fk_projet into elements to null
673  $listoftables=array(
674  'propal'=>'fk_projet', 'commande'=>'fk_projet', 'facture'=>'fk_projet',
675  'supplier_proposal'=>'fk_projet', 'commande_fournisseur'=>'fk_projet', 'facture_fourn'=>'fk_projet',
676  'expensereport_det'=>'fk_projet', 'contrat'=>'fk_projet', 'fichinter'=>'fk_projet', 'don'=>'fk_projet',
677  'actioncomm'=>'fk_project'
678  );
679  foreach($listoftables as $key => $value)
680  {
681  $sql = "UPDATE " . MAIN_DB_PREFIX . $key . " SET ".$value." = NULL where ".$value." = ". $this->id;
682  $resql = $this->db->query($sql);
683  if (!$resql)
684  {
685  $this->errors[] = $this->db->lasterror();
686  $error++;
687  break;
688  }
689  }
690 
691  // Fetch tasks
692  $this->getLinesArray($user);
693 
694  // Delete tasks
695  $ret = $this->deleteTasks($user);
696  if ($ret < 0) $error++;
697 
698  // Delete project
699  if (! $error)
700  {
701  $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet";
702  $sql.= " WHERE rowid=" . $this->id;
703 
704  $resql = $this->db->query($sql);
705  if (!$resql)
706  {
707  $this->errors[] = $langs->trans("CantRemoveProject");
708  $error++;
709  }
710  }
711 
712  if (! $error)
713  {
714  $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_extrafields";
715  $sql.= " WHERE fk_object=" . $this->id;
716 
717  $resql = $this->db->query($sql);
718  if (! $resql)
719  {
720  $this->errors[] = $this->db->lasterror();
721  $error++;
722  }
723  }
724 
725  if (empty($error))
726  {
727  // We remove directory
728  $projectref = dol_sanitizeFileName($this->ref);
729  if ($conf->projet->dir_output)
730  {
731  $dir = $conf->projet->dir_output . "/" . $projectref;
732  if (file_exists($dir))
733  {
734  $res = @dol_delete_dir_recursive($dir);
735  if (!$res)
736  {
737  $this->errors[] = 'ErrorFailToDeleteDir';
738  $error++;
739  }
740  }
741  }
742 
743  if (!$notrigger)
744  {
745  // Call trigger
746  $result=$this->call_trigger('PROJECT_DELETE',$user);
747 
748  if ($result < 0) {
749  $error++;
750  }
751  // End call triggers
752  }
753  }
754 
755  if (empty($error))
756  {
757  $this->db->commit();
758  return 1;
759  }
760  else
761  {
762  foreach ( $this->errors as $errmsg )
763  {
764  dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
765  $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
766  }
767  dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
768  $this->db->rollback();
769  return -1;
770  }
771  }
772 
779  function deleteTasks($user)
780  {
781  $countTasks = count($this->lines);
782  $deleted = false;
783  if ($countTasks)
784  {
785  foreach($this->lines as $task)
786  {
787  if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them)
788  $deleted = true;
789  $ret = $task->delete($user);
790  if ($ret <= 0)
791  {
792  $this->errors[] = $this->db->lasterror();
793  return -1;
794  }
795  }
796  }
797  }
798  $this->getLinesArray($user);
799  if ($deleted && count($this->lines) < $countTasks)
800  {
801  if (count($this->lines)) $this->deleteTasks($this->lines);
802  }
803 
804  return 1;
805  }
806 
814  function setValid($user, $notrigger=0)
815  {
816  global $langs, $conf;
817 
818  $error=0;
819 
820  if ($this->statut != 1)
821  {
822  // Check parameters
823  if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title))
824  {
825  $this->error=$langs->trans("ErrorFieldFormat",$langs->transnoentities("Label")).'. '.$langs->trans('RemoveString',$langs->transnoentitiesnoconv("CopyOf"));
826  return -1;
827  }
828 
829  $this->db->begin();
830 
831  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet";
832  $sql.= " SET fk_statut = 1";
833  $sql.= " WHERE rowid = " . $this->id;
834  $sql.= " AND entity = " . $conf->entity;
835 
836  dol_syslog(get_class($this)."::setValid", LOG_DEBUG);
837  $resql = $this->db->query($sql);
838  if ($resql)
839  {
840  // Call trigger
841  if (empty($notrigger))
842  {
843  $result=$this->call_trigger('PROJECT_VALIDATE',$user);
844  if ($result < 0) { $error++; }
845  // End call triggers
846  }
847 
848  if (!$error)
849  {
850  $this->statut=1;
851  $this->db->commit();
852  return 1;
853  }
854  else
855  {
856  $this->db->rollback();
857  $this->error = join(',', $this->errors);
858  dol_syslog(get_class($this)."::setValid " . $this->error, LOG_ERR);
859  return -1;
860  }
861  }
862  else
863  {
864  $this->db->rollback();
865  $this->error = $this->db->lasterror();
866  return -1;
867  }
868  }
869  }
870 
877  function setClose($user)
878  {
879  global $langs, $conf;
880 
881  $now = dol_now();
882 
883  $error=0;
884 
885  if ($this->statut != 2)
886  {
887  $this->db->begin();
888 
889  $sql = "UPDATE " . MAIN_DB_PREFIX . "projet";
890  $sql.= " SET fk_statut = 2, fk_user_close = ".$user->id.", date_close = '".$this->db->idate($now)."'";
891  $sql.= " WHERE rowid = " . $this->id;
892  $sql.= " AND entity = " . $conf->entity;
893  $sql.= " AND fk_statut = 1";
894 
895  if (! empty($conf->global->PROJECT_USE_OPPORTUNITIES))
896  {
897  // TODO What to do if fk_opp_status is not code 'WON' or 'LOST'
898  }
899 
900  dol_syslog(get_class($this)."::setClose", LOG_DEBUG);
901  $resql = $this->db->query($sql);
902  if ($resql)
903  {
904  // Call trigger
905  $result=$this->call_trigger('PROJECT_CLOSE',$user);
906  if ($result < 0) { $error++; }
907  // End call triggers
908 
909  if (!$error)
910  {
911  $this->statut = 2;
912  $this->db->commit();
913  return 1;
914  }
915  else
916  {
917  $this->db->rollback();
918  $this->error = join(',', $this->errors);
919  dol_syslog(get_class($this)."::setClose " . $this->error, LOG_ERR);
920  return -1;
921  }
922  }
923  else
924  {
925  $this->db->rollback();
926  $this->error = $this->db->lasterror();
927  return -1;
928  }
929  }
930 
931  return 0;
932  }
933 
940  function getLibStatut($mode=0)
941  {
942  return $this->LibStatut($this->statut, $mode);
943  }
944 
945  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
953  function LibStatut($statut, $mode=0)
954  {
955  // phpcs:enable
956  global $langs;
957 
958  if ($mode == 0)
959  {
960  return $langs->trans($this->statuts_long[$statut]);
961  }
962  if ($mode == 1)
963  {
964  return $langs->trans($this->statuts_short[$statut]);
965  }
966  if ($mode == 2)
967  {
968  if ($statut == 0)
969  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0') . ' ' . $langs->trans($this->statuts_short[$statut]);
970  if ($statut == 1)
971  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4') . ' ' . $langs->trans($this->statuts_short[$statut]);
972  if ($statut == 2)
973  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6') . ' ' . $langs->trans($this->statuts_short[$statut]);
974  }
975  if ($mode == 3)
976  {
977  if ($statut == 0)
978  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0');
979  if ($statut == 1)
980  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4');
981  if ($statut == 2)
982  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6');
983  }
984  if ($mode == 4)
985  {
986  if ($statut == 0)
987  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0') . ' ' . $langs->trans($this->statuts_long[$statut]);
988  if ($statut == 1)
989  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4') . ' ' . $langs->trans($this->statuts_long[$statut]);
990  if ($statut == 2)
991  return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6') . ' ' . $langs->trans($this->statuts_long[$statut]);
992  }
993  if ($mode == 5)
994  {
995  if ($statut == 0)
996  return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut0');
997  if ($statut == 1)
998  return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut4');
999  if ($statut == 2)
1000  return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut6');
1001  }
1002  }
1003 
1016  function getNomUrl($withpicto=0, $option='', $addlabel=0, $moreinpopup='', $sep=' - ', $notooltip=0, $save_lastsearch_value=-1)
1017  {
1018  global $conf, $langs, $user, $hookmanager;
1019 
1020  if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips
1021 
1022  $result = '';
1023 
1024  $label='';
1025  if ($option != 'nolink') $label = '<u>' . $langs->trans("ShowProject") . '</u>';
1026  $label .= ($label?'<br>':'').'<b>' . $langs->trans('Ref') . ': </b>' . $this->ref; // The space must be after the : to not being explode when showing the title in img_picto
1027  $label .= ($label?'<br>':'').'<b>' . $langs->trans('Label') . ': </b>' . $this->title; // The space must be after the : to not being explode when showing the title in img_picto
1028  if (! empty($this->thirdparty_name))
1029  $label .= ($label?'<br>':'').'<b>' . $langs->trans('ThirdParty') . ': </b>' . $this->thirdparty_name; // The space must be after the : to not being explode when showing the title in img_picto
1030  if (! empty($this->dateo))
1031  $label .= ($label?'<br>':'').'<b>' . $langs->trans('DateStart') . ': </b>' . dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto
1032  if (! empty($this->datee))
1033  $label .= ($label?'<br>':'').'<b>' . $langs->trans('DateEnd') . ': </b>' . dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto
1034  if ($moreinpopup) $label.='<br>'.$moreinpopup;
1035 
1036  $url='';
1037  if ($option != 'nolink')
1038  {
1039  if (preg_match('/\.php$/',$option)) {
1040  $url = dol_buildpath($option,1) . '?id=' . $this->id;
1041  }
1042  else if ($option == 'task')
1043  {
1044  $url = DOL_URL_ROOT . '/projet/tasks.php?id=' . $this->id;
1045  }
1046  else
1047  {
1048  $url = DOL_URL_ROOT . '/projet/card.php?id=' . $this->id;
1049  }
1050  // Add param to save lastsearch_values or not
1051  $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1052  if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1053  if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1054  }
1055 
1056  $linkclose='';
1057  if (empty($notooltip) && $user->rights->projet->lire)
1058  {
1059  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1060  {
1061  $label=$langs->trans("ShowProject");
1062  $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1063  }
1064  $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"';
1065  $linkclose.=' class="classfortooltip"';
1066 
1067  /*
1068  $hookmanager->initHooks(array('projectdao'));
1069  $parameters=array('id'=>$this->id);
1070  // Note that $action and $object may have been modified by some hooks
1071  $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action);
1072  if ($reshook > 0)
1073  $linkclose = $hookmanager->resPrint;
1074  */
1075  }
1076 
1077  $picto = 'projectpub';
1078  if (! $this->public) $picto = 'project';
1079 
1080  $linkstart = '<a href="'.$url.'"';
1081  $linkstart.=$linkclose.'>';
1082  $linkend='</a>';
1083 
1084  $result .= $linkstart;
1085  if ($withpicto) $result.=img_object(($notooltip?'':$label), $picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1086  if ($withpicto != 2) $result.= $this->ref;
1087  $result .= $linkend;
1088  if ($withpicto != 2) $result.=(($addlabel && $this->title) ? $sep . dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : '');
1089 
1090  global $action;
1091  $hookmanager->initHooks(array('projectdao'));
1092  $parameters=array('id'=>$this->id, 'getnomurl'=>$result);
1093  $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
1094  if ($reshook > 0) $result = $hookmanager->resPrint;
1095  else $result .= $hookmanager->resPrint;
1096 
1097  return $result;
1098  }
1099 
1107  function initAsSpecimen()
1108  {
1109  global $user, $langs, $conf;
1110 
1111  $now=dol_now();
1112 
1113  // Initialise parameters
1114  $this->id = 0;
1115  $this->ref = 'SPECIMEN';
1116  $this->specimen = 1;
1117  $this->socid = 1;
1118  $this->date_c = $now;
1119  $this->date_m = $now;
1120  $this->date_start = $now;
1121  $this->date_end = $now + (3600 * 24 * 365);
1122  $this->note_public = 'SPECIMEN';
1123  $this->fk_ele = 20000;
1124  $this->opp_amount = 20000;
1125  $this->budget_amount = 10000;
1126 
1127  /*
1128  $nbp = mt_rand(1, 9);
1129  $xnbp = 0;
1130  while ($xnbp < $nbp)
1131  {
1132  $line = new Task($this->db);
1133  $line->fk_project = 0;
1134  $line->label = $langs->trans("Label") . " " . $xnbp;
1135  $line->description = $langs->trans("Description") . " " . $xnbp;
1136 
1137  $this->lines[]=$line;
1138  $xnbp++;
1139  }
1140  */
1141  }
1142 
1150  function restrictedProjectArea($user, $mode='read')
1151  {
1152  // To verify role of users
1153  $userAccess = 0;
1154  if (($mode == 'read' && ! empty($user->rights->projet->all->lire)) || ($mode == 'write' && ! empty($user->rights->projet->all->creer)) || ($mode == 'delete' && ! empty($user->rights->projet->all->supprimer)))
1155  {
1156  $userAccess = 1;
1157  }
1158  else if ($this->public && (($mode == 'read' && ! empty($user->rights->projet->lire)) || ($mode == 'write' && ! empty($user->rights->projet->creer)) || ($mode == 'delete' && ! empty($user->rights->projet->supprimer))))
1159  {
1160  $userAccess = 1;
1161  }
1162  else
1163  {
1164  foreach (array('internal', 'external') as $source)
1165  {
1166  $userRole = $this->liste_contact(4, $source);
1167  $num = count($userRole);
1168 
1169  $nblinks = 0;
1170  while ($nblinks < $num)
1171  {
1172  if ($source == 'internal' && preg_match('/^PROJECT/', $userRole[$nblinks]['code']) && $user->id == $userRole[$nblinks]['id'])
1173  {
1174  if ($mode == 'read' && $user->rights->projet->lire) $userAccess++;
1175  if ($mode == 'write' && $user->rights->projet->creer) $userAccess++;
1176  if ($mode == 'delete' && $user->rights->projet->supprimer) $userAccess++;
1177  }
1178  $nblinks++;
1179  }
1180  }
1181  //if (empty($nblinks)) // If nobody has permission, we grant creator
1182  //{
1183  // if ((!empty($this->user_author_id) && $this->user_author_id == $user->id))
1184  // {
1185  // $userAccess = 1;
1186  // }
1187  //}
1188  }
1189 
1190  return ($userAccess?$userAccess:-1);
1191  }
1192 
1203  function getProjectsAuthorizedForUser($user, $mode=0, $list=0, $socid=0, $filter='')
1204  {
1205  $projects = array();
1206  $temp = array();
1207 
1208  $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref";
1209  $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p";
1210  if ($mode == 0 || $mode == 1)
1211  {
1212  $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec";
1213  }
1214  $sql.= " WHERE p.entity IN (".getEntity('project').")";
1215  // Internal users must see project he is contact to even if project linked to a third party he can't see.
1216  //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1217  if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")";
1218 
1219  // Get id of types of contacts for projects (This list never contains a lot of elements)
1220  $listofprojectcontacttype=array();
1221  $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
1222  $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'";
1223  $sql2.= " AND ctc.source = 'internal'";
1224  $resql = $this->db->query($sql2);
1225  if ($resql)
1226  {
1227  while($obj = $this->db->fetch_object($resql))
1228  {
1229  $listofprojectcontacttype[$obj->rowid]=$obj->code;
1230  }
1231  }
1232  else dol_print_error($this->db);
1233  if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found
1234 
1235  if ($mode == 0)
1236  {
1237  $sql.= " AND ec.element_id = p.rowid";
1238  $sql.= " AND ( p.public = 1";
1239  $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")";
1240  $sql.= " AND ec.fk_socpeople = ".$user->id.")";
1241  $sql.= " )";
1242  }
1243  if ($mode == 1)
1244  {
1245  $sql.= " AND ec.element_id = p.rowid";
1246  $sql.= " AND (";
1247  $sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")";
1248  $sql.= " AND ec.fk_socpeople = ".$user->id.")";
1249  $sql.= " )";
1250  }
1251  if ($mode == 2)
1252  {
1253  // No filter. Use this if user has permission to see all project
1254  }
1255 
1256  $sql.= $filter;
1257  //print $sql;
1258 
1259  $resql = $this->db->query($sql);
1260  if ($resql)
1261  {
1262  $num = $this->db->num_rows($resql);
1263  $i = 0;
1264  while ($i < $num)
1265  {
1266  $row = $this->db->fetch_row($resql);
1267  $projects[$row[0]] = $row[1];
1268  $temp[] = $row[0];
1269  $i++;
1270  }
1271 
1272  $this->db->free($resql);
1273 
1274  if ($list)
1275  {
1276  if (empty($temp)) return '0';
1277  $result = implode(',', $temp);
1278  return $result;
1279  }
1280  }
1281  else
1282  {
1283  dol_print_error($this->db);
1284  }
1285 
1286  return $projects;
1287  }
1288 
1303  function createFromClone($fromid,$clone_contact=false,$clone_task=true,$clone_project_file=false,$clone_task_file=false,$clone_note=true,$move_date=true,$notrigger=0,$newthirdpartyid=0)
1304  {
1305  global $user,$langs,$conf;
1306 
1307  $error=0;
1308 
1309  dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date,LOG_DEBUG);
1310 
1311  $now = dol_mktime(0,0,0,idate('m',dol_now()),idate('d',dol_now()),idate('Y',dol_now()));
1312 
1313  $clone_project=new Project($this->db);
1314 
1315  $clone_project->context['createfromclone']='createfromclone';
1316 
1317  $this->db->begin();
1318 
1319  // Load source object
1320  $clone_project->fetch($fromid);
1321  $clone_project->fetch_optionals();
1322  if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid;
1323  $clone_project->fetch_thirdparty();
1324 
1325  $orign_dt_start=$clone_project->date_start;
1326  $orign_project_ref=$clone_project->ref;
1327 
1328  $clone_project->id=0;
1329  if ($move_date) {
1330  $clone_project->date_start = $now;
1331  if (!(empty($clone_project->date_end)))
1332  {
1333  $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start);
1334  }
1335  }
1336 
1337  $clone_project->datec = $now;
1338 
1339  if (! $clone_note)
1340  {
1341  $clone_project->note_private='';
1342  $clone_project->note_public='';
1343  }
1344 
1345  //Generate next ref
1346  $defaultref='';
1347  $obj = empty($conf->global->PROJECT_ADDON)?'mod_project_simple':$conf->global->PROJECT_ADDON;
1348  // Search template files
1349  $file=''; $classname=''; $filefound=0;
1350  $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']);
1351  foreach($dirmodels as $reldir)
1352  {
1353  $file=dol_buildpath($reldir."core/modules/project/".$obj.'.php',0);
1354  if (file_exists($file))
1355  {
1356  $filefound=1;
1357  dol_include_once($reldir."core/modules/project/".$obj.'.php');
1358  $modProject = new $obj;
1359  $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty)?$clone_project->thirdparty:null, $clone_project);
1360  break;
1361  }
1362  }
1363  if (is_numeric($defaultref) && $defaultref <= 0) $defaultref='';
1364 
1365  $clone_project->ref=$defaultref;
1366  $clone_project->title=$langs->trans("CopyOf").' '.$clone_project->title;
1367 
1368  // Create clone
1369  $result=$clone_project->create($user,$notrigger);
1370 
1371  // Other options
1372  if ($result < 0)
1373  {
1374  $this->error.=$clone_project->error;
1375  $error++;
1376  }
1377 
1378  if (! $error)
1379  {
1380  //Get the new project id
1381  $clone_project_id=$clone_project->id;
1382 
1383  //Note Update
1384  if (!$clone_note)
1385  {
1386  $clone_project->note_private='';
1387  $clone_project->note_public='';
1388  }
1389  else
1390  {
1391  $this->db->begin();
1392  $res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES),'_public');
1393  if ($res < 0)
1394  {
1395  $this->error.=$clone_project->error;
1396  $error++;
1397  $this->db->rollback();
1398  }
1399  else
1400  {
1401  $this->db->commit();
1402  }
1403 
1404  $this->db->begin();
1405  $res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private');
1406  if ($res < 0)
1407  {
1408  $this->error.=$clone_project->error;
1409  $error++;
1410  $this->db->rollback();
1411  }
1412  else
1413  {
1414  $this->db->commit();
1415  }
1416  }
1417 
1418  //Duplicate contact
1419  if ($clone_contact)
1420  {
1421  $origin_project = new Project($this->db);
1422  $origin_project->fetch($fromid);
1423 
1424  foreach(array('internal','external') as $source)
1425  {
1426  $tab = $origin_project->liste_contact(-1,$source);
1427 
1428  foreach ($tab as $contacttoadd)
1429  {
1430  $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'],$notrigger);
1431  if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS')
1432  {
1433  $langs->load("errors");
1434  $this->error.=$langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1435  $error++;
1436  }
1437  else
1438  {
1439  if ($clone_project->error!='')
1440  {
1441  $this->error.=$clone_project->error;
1442  $error++;
1443  }
1444  }
1445  }
1446  }
1447  }
1448 
1449  //Duplicate file
1450  if ($clone_project_file)
1451  {
1452  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1453 
1454  $clone_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($defaultref);
1455  $ori_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($orign_project_ref);
1456 
1457  if (dol_mkdir($clone_project_dir) >= 0)
1458  {
1459  $filearray=dol_dir_list($ori_project_dir,"files",0,'','(\.meta|_preview.*\.png)$','',SORT_ASC,1);
1460  foreach($filearray as $key => $file)
1461  {
1462  $rescopy = dol_copy($ori_project_dir . '/' . $file['name'], $clone_project_dir . '/' . $file['name'],0,1);
1463  if (is_numeric($rescopy) && $rescopy < 0)
1464  {
1465  $this->error.=$langs->trans("ErrorFailToCopyFile",$ori_project_dir . '/' . $file['name'],$clone_project_dir . '/' . $file['name']);
1466  $error++;
1467  }
1468  }
1469  }
1470  else
1471  {
1472  $this->error.=$langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1473  $error++;
1474  }
1475  }
1476 
1477  //Duplicate task
1478  if ($clone_task)
1479  {
1480  require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php';
1481 
1482  $taskstatic = new Task($this->db);
1483 
1484  // Security check
1485  $socid=0;
1486  if ($user->societe_id > 0) $socid = $user->societe_id;
1487 
1488  $tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0);
1489 
1490  $tab_conv_child_parent=array();
1491 
1492  // Loop on each task, to clone it
1493  foreach ($tasksarray as $tasktoclone)
1494  {
1495  $result_clone = $taskstatic->createFromClone($tasktoclone->id,$clone_project_id,$tasktoclone->fk_parent,$move_date,true,false,$clone_task_file,true,false);
1496  if ($result_clone <= 0)
1497  {
1498  $this->error.=$result_clone->error;
1499  $error++;
1500  }
1501  else
1502  {
1503  $new_task_id=$result_clone;
1504  $taskstatic->fetch($tasktoclone->id);
1505 
1506  //manage new parent clone task id
1507  // if the current task has child we store the original task id and the equivalent clone task id
1508  if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id,$tab_conv_child_parent))
1509  {
1510  $tab_conv_child_parent[$tasktoclone->id] = $new_task_id;
1511  }
1512  }
1513  }
1514 
1515  //Parse all clone node to be sure to update new parent
1516  $tasksarray=$taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0);
1517  foreach ($tasksarray as $task_cloned)
1518  {
1519  $taskstatic->fetch($task_cloned->id);
1520  if ($taskstatic->fk_task_parent!=0)
1521  {
1522  $taskstatic->fk_task_parent=$tab_conv_child_parent[$taskstatic->fk_task_parent];
1523  }
1524  $res=$taskstatic->update($user,$notrigger);
1525  if ($result_clone <= 0)
1526  {
1527  $this->error.=$taskstatic->error;
1528  $error++;
1529  }
1530  }
1531  }
1532  }
1533 
1534  unset($clone_project->context['createfromclone']);
1535 
1536  if (! $error)
1537  {
1538  $this->db->commit();
1539  return $clone_project_id;
1540  }
1541  else
1542  {
1543  $this->db->rollback();
1544  dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : " . $this->error, LOG_ERR);
1545  return -1;
1546  }
1547  }
1548 
1549 
1556  function shiftTaskDate($old_project_dt_start)
1557  {
1558  global $user,$langs,$conf;
1559 
1560  $error=0;
1561 
1562  $taskstatic = new Task($this->db);
1563 
1564  // Security check
1565  $socid=0;
1566  if ($user->societe_id > 0) $socid = $user->societe_id;
1567 
1568  $tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1569 
1570  foreach ($tasksarray as $tasktoshiftdate)
1571  {
1572  $to_update=false;
1573  // Fetch only if update of date will be made
1574  if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end)))
1575  {
1576  //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1577  $to_update=true;
1578  $task = new Task($this->db);
1579  $result = $task->fetch($tasktoshiftdate->id);
1580  if (!$result)
1581  {
1582  $error++;
1583  $this->error.=$task->error;
1584  }
1585  }
1586  //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1587 
1588  //Calcultate new task start date with difference between old proj start date and origin task start date
1589  if (!empty($tasktoshiftdate->date_start))
1590  {
1591  $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
1592  }
1593 
1594  //Calcultate new task end date with difference between origin proj end date and origin task end date
1595  if (!empty($tasktoshiftdate->date_end))
1596  {
1597  $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1598  }
1599 
1600  if ($to_update)
1601  {
1602  $result = $task->update($user);
1603  if (!$result)
1604  {
1605  $error++;
1606  $this->error.=$task->error;
1607  }
1608  }
1609  }
1610  if ($error!=0)
1611  {
1612  return -1;
1613  }
1614  return $result;
1615  }
1616 
1617 
1618  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1626  function update_element($tableName, $elementSelectId)
1627  {
1628  // phpcs:enable
1629  $sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1630 
1631  if ($tableName == "actioncomm")
1632  {
1633  $sql.= " SET fk_project=".$this->id;
1634  $sql.= " WHERE id=".$elementSelectId;
1635  }
1636  else
1637  {
1638  $sql.= " SET fk_projet=".$this->id;
1639  $sql.= " WHERE rowid=".$elementSelectId;
1640  }
1641 
1642  dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
1643  $resql=$this->db->query($sql);
1644  if (!$resql) {
1645  $this->error=$this->db->lasterror();
1646  return -1;
1647  }else {
1648  return 1;
1649  }
1650  }
1651 
1652  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1660  function remove_element($tableName, $elementSelectId)
1661  {
1662  // phpcs:enable
1663  $sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1664 
1665  if ($TableName=="actioncomm")
1666  {
1667  $sql.= " SET fk_project=NULL";
1668  $sql.= " WHERE id=".$elementSelectId;
1669  }
1670  else
1671  {
1672  $sql.= " SET fk_projet=NULL";
1673  $sql.= " WHERE rowid=".$elementSelectId;
1674  }
1675 
1676  dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
1677  $resql=$this->db->query($sql);
1678  if (!$resql) {
1679  $this->error=$this->db->lasterror();
1680  return -1;
1681  }else {
1682  return 1;
1683  }
1684  }
1685 
1696  public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
1697  {
1698  global $conf,$langs;
1699 
1700  $langs->load("projects");
1701 
1702  if (! dol_strlen($modele)) {
1703 
1704  $modele = 'baleine';
1705 
1706  if ($this->modelpdf) {
1707  $modele = $this->modelpdf;
1708  } elseif (! empty($conf->global->PROJECT_ADDON_PDF)) {
1709  $modele = $conf->global->PROJECT_ADDON_PDF;
1710  }
1711  }
1712 
1713  $modelpath = "core/modules/project/doc/";
1714 
1715  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1716  }
1717 
1718 
1728  public function loadTimeSpent($datestart, $taskid=0, $userid=0)
1729  {
1730  $error=0;
1731 
1732  $this->weekWorkLoad=array();
1733  $this->weekWorkLoadPerTask=array();
1734 
1735  if (empty($datestart)) dol_print_error('','Error datestart parameter is empty');
1736 
1737  $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task";
1738  $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
1739  $sql.= " WHERE ptt.fk_task = pt.rowid";
1740  $sql.= " AND pt.fk_projet = ".$this->id;
1741  $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' ";
1742  $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
1743  if ($task_id) $sql.= " AND ptt.fk_task=".$taskid;
1744  if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid;
1745 
1746  //print $sql;
1747  $resql=$this->db->query($sql);
1748  if ($resql)
1749  {
1750  $daylareadyfound=array();
1751 
1752  $num = $this->db->num_rows($resql);
1753  $i = 0;
1754  // Loop on each record found, so each couple (project id, task id)
1755  while ($i < $num)
1756  {
1757  $obj=$this->db->fetch_object($resql);
1758  $day=$this->db->jdate($obj->task_date); // task_date is date without hours
1759  if (empty($daylareadyfound[$day]))
1760  {
1761  $this->weekWorkLoad[$day] = $obj->task_duration;
1762  $this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration;
1763  }
1764  else
1765  {
1766  $this->weekWorkLoad[$day] += $obj->task_duration;
1767  $this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration;
1768  }
1769  $daylareadyfound[$day]=1;
1770  $i++;
1771  }
1772  $this->db->free($resql);
1773  return 1;
1774  }
1775  else
1776  {
1777  $this->error="Error ".$this->db->lasterror();
1778  dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
1779  return -1;
1780  }
1781  }
1782 
1783 
1784  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1791  function load_board($user)
1792  {
1793  // phpcs:enable
1794  global $conf, $langs;
1795 
1796  // For external user, no check is done on company because readability is managed by public status of project and assignement.
1797  //$socid=$user->societe_id;
1798 
1799  if (! $user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user,0,1,$socid);
1800 
1801  $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
1802  $sql.= " FROM (".MAIN_DB_PREFIX."projet as p";
1803  $sql.= ")";
1804  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
1805  // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1806  //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
1807  $sql.= " WHERE p.fk_statut = 1";
1808  $sql.= " AND p.entity IN (".getEntity('project').')';
1809  if (! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
1810  // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
1811  //if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1812  // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1813  //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))";
1814 
1815  //print $sql;
1816  $resql=$this->db->query($sql);
1817  if ($resql)
1818  {
1819  $project_static = new Project($this->db);
1820 
1821  $response = new WorkboardResponse();
1822  $response->warning_delay = $conf->projet->warning_delay/60/60/24;
1823  $response->label = $langs->trans("OpenedProjects");
1824  if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
1825  else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
1826  $response->img = img_object('',"projectpub");
1827 
1828  // This assignment in condition is not a bug. It allows walking the results.
1829  while ($obj=$this->db->fetch_object($resql))
1830  {
1831  $response->nbtodo++;
1832 
1833  $project_static->statut = $obj->status;
1834  $project_static->opp_status = $obj->opp_status;
1835  $project_static->datee = $this->db->jdate($obj->datee);
1836 
1837  if ($project_static->hasDelay()) {
1838  $response->nbtodolate++;
1839  }
1840  }
1841 
1842  return $response;
1843  }
1844  else
1845  {
1846  $this->error=$this->db->error();
1847  return -1;
1848  }
1849  }
1850 
1851 
1860  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1861  {
1862  $tables = array(
1863  'projet'
1864  );
1865 
1866  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1867  }
1868 
1869 
1870  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1876  function load_state_board()
1877  {
1878  // phpcs:enable
1879  global $user;
1880 
1881  $this->nb=array();
1882 
1883  $sql = "SELECT count(p.rowid) as nb";
1884  $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
1885  $sql.= " WHERE";
1886  $sql.= " p.entity IN (".getEntity('project').")";
1887  if (! $user->rights->projet->all->lire)
1888  {
1889  $projectsListId = $this->getProjectsAuthorizedForUser($user,0,1);
1890  $sql .= "AND p.rowid IN (".$projectsListId.")";
1891  }
1892 
1893  $resql=$this->db->query($sql);
1894  if ($resql)
1895  {
1896  while ($obj=$this->db->fetch_object($resql))
1897  {
1898  $this->nb["projects"]=$obj->nb;
1899  }
1900  $this->db->free($resql);
1901  return 1;
1902  }
1903  else
1904  {
1905  dol_print_error($this->db);
1906  $this->error=$this->db->error();
1907  return -1;
1908  }
1909  }
1910 
1911 
1917  public function hasDelay()
1918  {
1919  global $conf;
1920 
1921  if (! ($this->statut == 1)) return false;
1922  if (! $this->datee && ! $this->date_end) return false;
1923 
1924  $now = dol_now();
1925 
1926  return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay);
1927  }
1928 
1929 
1936  function info($id)
1937  {
1938  $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
1939  $sql.= ' date_close as datecloture,';
1940  $sql.= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture';
1941  $sql.= ' FROM '.MAIN_DB_PREFIX.'projet as c';
1942  $sql.= ' WHERE c.rowid = '.$id;
1943  $result=$this->db->query($sql);
1944  if ($result)
1945  {
1946  if ($this->db->num_rows($result))
1947  {
1948  $obj = $this->db->fetch_object($result);
1949  $this->id = $obj->rowid;
1950  if ($obj->fk_user_author)
1951  {
1952  $cuser = new User($this->db);
1953  $cuser->fetch($obj->fk_user_author);
1954  $this->user_creation = $cuser;
1955  }
1956 
1957  if ($obj->fk_user_cloture)
1958  {
1959  $cluser = new User($this->db);
1960  $cluser->fetch($obj->fk_user_cloture);
1961  $this->user_cloture = $cluser;
1962  }
1963 
1964  $this->date_creation = $this->db->jdate($obj->datec);
1965  $this->date_modification = $this->db->jdate($obj->datem);
1966  $this->date_cloture = $this->db->jdate($obj->datecloture);
1967  }
1968 
1969  $this->db->free($result);
1970  }
1971  else
1972  {
1973  dol_print_error($this->db);
1974  }
1975  }
1976 
1987  public function setCategories($categories)
1988  {
1989  // Decode type
1990  $type_id = Categorie::TYPE_PROJECT;
1991  $type_text = 'project';
1992 
1993 
1994  // Handle single category
1995  if (!is_array($categories)) {
1996  $categories = array($categories);
1997  }
1998 
1999  // Get current categories
2000  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2001  $c = new Categorie($this->db);
2002  $existing = $c->containing($this->id, $type_id, 'id');
2003 
2004  // Diff
2005  if (is_array($existing)) {
2006  $to_del = array_diff($existing, $categories);
2007  $to_add = array_diff($categories, $existing);
2008  } else {
2009  $to_del = array(); // Nothing to delete
2010  $to_add = $categories;
2011  }
2012 
2013  // Process
2014  foreach ($to_del as $del) {
2015  if ($c->fetch($del) > 0) {
2016  $result=$c->del_type($this, $type_text);
2017  if ($result<0) {
2018  $this->errors=$c->errors;
2019  $this->error=$c->error;
2020  return -1;
2021  }
2022  }
2023  }
2024  foreach ($to_add as $add) {
2025  if ($c->fetch($add) > 0) {
2026  $result=$c->add_type($this, $type_text);
2027  if ($result<0) {
2028  $this->errors=$c->errors;
2029  $this->error=$c->error;
2030  return -1;
2031  }
2032  }
2033  }
2034 
2035  return 1;
2036  }
2037 
2038 
2045  function getLinesArray($user)
2046  {
2047  require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
2048  $taskstatic = new Task($this->db);
2049 
2050  $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0);
2051  }
2052 }
dol_copy($srcfile, $destfile, $newmask=0, $overwriteifexists=1)
Copy a file to another file.
Definition: files.lib.php:666
load_state_board()
Charge indicateurs this->nb pour le tableau de bord.
print
Draft customers invoices.
Definition: index.php:91
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;...&#39; if string larger than length.
getNomUrl($withpicto=0, $option='', $addlabel=0, $moreinpopup='', $sep=' - ', $notooltip=0, $save_lastsearch_value=-1)
Return clicable name (with picto eventually)
if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) if(! empty($conf->don->enabled) && $user->rights->societe->lire) if(! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) if(! empty($conf->facture->enabled) &&! empty($conf->commande->enabled) && $user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(! empty($conf->facture->enabled) && $user->rights->facture->lire) if(! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1053
</td >< td class="liste_titre" align="right"></td ></tr >< tr class="liste_titre">< input type="checkbox" onClick="toggle(this)"/> Ref p ref Label p label Duration p duration warehouseinternal SELECT description FROM product_lang WHERE qty< br > qty qty qty StockTooLow img yes disabled img no img no< tr class="oddeven">< td >< input type="checkbox" class="check" name="' . $i . '"' . $disabled . '></td >< td >< input type="checkbox" class="check" name="choose'.$i.'"></td >< td class="nowrap"></td >< td >< input type="hidden" name="desc' . $i . '" value="' . dol_escape_htmltag($objp-> description
Only used if Module[ID]Desc translation string is not found.
Definition: replenish.php:573
loadTimeSpent($datestart, $taskid=0, $userid=0)
Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of projec...
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm=false, $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
restrictedProjectArea($user, $mode='read')
Check if user has permission on current project.
initAsSpecimen()
Initialise an instance with random values.
setCategories($categories)
Sets object to supplied categories.
Class to manage Dolibarr users.
Definition: user.class.php:41
Class to manage Dolibarr database access.
deleteTasks($user)
Delete tasks with no children first, then task with children recursively.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
createFromClone($fromid, $clone_contact=false, $clone_task=true, $clone_project_file=false, $clone_task_file=false, $clone_note=true, $move_date=true, $notrigger=0, $newthirdpartyid=0)
Load an object from its id and create a new one in database.
dol_substr($string, $start, $length, $stringencoding='', $trunconbytes=0)
Make a substring.
remove_element($tableName, $elementSelectId)
Associate element to a project.
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
hasDelay()
Is the project delayed?
const STATUS_CLOSED
Closed status.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
liste_array($socid='')
Return list of projects.
fetch($id, $ref='')
Get object from database.
const STATUS_VALIDATED
Open/Validated status.
dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1)
Move a file into another name.
Definition: files.lib.php:814
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
__construct($db)
Constructor.
shiftTaskDate($old_project_dt_start)
Shift project task date from current date to delta.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
Class to manage categories.
getLinesArray($user)
Create an array of tasks of current project.
Class to manage projects.
info($id)
Charge les informations d&#39;ordre info dans l&#39;objet commande.
if(! function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create an intervention document on disk using template defined into PROJECT_ADDON_PDF.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1273
getProjectsAuthorizedForUser($user, $mode=0, $list=0, $socid=0, $filter='')
Return array of projects a user has permission on, is affected to, or all projects.
$user_author_id
Id of project creator. Not defined if shared project.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:59
liste_contact($statut=-1, $source='external', $list=0, $code='')
Get array of all contacts for an object.
dol_now($mode='gmt')
Return date for now.
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...
dol_print_date($time, $format='', $tzoutput='tzserver', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
$public
Tell if this is a public or private project.
get_element_list($type, $tablename, $datefieldname='', $dates='', $datee='', $projectkey='fk_projet')
Return list of elements for type, linked to a project.
Class to manage tasks.
Definition: task.class.php:33
create($user, $notrigger=0)
Create a project into database.
$table_ref_field
{}
dol_mkdir($dir, $dataroot='', $newmask=null)
Creation of a directory (this can create recursive subdir)
update($user, $notrigger=0)
Update a project.
setValid($user, $notrigger=0)
Validate a project.
update_element($tableName, $elementSelectId)
Associate element to a project.
dol_html_entity_decode($a, $b, $c='UTF-8')
Replace html_entity_decode functions to manage errors.
call_trigger($trigger_name, $user)
Call trigger based on this instance.
const STATUS_DRAFT
Draft status.
getLibStatut($mode=0)
Return status label of object.
dol_time_plus_duree($time, $duration_value, $duration_unit)
Add a delay to a date.
Definition: date.lib.php:116
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it&#39;s its name (generic function)
price2num($amount, $rounding='', $alreadysqlnb=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
setClose($user)
Close a project.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
LibStatut($statut, $mode=0)
Renvoi status label for a status.