dolibarr 19.0.3
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-2020 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 * Copyright (C) 2019 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
10 * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
31require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
32
36class Project extends CommonObject
37{
41 public $element = 'project';
42
46 public $table_element = 'projet';
47
51 public $table_element_line = 'projet_task';
52
56 public $table_element_date;
57
61 public $fk_element = 'fk_projet';
62
67 public $ismultientitymanaged = 1;
68
72 public $isextrafieldmanaged = 1;
73
77 public $picto = 'project';
78
82 protected $table_ref_field = 'ref';
83
87 public $fk_project;
88
92 public $description;
93
97 public $title;
98
104 public $dateo;
105
109 public $date_start;
110
116 public $datee;
117
121 public $date_end;
122
126 public $date_start_event;
127
131 public $date_end_event;
132
136 public $location;
137
141 public $date_close;
142
143 public $socid; // To store id of thirdparty
144 public $thirdparty_name; // To store name of thirdparty (defined only in some cases)
145
147
151 public $fk_user_close;
152
153 public $public;
154
158 public $budget_amount;
159
163 public $usage_opportunity;
164
168 public $usage_task;
169
173 public $usage_bill_time; // Is the time spent on project must be invoiced or not
174
178 public $usage_organize_event;
179
183 public $accept_conference_suggestions;
184
188 public $accept_booth_suggestions;
189
193 public $price_registration;
194
198 public $price_booth;
199
203 public $max_attendees;
204
205 public $labelStatusShort;
206 public $labelStatus;
207
208 public $statut; // 0=draft, 1=opened, 2=closed
209
210 public $opp_status; // opportunity status, into table llx_c_lead_status
211 public $opp_status_code;
212 public $fk_opp_status; // opportunity status, into table llx_c_lead_status
213 public $opp_amount; // opportunity amount
214 public $opp_percent; // opportunity probability
215 public $opp_weighted_amount; // opportunity weighted amount
216
217 public $email_msgid;
218
219 public $oldcopy;
220
221 public $weekWorkLoad; // Used to store workload details of a projet
222 public $weekWorkLoadPerTask; // Used to store workload details of tasks of a projet
223
227 public $monthWorkLoad;
228
232 public $monthWorkLoadPerTask;
233
239 public $datec;
240
244 public $date_c;
245
251 public $datem;
252
256 public $date_m;
257
261 public $ip;
262
266 public $lines;
267
292 // BEGIN MODULEBUILDER PROPERTIES
296 public $fields = array(
297 'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
298 'fk_project' =>array('type'=>'integer', 'label'=>'Parent', 'enabled'=>1, 'visible'=>1, 'notnull'=>0, 'position'=>12),
299 'ref' =>array('type'=>'varchar(50)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'showoncombobox'=>1, 'position'=>15, 'searchall'=>1),
300 'title' =>array('type'=>'varchar(255)', 'label'=>'ProjectLabel', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>17, 'showoncombobox'=>2, 'searchall'=>1),
301 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>19),
302 'fk_soc' =>array('type'=>'integer', 'label'=>'Thirdparty', 'enabled'=>1, 'visible'=>0, 'position'=>20),
303 'dateo' =>array('type'=>'date', 'label'=>'DateStart', 'enabled'=>1, 'visible'=>1, 'position'=>30),
304 'datee' =>array('type'=>'date', 'label'=>'DateEnd', 'enabled'=>1, 'visible'=>1, 'position'=>35),
305 'description' =>array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>3, 'position'=>55, 'searchall'=>1),
306 'public' =>array('type'=>'integer', 'label'=>'Visibility', 'enabled'=>1, 'visible'=>1, 'position'=>65),
307 'fk_opp_status' =>array('type'=>'integer', 'label'=>'OpportunityStatusShort', 'enabled'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'visible'=>1, 'position'=>75),
308 'opp_percent' =>array('type'=>'double(5,2)', 'label'=>'OpportunityProbabilityShort', 'enabled'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'visible'=>1, 'position'=>80),
309 'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>85, 'searchall'=>1),
310 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>90, 'searchall'=>1),
311 'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPdf', 'enabled'=>1, 'visible'=>0, 'position'=>95),
312 'date_close' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>0, 'position'=>105),
313 'fk_user_close' =>array('type'=>'integer', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>0, 'position'=>110),
314 'opp_amount' =>array('type'=>'double(24,8)', 'label'=>'OpportunityAmountShort', 'enabled'=>1, 'visible'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'position'=>115),
315 'budget_amount' =>array('type'=>'double(24,8)', 'label'=>'Budget', 'enabled'=>1, 'visible'=>-1, 'position'=>119),
316 'usage_opportunity' =>array('type'=>'integer', 'label'=>'UsageOpportunity', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
317 'usage_task' =>array('type'=>'integer', 'label'=>'UsageTasks', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
318 'usage_bill_time' =>array('type'=>'integer', 'label'=>'UsageBillTimeShort', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
319 'usage_organize_event' =>array('type'=>'integer', 'label'=>'UsageOrganizeEvent', 'enabled'=>1, 'visible'=>-1, 'position'=>145),
320 // Properties for event organization
321 'date_start_event' =>array('type'=>'date', 'label'=>'DateStartEvent', 'enabled'=>"isModEnabled('eventorganization')", 'visible'=>1, 'position'=>200),
322 'date_end_event' =>array('type'=>'date', 'label'=>'DateEndEvent', 'enabled'=>"isModEnabled('eventorganization')", 'visible'=>1, 'position'=>201),
323 'location' =>array('type'=>'text', 'label'=>'Location', 'enabled'=>1, 'visible'=>3, 'position'=>55, 'searchall'=>202),
324 'accept_conference_suggestions' =>array('type'=>'integer', 'label'=>'AllowUnknownPeopleSuggestConf', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
325 'accept_booth_suggestions' =>array('type'=>'integer', 'label'=>'AllowUnknownPeopleSuggestBooth', 'enabled'=>1, 'visible'=>-1, 'position'=>211),
326 'price_registration' =>array('type'=>'double(24,8)', 'label'=>'PriceOfRegistration', 'enabled'=>1, 'visible'=>-1, 'position'=>212),
327 'price_booth' =>array('type'=>'double(24,8)', 'label'=>'PriceOfBooth', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
328 'max_attendees' =>array('type'=>'integer', 'label'=>'MaxNbOfAttendees', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
329 // Generic
330 'datec' =>array('type'=>'datetime', 'label'=>'DateCreationShort', 'enabled'=>1, 'visible'=>-2, 'position'=>400),
331 'tms' =>array('type'=>'timestamp', 'label'=>'DateModificationShort', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>405),
332 'fk_user_creat' =>array('type'=>'integer', 'label'=>'UserCreation', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>410),
333 'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModification', 'enabled'=>1, 'visible'=>0, 'position'=>415),
334 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'position'=>420),
335 'email_msgid'=>array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'enabled'=>1, 'visible'=>-1, 'position'=>450, 'help'=>'EmailMsgIDWhenSourceisEmail', 'csslist'=>'tdoverflowmax125'),
336 'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>500),
337 );
338 // END MODULEBUILDER PROPERTIES
339
343 const STATUS_DRAFT = 0;
344
349
353 const STATUS_CLOSED = 2;
354
360 public function __construct($db)
361 {
362 global $conf;
363
364 $this->db = $db;
365
366 $this->labelStatusShort = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
367 $this->labelStatus = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
368
369 global $conf;
370
371 if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID')) {
372 $this->fields['rowid']['visible'] = 0;
373 }
374
375 if (!getDolGlobalString('PROJECT_USE_OPPORTUNITIES')) {
376 $this->fields['fk_opp_status']['enabled'] = 0;
377 $this->fields['opp_percent']['enabled'] = 0;
378 $this->fields['opp_amount']['enabled'] = 0;
379 $this->fields['usage_opportunity']['enabled'] = 0;
380 }
381
382 if (getDolGlobalString('PROJECT_HIDE_TASKS')) {
383 $this->fields['usage_bill_time']['visible'] = 0;
384 $this->fields['usage_task']['visible'] = 0;
385 }
386
387 if (empty($conf->eventorganization->enabled)) {
388 $this->fields['usage_organize_event']['visible'] = 0;
389 $this->fields['accept_conference_suggestions']['enabled'] = 0;
390 $this->fields['accept_booth_suggestions']['enabled'] = 0;
391 $this->fields['price_registration']['enabled'] = 0;
392 $this->fields['price_booth']['enabled'] = 0;
393 $this->fields['max_attendees']['enabled'] = 0;
394 }
395 }
396
404 public function create($user, $notrigger = 0)
405 {
406 global $conf, $langs;
407
408 $error = 0;
409 $ret = 0;
410
411 $now = dol_now();
412
413 // Clean parameters
414 $this->note_private = dol_substr($this->note_private, 0, 65535);
415 $this->note_public = dol_substr($this->note_public, 0, 65535);
416
417 // Check parameters
418 if (!trim($this->ref)) {
419 $this->error = 'ErrorFieldsRequired';
420 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
421 return -1;
422 }
423 if (getDolGlobalString('PROJECT_THIRDPARTY_REQUIRED') && !($this->socid > 0)) {
424 $this->error = 'ErrorFieldsRequired';
425 dol_syslog(get_class($this)."::create error -1 thirdparty not defined and option PROJECT_THIRDPARTY_REQUIRED is set", LOG_ERR);
426 return -1;
427 }
428
429 // Create project
430 $this->db->begin();
431
432 $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet (";
433 $sql .= "ref";
434 $sql .= ", fk_project";
435 $sql .= ", title";
436 $sql .= ", description";
437 $sql .= ", fk_soc";
438 $sql .= ", fk_user_creat";
439 $sql .= ", fk_statut";
440 $sql .= ", fk_opp_status";
441 $sql .= ", opp_percent";
442 $sql .= ", public";
443 $sql .= ", datec";
444 $sql .= ", dateo";
445 $sql .= ", datee";
446 $sql .= ", opp_amount";
447 $sql .= ", budget_amount";
448 $sql .= ", usage_opportunity";
449 $sql .= ", usage_task";
450 $sql .= ", usage_bill_time";
451 $sql .= ", usage_organize_event";
452 $sql .= ", accept_conference_suggestions";
453 $sql .= ", accept_booth_suggestions";
454 $sql .= ", price_registration";
455 $sql .= ", price_booth";
456 $sql .= ", max_attendees";
457 $sql .= ", date_start_event";
458 $sql .= ", date_end_event";
459 $sql .= ", location";
460 $sql .= ", email_msgid";
461 $sql .= ", note_private";
462 $sql .= ", note_public";
463 $sql .= ", entity";
464 $sql .= ", ip";
465 $sql .= ") VALUES (";
466 $sql .= "'".$this->db->escape($this->ref)."'";
467 $sql .= ", ".($this->fk_project ? ((int) $this->fk_project) : "null");
468 $sql .= ", '".$this->db->escape($this->title)."'";
469 $sql .= ", '".$this->db->escape($this->description)."'";
470 $sql .= ", ".($this->socid > 0 ? $this->socid : "null");
471 $sql .= ", ".((int) $user->id);
472 $sql .= ", ".(is_numeric($this->statut) ? ((int) $this->statut) : '0');
473 $sql .= ", ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? ((int) $this->opp_status) : 'NULL');
474 $sql .= ", ".(is_numeric($this->opp_percent) ? ((int) $this->opp_percent) : 'NULL');
475 $sql .= ", ".($this->public ? 1 : 0);
476 $sql .= ", '".$this->db->idate($now)."'";
477 $sql .= ", ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
478 $sql .= ", ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
479 $sql .= ", ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : 'null');
480 $sql .= ", ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : 'null');
481 $sql .= ", ".($this->usage_opportunity ? 1 : 0);
482 $sql .= ", ".($this->usage_task ? 1 : 0);
483 $sql .= ", ".($this->usage_bill_time ? 1 : 0);
484 $sql .= ", ".($this->usage_organize_event ? 1 : 0);
485 $sql .= ", ".($this->accept_conference_suggestions ? 1 : 0);
486 $sql .= ", ".($this->accept_booth_suggestions ? 1 : 0);
487 $sql .= ", ".(strcmp($this->price_registration, '') ? price2num($this->price_registration) : 'null');
488 $sql .= ", ".(strcmp($this->price_booth, '') ? price2num($this->price_booth) : 'null');
489 $sql .= ", ".(strcmp($this->max_attendees, '') ? ((int) $this->max_attendees) : 'null');
490 $sql .= ", ".($this->date_start_event != '' ? "'".$this->db->idate($this->date_start_event)."'" : 'null');
491 $sql .= ", ".($this->date_end_event != '' ? "'".$this->db->idate($this->date_end_event)."'" : 'null');
492 $sql .= ", ".($this->location ? "'".$this->db->escape($this->location)."'" : 'null');
493 $sql .= ", ".($this->email_msgid ? "'".$this->db->escape($this->email_msgid)."'" : 'null');
494 $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null');
495 $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null');
496 $sql .= ", ".((int) $conf->entity);
497 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
498 $sql .= ")";
499
500 dol_syslog(get_class($this)."::create", LOG_DEBUG);
501 $resql = $this->db->query($sql);
502 if ($resql) {
503 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet");
504 $ret = $this->id;
505
506 if (!$notrigger) {
507 // Call trigger
508 $result = $this->call_trigger('PROJECT_CREATE', $user);
509 if ($result < 0) {
510 $error++;
511 }
512 // End call triggers
513 }
514 } else {
515 $this->error = $this->db->lasterror();
516 $error++;
517 }
518
519 // Update extrafield
520 if (!$error) {
521 $result = $this->insertExtraFields();
522 if ($result < 0) {
523 $error++;
524 }
525 }
526
527 if (!$error && (getDolGlobalString('MAIN_DISABLEDRAFTSTATUS') || getDolGlobalString('MAIN_DISABLEDRAFTSTATUS_PROJECT'))) {
528 $res = $this->setValid($user);
529 if ($res < 0) {
530 $error++;
531 }
532 }
533
534 if (!$error) {
535 $this->db->commit();
536 return $ret;
537 } else {
538 $this->db->rollback();
539 return -1;
540 }
541 }
542
550 public function update($user, $notrigger = 0)
551 {
552 global $langs, $conf;
553
554 $error = 0;
555
556 // Clean parameters
557 $this->title = trim($this->title);
558 $this->description = trim($this->description);
559 if ($this->opp_amount < 0) {
560 $this->opp_amount = '';
561 }
562 if ($this->opp_percent < 0) {
563 $this->opp_percent = '';
564 }
565 if ($this->date_end && $this->date_end < $this->date_start) {
566 $this->error = $langs->trans("ErrorDateEndLowerThanDateStart");
567 $this->errors[] = $this->error;
568 $this->db->rollback();
569 dol_syslog(get_class($this)."::update error -3 ".$this->error, LOG_ERR);
570 return -3;
571 }
572
573 $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
574
575 if (dol_strlen(trim($this->ref)) > 0) {
576 $this->db->begin();
577
578 $sql = "UPDATE ".MAIN_DB_PREFIX."projet SET";
579 $sql .= " ref='".$this->db->escape($this->ref)."'";
580 $sql .= ", fk_project=".($this->fk_project ? ((int) $this->fk_project) : "null");
581 $sql .= ", title = '".$this->db->escape($this->title)."'";
582 $sql .= ", description = '".$this->db->escape($this->description)."'";
583 $sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : "null");
584 $sql .= ", fk_statut = ".((int) $this->statut);
585 $sql .= ", fk_opp_status = ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null');
586 $sql .= ", opp_percent = ".((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null');
587 $sql .= ", public = ".($this->public ? 1 : 0);
588 $sql .= ", datec = ".($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null');
589 $sql .= ", dateo = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
590 $sql .= ", datee = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
591 $sql .= ", date_close = ".($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null');
592 $sql .= ", note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
593 $sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
594 $sql .= ", fk_user_close = ".($this->fk_user_close > 0 ? $this->fk_user_close : "null");
595 $sql .= ", opp_amount = ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null");
596 $sql .= ", budget_amount = ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null");
597 $sql .= ", fk_user_modif = ".$user->id;
598 $sql .= ", usage_opportunity = ".($this->usage_opportunity ? 1 : 0);
599 $sql .= ", usage_task = ".($this->usage_task ? 1 : 0);
600 $sql .= ", usage_bill_time = ".($this->usage_bill_time ? 1 : 0);
601 $sql .= ", usage_organize_event = ".($this->usage_organize_event ? 1 : 0);
602 $sql .= ", accept_conference_suggestions = ".($this->accept_conference_suggestions ? 1 : 0);
603 $sql .= ", accept_booth_suggestions = ".($this->accept_booth_suggestions ? 1 : 0);
604 $sql .= ", price_registration = ".(strcmp($this->price_registration, '') ? price2num($this->price_registration) : "null");
605 $sql .= ", price_booth = ".(strcmp($this->price_booth, '') ? price2num($this->price_booth) : "null");
606 $sql .= ", max_attendees = ".(strcmp($this->max_attendees, '') ? price2num($this->max_attendees) : "null");
607 $sql .= ", date_start_event = ".($this->date_start_event != '' ? "'".$this->db->idate($this->date_start_event)."'" : 'null');
608 $sql .= ", date_end_event = ".($this->date_end_event != '' ? "'".$this->db->idate($this->date_end_event)."'" : 'null');
609 $sql .= ", location = '".$this->db->escape($this->location)."'";
610 $sql .= ", entity = ".((int) $this->entity);
611 $sql .= " WHERE rowid = ".((int) $this->id);
612
613 dol_syslog(get_class($this)."::update", LOG_DEBUG);
614 $resql = $this->db->query($sql);
615 if ($resql) {
616 // Update extrafield
617 if (!$error) {
618 $result = $this->insertExtraFields();
619 if ($result < 0) {
620 $error++;
621 }
622 }
623
624 if (!$error && !$notrigger) {
625 // Call trigger
626 $result = $this->call_trigger('PROJECT_MODIFY', $user);
627 if ($result < 0) {
628 $error++;
629 }
630 // End call triggers
631 }
632
633 if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
634 // We remove directory
635 if ($conf->project->dir_output) {
636 $olddir = $conf->project->dir_output."/".dol_sanitizeFileName($this->oldcopy->ref);
637 $newdir = $conf->project->dir_output."/".dol_sanitizeFileName($this->ref);
638 if (file_exists($olddir)) {
639 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
640 $res = @rename($olddir, $newdir);
641 if (!$res) {
642 $langs->load("errors");
643 $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
644 $error++;
645 }
646 }
647 }
648 }
649 if (!$error) {
650 $this->db->commit();
651 $result = 1;
652 } else {
653 $this->db->rollback();
654 $result = -1;
655 }
656 } else {
657 $this->error = $this->db->lasterror();
658 $this->errors[] = $this->error;
659 $this->db->rollback();
660 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
661 $result = -4;
662 } else {
663 $result = -2;
664 }
665 dol_syslog(get_class($this)."::update error ".$result." ".$this->error, LOG_ERR);
666 }
667 } else {
668 dol_syslog(get_class($this)."::update ref null");
669 $result = -1;
670 }
671
672 return $result;
673 }
674
684 public function fetch($id, $ref = '', $ref_ext = '', $email_msgid = '')
685 {
686 if (empty($id) && empty($ref) && empty($ref_ext) && empty($email_msgid)) {
687 dol_syslog(get_class($this)."::fetch Bad parameters", LOG_WARNING);
688 return -1;
689 }
690
691 $sql = "SELECT rowid, entity, fk_project, ref, title, description, public, datec, opp_amount, budget_amount,";
692 $sql .= " tms, dateo as date_start, datee as date_end, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut as status, fk_opp_status, opp_percent,";
693 $sql .= " note_private, note_public, model_pdf, usage_opportunity, usage_task, usage_bill_time, usage_organize_event, email_msgid,";
694 $sql .= " accept_conference_suggestions, accept_booth_suggestions, price_registration, price_booth, max_attendees, date_start_event, date_end_event, location, extraparams";
695 $sql .= " FROM ".MAIN_DB_PREFIX."projet";
696 if (!empty($id)) {
697 $sql .= " WHERE rowid = ".((int) $id);
698 } else {
699 $sql .= " WHERE entity IN (".getEntity('project').")";
700 if (!empty($ref)) {
701 $sql .= " AND ref = '".$this->db->escape($ref)."'";
702 } elseif (!empty($ref_ext)) {
703 $sql .= " AND ref_ext = '".$this->db->escape($ref_ext)."'";
704 } else {
705 $sql .= " AND email_msgid = '".$this->db->escape($email_msgid)."'";
706 }
707 }
708
709 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
710 $resql = $this->db->query($sql);
711 if ($resql) {
712 $num_rows = $this->db->num_rows($resql);
713
714 if ($num_rows) {
715 $obj = $this->db->fetch_object($resql);
716
717 $this->id = $obj->rowid;
718 $this->entity = $obj->entity;
719 $this->ref = $obj->ref;
720 $this->fk_project = $obj->fk_project;
721 $this->title = $obj->title;
722 $this->description = $obj->description;
723 $this->date_c = $this->db->jdate($obj->datec);
724 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
725 $this->date_m = $this->db->jdate($obj->tms);
726 $this->datem = $this->db->jdate($obj->tms); // TODO deprecated
727 $this->date_start = $this->db->jdate($obj->date_start);
728 $this->date_end = $this->db->jdate($obj->date_end);
729 $this->date_close = $this->db->jdate($obj->date_close);
730 $this->note_private = $obj->note_private;
731 $this->note_public = $obj->note_public;
732 $this->socid = $obj->fk_soc;
733 $this->user_author_id = $obj->fk_user_creat;
734 $this->user_modification_id = $obj->fk_user_modif;
735 $this->user_closing_id = $obj->fk_user_close;
736 $this->public = $obj->public;
737 $this->statut = $obj->status; // deprecated
738 $this->status = $obj->status;
739 $this->opp_status = $obj->fk_opp_status;
740 $this->opp_amount = $obj->opp_amount;
741 $this->opp_percent = $obj->opp_percent;
742 $this->budget_amount = $obj->budget_amount;
743 $this->model_pdf = $obj->model_pdf;
744 $this->usage_opportunity = (int) $obj->usage_opportunity;
745 $this->usage_task = (int) $obj->usage_task;
746 $this->usage_bill_time = (int) $obj->usage_bill_time;
747 $this->usage_organize_event = (int) $obj->usage_organize_event;
748 $this->accept_conference_suggestions = (int) $obj->accept_conference_suggestions;
749 $this->accept_booth_suggestions = (int) $obj->accept_booth_suggestions;
750 $this->price_registration = $obj->price_registration;
751 $this->price_booth = $obj->price_booth;
752 $this->max_attendees = $obj->max_attendees;
753 $this->date_start_event = $this->db->jdate($obj->date_start_event);
754 $this->date_end_event = $this->db->jdate($obj->date_end_event);
755 $this->location = $obj->location;
756 $this->email_msgid = $obj->email_msgid;
757 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
758
759 $this->db->free($resql);
760
761 // Retrieve all extrafield
762 // fetch optionals attributes and labels
763 $this->fetch_optionals();
764
765 return 1;
766 }
767
768 $this->db->free($resql);
769
770 return 0;
771 } else {
772 $this->error = $this->db->lasterror();
773 $this->errors[] = $this->db->lasterror();
774 return -1;
775 }
776 }
777
786 public function fetchAndSetSubstitution($id, $key, $fetched = false)
787 {
788 $substitution = '';
789
790 if ($fetched === false) {
791 $res = $this->fetch($id);
792 if ($res > 0) {
793 $fetched = true;
794 }
795 }
796
797 if ($fetched === true) {
798 if ($key == '__PROJECT_ID__') {
799 $substitution = ($this->id > 0 ? $this->id : '');
800 } elseif ($key == '__PROJECT_REF__') {
801 $substitution = $this->ref;
802 } elseif ($key == '__PROJECT_NAME__') {
803 $substitution = $this->title;
804 }
805 }
806
807 return $substitution;
808 }
809
810 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
822 public function get_element_list($type, $tablename, $datefieldname = '', $date_start = '', $date_end = '', $projectkey = 'fk_projet')
823 {
824 // phpcs:enable
825
826 global $hookmanager;
827
828 $elements = array();
829
830 if ($this->id <= 0) {
831 return $elements;
832 }
833
834 $ids = $this->id;
835
836 if ($type == 'agenda') {
837 $sql = "SELECT id as rowid FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project IN (".$this->db->sanitize($ids).") AND entity IN (".getEntity('agenda').")";
838 } elseif ($type == 'expensereport') {
839 $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 (".$this->db->sanitize($ids).")";
840 } elseif ($type == 'project_task') {
841 $sql = "SELECT DISTINCT pt.rowid FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet IN (".$this->db->sanitize($ids).")";
842 } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user
843 $sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."element_time as ptt WHERE pt.rowid = ptt.fk_element AND ptt.elementtype = 'task' AND pt.fk_projet IN (".$this->db->sanitize($ids).")";
844 } elseif ($type == 'stocktransfer_stocktransfer') {
845 $sql = "SELECT ms.rowid, ms.fk_user_author as fk_user FROM ".MAIN_DB_PREFIX."stocktransfer_stocktransfer 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 (".$this->db->sanitize($ids).") AND ms.type_mouvement = 1";
846 } elseif ($type == 'loan') {
847 $sql = "SELECT l.rowid, l.fk_user_author as fk_user FROM ".MAIN_DB_PREFIX."loan as l WHERE l.entity IN (".getEntity('loan').") AND l.fk_projet IN (".$this->db->sanitize($ids).")";
848 } else {
849 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." IN (".$this->db->sanitize($ids).") AND entity IN (".getEntity($type).")";
850 }
851
852 if ($date_start > 0 && $type == 'loan') {
853 $sql .= " AND (dateend > '".$this->db->idate($date_start)."' OR dateend IS NULL)";
854 } elseif ($date_start > 0 && ($type != 'project_task')) { // For table project_taks, we want the filter on date apply on project_time_spent table
855 if (empty($datefieldname) && !empty($this->table_element_date)) {
856 $datefieldname = $this->table_element_date;
857 }
858 if (empty($datefieldname)) {
859 return 'Error this object has no date field defined';
860 }
861 $sql .= " AND (".$datefieldname." >= '".$this->db->idate($date_start)."' OR ".$datefieldname." IS NULL)";
862 }
863
864 if ($date_end > 0 && $type == 'loan') {
865 $sql .= " AND (datestart < '".$this->db->idate($date_end)."' OR datestart IS NULL)";
866 } elseif ($date_end > 0 && ($type != 'project_task')) { // For table project_taks, we want the filter on date apply on project_time_spent table
867 if (empty($datefieldname) && !empty($this->table_element_date)) {
868 $datefieldname = $this->table_element_date;
869 }
870 if (empty($datefieldname)) {
871 return 'Error this object has no date field defined';
872 }
873 $sql .= " AND (".$datefieldname." <= '".$this->db->idate($date_end)."' OR ".$datefieldname." IS NULL)";
874 }
875
876 $parameters = array(
877 'sql'=>$sql,
878 'type' => $type,
879 'tablename' => $tablename,
880 'datefieldname' => $datefieldname,
881 'dates' => $date_start,
882 'datee' => $date_end,
883 'fk_projet' => $projectkey,
884 'ids' => $ids,
885 );
886 $reshook = $hookmanager->executeHooks('getElementList', $parameters);
887 if ($reshook > 0) {
888 $sql = $hookmanager->resPrint;
889 } else {
890 $sql .= $hookmanager->resPrint;
891 }
892
893 if (!$sql) {
894 return -1;
895 }
896
897 //print $sql;
898 dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG);
899 $result = $this->db->query($sql);
900 if ($result) {
901 $nump = $this->db->num_rows($result);
902 if ($nump) {
903 $i = 0;
904 while ($i < $nump) {
905 $obj = $this->db->fetch_object($result);
906
907 $elements[$i] = $obj->rowid.(empty($obj->fk_user) ? '' : '_'.$obj->fk_user);
908
909 $i++;
910 }
911 $this->db->free($result);
912 }
913
914 /* Return array even if empty*/
915 return $elements;
916 } else {
917 dol_print_error($this->db);
918 }
919 }
920
928 public function delete($user, $notrigger = 0)
929 {
930 global $langs, $conf;
931 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
932
933 $error = 0;
934
935 $this->db->begin();
936
937 if (!$error) {
938 // Delete linked contacts
939 $res = $this->delete_linked_contact();
940 if ($res < 0) {
941 $this->error = 'ErrorFailToDeleteLinkedContact';
942 //$error++;
943 $this->db->rollback();
944 return 0;
945 }
946 }
947
948 // Set fk_projet into elements to null
949 $listoftables = array(
950 'propal'=>'fk_projet', 'commande'=>'fk_projet', 'facture'=>'fk_projet',
951 'supplier_proposal'=>'fk_projet', 'commande_fournisseur'=>'fk_projet', 'facture_fourn'=>'fk_projet',
952 'expensereport_det'=>'fk_projet', 'contrat'=>'fk_projet',
953 'fichinter'=>'fk_projet',
954 'don'=>array('field'=>'fk_projet', 'module'=>'don'),
955 'actioncomm'=>'fk_project',
956 'mrp_mo'=>'fk_project',
957 'entrepot'=>'fk_project'
958 );
959 foreach ($listoftables as $key => $value) {
960 if (is_array($value)) {
961 if (!isModEnabled($value['module'])) {
962 continue;
963 }
964 $fieldname = $value['field'];
965 } else {
966 $fieldname = $value;
967 }
968 $sql = "UPDATE ".MAIN_DB_PREFIX.$key." SET ".$fieldname." = NULL where ".$fieldname." = ".((int) $this->id);
969
970 $resql = $this->db->query($sql);
971 if (!$resql) {
972 $this->errors[] = $this->db->lasterror();
973 $error++;
974 break;
975 }
976 }
977
978 // Remove linked categories.
979 if (!$error) {
980 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_project";
981 $sql .= " WHERE fk_project = ".((int) $this->id);
982
983 $result = $this->db->query($sql);
984 if (!$result) {
985 $error++;
986 $this->errors[] = $this->db->lasterror();
987 }
988 }
989
990 // Fetch tasks
991 $this->getLinesArray($user, 0);
992
993 // Delete tasks
994 $ret = $this->deleteTasks($user);
995 if ($ret < 0) {
996 $error++;
997 }
998
999
1000 // Delete all child tables
1001 if (!$error) {
1002 $elements = array('categorie_project'); // elements to delete. TODO Make goodway to delete
1003 foreach ($elements as $table) {
1004 if (!$error) {
1005 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$table;
1006 $sql .= " WHERE fk_project = ".((int) $this->id);
1007
1008 $result = $this->db->query($sql);
1009 if (!$result) {
1010 $error++;
1011 $this->errors[] = $this->db->lasterror();
1012 }
1013 }
1014 }
1015 }
1016
1017 if (!$error) {
1018 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_extrafields";
1019 $sql .= " WHERE fk_object = ".((int) $this->id);
1020
1021 $resql = $this->db->query($sql);
1022 if (!$resql) {
1023 $this->errors[] = $this->db->lasterror();
1024 $error++;
1025 }
1026 }
1027
1028 // Delete project
1029 if (!$error) {
1030 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet";
1031 $sql .= " WHERE rowid=".((int) $this->id);
1032
1033 $resql = $this->db->query($sql);
1034 if (!$resql) {
1035 $this->errors[] = $langs->trans("CantRemoveProject", $langs->transnoentitiesnoconv("ProjectOverview"));
1036 $error++;
1037 }
1038 }
1039
1040
1041
1042 if (empty($error)) {
1043 // We remove directory
1044 $projectref = dol_sanitizeFileName($this->ref);
1045 if ($conf->project->dir_output) {
1046 $dir = $conf->project->dir_output."/".$projectref;
1047 if (file_exists($dir)) {
1048 $res = @dol_delete_dir_recursive($dir);
1049 if (!$res) {
1050 $this->errors[] = 'ErrorFailToDeleteDir';
1051 $error++;
1052 }
1053 }
1054 }
1055
1056 if (!$notrigger) {
1057 // Call trigger
1058 $result = $this->call_trigger('PROJECT_DELETE', $user);
1059
1060 if ($result < 0) {
1061 $error++;
1062 }
1063 // End call triggers
1064 }
1065 }
1066
1067 if (empty($error)) {
1068 $this->db->commit();
1069 return 1;
1070 } else {
1071 foreach ($this->errors as $errmsg) {
1072 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1073 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1074 }
1075 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
1076 $this->db->rollback();
1077 return -1;
1078 }
1079 }
1080
1089 public function getElementCount($type, $tablename, $projectkey = 'fk_projet')
1090 {
1091 if ($this->id <= 0) {
1092 return 0;
1093 }
1094
1095 if ($type == 'agenda') {
1096 $sql = "SELECT COUNT(id) as nb FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project = ".((int) $this->id)." AND entity IN (".getEntity('agenda').")";
1097 } elseif ($type == 'expensereport') {
1098 $sql = "SELECT COUNT(ed.rowid) as nb 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 = ".((int) $this->id);
1099 } elseif ($type == 'project_task') {
1100 $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet = ".((int) $this->id);
1101 } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user
1102 $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."element_time as ptt WHERE pt.rowid = ptt.fk_element AND ptt.elementtype = 'task' AND pt.fk_projet = ".((int) $this->id);
1103 } elseif ($type == 'stock_mouvement') {
1104 $sql = "SELECT COUNT(ms.rowid) as nb 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 = ".((int) $this->id)." AND ms.type_mouvement = 1";
1105 } elseif ($type == 'loan') {
1106 $sql = "SELECT COUNT(l.rowid) as nb FROM ".MAIN_DB_PREFIX."loan as l WHERE l.entity IN (".getEntity('loan').") AND l.fk_projet = ".((int) $this->id);
1107 } else {
1108 $sql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." = ".((int) $this->id)." AND entity IN (".getEntity($type).")";
1109 }
1110
1111 $result = $this->db->query($sql);
1112
1113 if (!$result) {
1114 return 0;
1115 }
1116
1117 $obj = $this->db->fetch_object($result);
1118
1119 $this->db->free($result);
1120
1121 return $obj->nb;
1122 }
1123
1130 public function deleteTasks($user)
1131 {
1132 $countTasks = count($this->lines);
1133 $deleted = false;
1134 if ($countTasks) {
1135 foreach ($this->lines as $task) {
1136 if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them)
1137 $deleted = true;
1138 $ret = $task->delete($user);
1139 if ($ret <= 0) {
1140 $this->errors[] = $this->db->lasterror();
1141 return -1;
1142 }
1143 }
1144 }
1145 }
1146 $this->getLinesArray($user);
1147 if ($deleted && count($this->lines) < $countTasks) {
1148 if (count($this->lines)) {
1149 $this->deleteTasks($this->lines);
1150 }
1151 }
1152
1153 return 1;
1154 }
1155
1163 public function setValid($user, $notrigger = 0)
1164 {
1165 global $langs, $conf;
1166
1167 $error = 0;
1168
1169 // Protection
1170 if ($this->status == self::STATUS_VALIDATED) {
1171 dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
1172 return 0;
1173 }
1174
1175 // Check parameters
1176 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title)) {
1177 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("Label")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1178 return -1;
1179 }
1180
1181 $this->db->begin();
1182
1183 $sql = "UPDATE ".MAIN_DB_PREFIX."projet";
1184 $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1185 $sql .= " WHERE rowid = ".((int) $this->id);
1186 //$sql .= " AND entity = ".((int) $conf->entity); // Disabled, when we use the ID for the where, we must not add any other search condition
1187
1188 dol_syslog(get_class($this)."::setValid", LOG_DEBUG);
1189 $resql = $this->db->query($sql);
1190 if ($resql) {
1191 // Call trigger
1192 if (empty($notrigger)) {
1193 $result = $this->call_trigger('PROJECT_VALIDATE', $user);
1194 if ($result < 0) {
1195 $error++;
1196 }
1197 // End call triggers
1198 }
1199
1200 if (!$error) {
1201 $this->statut = 1;
1202 $this->db->commit();
1203 return 1;
1204 } else {
1205 $this->db->rollback();
1206 $this->error = join(',', $this->errors);
1207 dol_syslog(get_class($this)."::setValid ".$this->error, LOG_ERR);
1208 return -1;
1209 }
1210 } else {
1211 $this->db->rollback();
1212 $this->error = $this->db->lasterror();
1213 return -1;
1214 }
1215 }
1216
1223 public function setClose($user)
1224 {
1225 global $langs, $conf;
1226
1227 $now = dol_now();
1228
1229 $error = 0;
1230
1231 if ($this->statut != self::STATUS_CLOSED) {
1232 $this->db->begin();
1233
1234 $sql = "UPDATE ".MAIN_DB_PREFIX."projet";
1235 $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", fk_user_close = ".((int) $user->id).", date_close = '".$this->db->idate($now)."'";
1236 $sql .= " WHERE rowid = ".((int) $this->id);
1237 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1238
1239 if (getDolGlobalString('PROJECT_USE_OPPORTUNITIES')) {
1240 // TODO What to do if fk_opp_status is not code 'WON' or 'LOST'
1241 }
1242
1243 dol_syslog(get_class($this)."::setClose", LOG_DEBUG);
1244 $resql = $this->db->query($sql);
1245 if ($resql) {
1246 // Call trigger
1247 $result = $this->call_trigger('PROJECT_CLOSE', $user);
1248 if ($result < 0) {
1249 $error++;
1250 }
1251 // End call triggers
1252
1253 if (!$error) {
1254 $this->statut = 2;
1255 $this->db->commit();
1256 return 1;
1257 } else {
1258 $this->db->rollback();
1259 $this->error = join(',', $this->errors);
1260 dol_syslog(get_class($this)."::setClose ".$this->error, LOG_ERR);
1261 return -1;
1262 }
1263 } else {
1264 $this->db->rollback();
1265 $this->error = $this->db->lasterror();
1266 return -1;
1267 }
1268 }
1269
1270 return 0;
1271 }
1272
1279 public function getLibStatut($mode = 0)
1280 {
1281 return $this->LibStatut(isset($this->statut) ? $this->statut : $this->status, $mode);
1282 }
1283
1284 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1292 public function LibStatut($status, $mode = 0)
1293 {
1294 // phpcs:enable
1295 global $langs;
1296
1297 if (is_null($status)) {
1298 return '';
1299 }
1300
1301 $statustrans = array(
1302 0 => 'status0',
1303 1 => 'status4',
1304 2 => 'status6',
1305 );
1306
1307 $statusClass = 'status0';
1308 if (!empty($statustrans[$status])) {
1309 $statusClass = $statustrans[$status];
1310 }
1311
1312 return dolGetStatus($langs->transnoentitiesnoconv($this->labelStatus[$status]), $langs->transnoentitiesnoconv($this->labelStatusShort[$status]), '', $statusClass, $mode);
1313 }
1314
1322 public function getTooltipContentArray($params)
1323 {
1324 global $conf, $langs;
1325
1326 $langs->load('projects');
1327 $option = $params['option'] ?? '';
1328 $moreinpopup = $params['moreinpopup'] ?? '';
1329
1330 $datas = [];
1331 if ($option != 'nolink') {
1332 $datas['picto'] = img_picto('', $this->picto, 'class="pictofixedwidth"').' <u class="paddingrightonly">'.$langs->trans("Project").'</u>';
1333 }
1334 if (isset($this->status)) {
1335 $datas['picto'] .= ' '.$this->getLibStatut(5);
1336 }
1337 $datas['ref'] = (isset($datas['picto']) ? '<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
1338 $datas['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
1339 if (isset($this->public)) {
1340 $datas['visibility'] = '<br><b>'.$langs->trans("Visibility").":</b> ";
1341 $datas['visibility'] .= ($this->public ? img_picto($langs->trans('SharedProject'), 'world', 'class="pictofixedwidth"').$langs->trans("SharedProject") : img_picto($langs->trans('PrivateProject'), 'private', 'class="pictofixedwidth"').$langs->trans("PrivateProject"));
1342 }
1343 if (!empty($this->thirdparty_name)) {
1344 $datas['thirdparty'] = '<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
1345 }
1346 if (!empty($this->date_start)) {
1347 $datas['datestart'] = '<br><b>'.$langs->trans('DateStart').': </b>'.dol_print_date($this->date_start, 'day'); // The space must be after the : to not being explode when showing the title in img_picto
1348 }
1349 if (!empty($this->date_end)) {
1350 $datas['dateend'] = '<br><b>'.$langs->trans('DateEnd').': </b>'.dol_print_date($this->date_end, 'day'); // The space must be after the : to not being explode when showing the title in img_picto
1351 }
1352 if ($moreinpopup) {
1353 $datas['moreinpopup'] = '<br>'.$moreinpopup;
1354 }
1355
1356 return $datas;
1357 }
1358
1373 public function getNomUrl($withpicto = 0, $option = '', $addlabel = 0, $moreinpopup = '', $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1, $morecss = '', $save_pageforbacktolist = '')
1374 {
1375 global $conf, $langs, $user, $hookmanager;
1376
1377 if (!empty($conf->dol_no_mouse_hover)) {
1378 $notooltip = 1; // Force disable tooltips
1379 }
1380
1381 $result = '';
1382 if (getDolGlobalString('PROJECT_OPEN_ALWAYS_ON_TAB')) {
1383 $option = $conf->global->PROJECT_OPEN_ALWAYS_ON_TAB;
1384 }
1385 $params = [
1386 'id' => $this->id,
1387 'objecttype' => $this->element,
1388 'moreinpopup' => $moreinpopup,
1389 'option' => $option,
1390 ];
1391 $classfortooltip = 'classfortooltip';
1392 $dataparams = '';
1393 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1394 $classfortooltip = 'classforajaxtooltip';
1395 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1396 $label = '';
1397 } else {
1398 $label = implode($this->getTooltipContentArray($params));
1399 }
1400
1401 $url = '';
1402 if ($option != 'nolink') {
1403 if (preg_match('/\.php$/', $option)) {
1404 $url = dol_buildpath($option, 1).'?id='.$this->id;
1405 } elseif ($option == 'task') {
1406 $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id;
1407 } elseif ($option == 'preview') {
1408 $url = DOL_URL_ROOT.'/projet/element.php?id='.$this->id;
1409 } elseif ($option == 'eventorganization') {
1410 $url = DOL_URL_ROOT.'/eventorganization/conferenceorbooth_list.php?projectid='.$this->id;
1411 } else {
1412 $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id;
1413 }
1414 // Add param to save lastsearch_values or not
1415 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1416 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1417 $add_save_lastsearch_values = 1;
1418 }
1419 if ($add_save_lastsearch_values) {
1420 $url .= '&save_lastsearch_values=1';
1421 }
1422 $add_save_backpagefor = ($save_pageforbacktolist ? 1 : 0);
1423 if ($add_save_backpagefor) {
1424 $url .= "&save_pageforbacktolist=".urlencode($save_pageforbacktolist);
1425 }
1426 }
1427
1428 $linkclose = '';
1429 if (empty($notooltip) && $user->hasRight('projet', 'lire')) {
1430 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1431 $label = $langs->trans("ShowProject");
1432 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1433 }
1434 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1435 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1436 } else {
1437 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1438 }
1439
1440 $picto = 'projectpub';
1441 if (!$this->public) {
1442 $picto = 'project';
1443 }
1444
1445 $linkstart = '<a href="'.$url.'"';
1446 $linkstart .= $linkclose.'>';
1447 $linkend = '</a>';
1448
1449 $result .= $linkstart;
1450 if ($withpicto) {
1451 $result .= img_object(($notooltip ? '' : $label), $picto, 'class="pictofixedwidth em088"', 0, 0, $notooltip ? 0 : 1);
1452 }
1453 if ($withpicto != 2) {
1454 $result .= $this->ref;
1455 }
1456 $result .= $linkend;
1457 if ($withpicto != 2) {
1458 $result .= (($addlabel && $this->title) ? '<span class="opacitymedium">'.$sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)).'</span>' : '');
1459 }
1460
1461 global $action;
1462 $hookmanager->initHooks(array('projectdao'));
1463 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1464 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1465 if ($reshook > 0) {
1466 $result = $hookmanager->resPrint;
1467 } else {
1468 $result .= $hookmanager->resPrint;
1469 }
1470
1471 return $result;
1472 }
1473
1481 public function initAsSpecimen()
1482 {
1483 global $user, $langs, $conf;
1484
1485 $now = dol_now();
1486
1487 // Initialise parameters
1488 $this->id = 0;
1489 $this->ref = 'SPECIMEN';
1490 $this->entity = $conf->entity;
1491 $this->specimen = 1;
1492 $this->socid = 1;
1493 $this->date_c = $now;
1494 $this->date_m = $now;
1495 $this->date_start = $now;
1496 $this->date_end = $now + (3600 * 24 * 365);
1497 $this->note_public = 'SPECIMEN';
1498 $this->fk_element = 20000;
1499 $this->opp_amount = 20000;
1500 $this->budget_amount = 10000;
1501
1502 $this->usage_opportunity = 1;
1503 $this->usage_task = 1;
1504 $this->usage_bill_time = 1;
1505 $this->usage_organize_event = 1;
1506
1507 /*
1508 $nbp = mt_rand(1, 9);
1509 $xnbp = 0;
1510 while ($xnbp < $nbp)
1511 {
1512 $line = new Task($this->db);
1513 $line->fk_project = 0;
1514 $line->label = $langs->trans("Label") . " " . $xnbp;
1515 $line->description = $langs->trans("Description") . " " . $xnbp;
1516
1517 $this->lines[]=$line;
1518 $xnbp++;
1519 }
1520 */
1521 }
1522
1530 public function restrictedProjectArea(User $user, $mode = 'read')
1531 {
1532 // To verify role of users
1533 $userAccess = 0;
1534 if (($mode == 'read' && $user->hasRight('projet', 'all', 'lire')) || ($mode == 'write' && $user->hasRight('projet', 'all', 'creer')) || ($mode == 'delete' && $user->hasRight('projet', 'all', 'supprimer'))) {
1535 $userAccess = 1;
1536 } elseif ($this->public && (($mode == 'read' && $user->hasRight('projet', 'lire')) || ($mode == 'write' && $user->hasRight('projet', 'creer')) || ($mode == 'delete' && $user->hasRight('projet', 'supprimer')))) {
1537 $userAccess = 1;
1538 } else { // No access due to permission to read all projects, so we check if we are a contact of project
1539 foreach (array('internal', 'external') as $source) {
1540 $userRole = $this->liste_contact(4, $source);
1541 $num = count($userRole);
1542
1543 $nblinks = 0;
1544 while ($nblinks < $num) {
1545 if ($source == 'internal' && $user->id == $userRole[$nblinks]['id']) { // $userRole[$nblinks]['id'] is id of user (llx_user) for internal contacts
1546 if ($mode == 'read' && $user->hasRight('projet', 'lire')) {
1547 $userAccess++;
1548 }
1549 if ($mode == 'write' && $user->hasRight('projet', 'creer')) {
1550 $userAccess++;
1551 }
1552 if ($mode == 'delete' && $user->hasRight('projet', 'supprimer')) {
1553 $userAccess++;
1554 }
1555 }
1556 if ($source == 'external' && $user->socid > 0 && $user->socid == $userRole[$nblinks]['socid']) { // $userRole[$nblinks]['id'] is id of contact (llx_socpeople) or external contacts
1557 if ($mode == 'read' && $user->hasRight('projet', 'lire')) {
1558 $userAccess++;
1559 }
1560 if ($mode == 'write' && $user->hasRight('projet', 'creer')) {
1561 $userAccess++;
1562 }
1563 if ($mode == 'delete' && $user->hasRight('projet', 'supprimer')) {
1564 $userAccess++;
1565 }
1566 }
1567 $nblinks++;
1568 }
1569 }
1570 //if (empty($nblinks)) // If nobody has permission, we grant creator
1571 //{
1572 // if ((!empty($this->user_author_id) && $this->user_author_id == $user->id))
1573 // {
1574 // $userAccess = 1;
1575 // }
1576 //}
1577 }
1578
1579 return ($userAccess ? $userAccess : -1);
1580 }
1581
1592 public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '')
1593 {
1594 $projects = array();
1595 $temp = array();
1596
1597 $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref";
1598 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1599 if ($mode == 0) {
1600 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_contact as ec ON ec.element_id = p.rowid";
1601 } elseif ($mode == 1) {
1602 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1603 } elseif ($mode == 2) {
1604 // No filter. Use this if user has permission to see all project
1605 }
1606 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1607 // Internal users must see project he is contact to even if project linked to a third party he can't see.
1608 //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).")";
1609 if ($socid > 0) {
1610 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
1611 }
1612
1613 // Get id of types of contacts for projects (This list never contains a lot of elements)
1614 $listofprojectcontacttype = array();
1615 $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
1616 $sql2 .= " WHERE ctc.element = '".$this->db->escape($this->element)."'";
1617 $sql2 .= " AND ctc.source = 'internal'";
1618 $resql = $this->db->query($sql2);
1619 if ($resql) {
1620 while ($obj = $this->db->fetch_object($resql)) {
1621 $listofprojectcontacttype[$obj->rowid] = $obj->code;
1622 }
1623 } else {
1624 dol_print_error($this->db);
1625 }
1626 if (count($listofprojectcontacttype) == 0) {
1627 $listofprojectcontacttype[0] = '0'; // To avoid syntax error if not found
1628 }
1629
1630 if ($mode == 0) {
1631 $sql .= " AND ( p.public = 1";
1632 $sql .= " OR ( ec.fk_c_type_contact IN (".$this->db->sanitize(join(',', array_keys($listofprojectcontacttype))).")";
1633 $sql .= " AND ec.fk_socpeople = ".((int) $user->id).")";
1634 $sql .= " )";
1635 } elseif ($mode == 1) {
1636 $sql .= " AND ec.element_id = p.rowid";
1637 $sql .= " AND (";
1638 $sql .= " ( ec.fk_c_type_contact IN (".$this->db->sanitize(join(',', array_keys($listofprojectcontacttype))).")";
1639 $sql .= " AND ec.fk_socpeople = ".((int) $user->id).")";
1640 $sql .= " )";
1641 } elseif ($mode == 2) {
1642 // No filter. Use this if user has permission to see all project
1643 }
1644
1645 $sql .= $filter;
1646 //print $sql;
1647
1648 $resql = $this->db->query($sql);
1649 if ($resql) {
1650 $num = $this->db->num_rows($resql);
1651 $i = 0;
1652 while ($i < $num) {
1653 $row = $this->db->fetch_row($resql);
1654 $projects[$row[0]] = $row[1];
1655 $temp[] = $row[0];
1656 $i++;
1657 }
1658
1659 $this->db->free($resql);
1660
1661 if ($list) {
1662 if (empty($temp)) {
1663 return '0';
1664 }
1665 $result = implode(',', $temp);
1666 return $result;
1667 }
1668 } else {
1669 dol_print_error($this->db);
1670 }
1671
1672 return $projects;
1673 }
1674
1690 public function createFromClone(User $user, $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)
1691 {
1692 global $langs, $conf;
1693
1694 $error = 0;
1695
1696 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);
1697
1698 $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now()));
1699
1700 $clone_project = new Project($this->db);
1701
1702 $clone_project->context['createfromclone'] = 'createfromclone';
1703
1704 $this->db->begin();
1705
1706 // Load source object
1707 $clone_project->fetch($fromid);
1708 $clone_project->fetch_optionals();
1709 if ($newthirdpartyid > 0) {
1710 $clone_project->socid = $newthirdpartyid;
1711 }
1712 $clone_project->fetch_thirdparty();
1713
1714 $orign_dt_start = $clone_project->date_start;
1715 $orign_project_ref = $clone_project->ref;
1716
1717 $clone_project->id = 0;
1718 if ($move_date) {
1719 $clone_project->date_start = $now;
1720 if (!(empty($clone_project->date_end))) {
1721 $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start);
1722 }
1723 }
1724
1725 $clone_project->date_c = $now;
1726
1727 if (!$clone_note) {
1728 $clone_project->note_private = '';
1729 $clone_project->note_public = '';
1730 }
1731
1732 //Generate next ref
1733 $defaultref = '';
1734 $obj = !getDolGlobalString('PROJECT_ADDON') ? 'mod_project_simple' : $conf->global->PROJECT_ADDON;
1735 // Search template files
1736 $file = '';
1737 $classname = '';
1738 $filefound = 0;
1739 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1740 foreach ($dirmodels as $reldir) {
1741 $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0);
1742 if (file_exists($file)) {
1743 $filefound = 1;
1744 dol_include_once($reldir."core/modules/project/".$obj.'.php');
1745 $modProject = new $obj();
1746 $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project);
1747 break;
1748 }
1749 }
1750 if (is_numeric($defaultref) && $defaultref <= 0) {
1751 $defaultref = '';
1752 }
1753
1754 $clone_project->ref = $defaultref;
1755 $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title;
1756
1757 // Create clone
1758 $result = $clone_project->create($user, $notrigger);
1759
1760 // Other options
1761 if ($result < 0) {
1762 $this->error .= $clone_project->error;
1763 $error++;
1764 }
1765
1766 if (!$error) {
1767 //Get the new project id
1768 $clone_project_id = $clone_project->id;
1769
1770 //Note Update
1771 if (!$clone_note) {
1772 $clone_project->note_private = '';
1773 $clone_project->note_public = '';
1774 } else {
1775 $this->db->begin();
1776 $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES | ENT_HTML5), '_public');
1777 if ($res < 0) {
1778 $this->error .= $clone_project->error;
1779 $error++;
1780 $this->db->rollback();
1781 } else {
1782 $this->db->commit();
1783 }
1784
1785 $this->db->begin();
1786 $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES | ENT_HTML5), '_private');
1787 if ($res < 0) {
1788 $this->error .= $clone_project->error;
1789 $error++;
1790 $this->db->rollback();
1791 } else {
1792 $this->db->commit();
1793 }
1794 }
1795
1796 //Duplicate contact
1797 if ($clone_contact) {
1798 $origin_project = new Project($this->db);
1799 $origin_project->fetch($fromid);
1800
1801 foreach (array('internal', 'external') as $source) {
1802 $tab = $origin_project->liste_contact(-1, $source);
1803 if (is_array($tab) && count($tab)>0) {
1804 foreach ($tab as $contacttoadd) {
1805 $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger);
1806 if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1807 $langs->load("errors");
1808 $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1809 $error++;
1810 } else {
1811 if ($clone_project->error != '') {
1812 $this->error .= $clone_project->error;
1813 $error++;
1814 }
1815 }
1816 }
1817 } elseif ($tab < 0) {
1818 $this->error .= $origin_project->error;
1819 $error++;
1820 }
1821 }
1822 }
1823
1824 //Duplicate file
1825 if ($clone_project_file) {
1826 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1827
1828 $clone_project_dir = $conf->project->dir_output."/".dol_sanitizeFileName($defaultref);
1829 $ori_project_dir = $conf->project->dir_output."/".dol_sanitizeFileName($orign_project_ref);
1830
1831 if (dol_mkdir($clone_project_dir) >= 0) {
1832 $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1833 foreach ($filearray as $key => $file) {
1834 $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1);
1835 if (is_numeric($rescopy) && $rescopy < 0) {
1836 $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']);
1837 $error++;
1838 }
1839 }
1840 } else {
1841 $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1842 $error++;
1843 }
1844 }
1845
1846 //Duplicate task
1847 if ($clone_task) {
1848 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
1849
1850 $taskstatic = new Task($this->db);
1851
1852 // Security check
1853 $socid = 0;
1854 if ($user->socid > 0) {
1855 $socid = $user->socid;
1856 }
1857
1858 $tasksarray = $taskstatic->getTasksArray(0, 0, $fromid, $socid, 0);
1859
1860 $tab_conv_child_parent = array();
1861
1862 // Loop on each task, to clone it
1863 foreach ($tasksarray as $tasktoclone) {
1864 $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_task_parent, $move_date, true, false, $clone_task_file, true, false);
1865 if ($result_clone <= 0) {
1866 $this->error .= $taskstatic->error;
1867 $error++;
1868 } else {
1869 $new_task_id = $result_clone;
1870 $taskstatic->fetch($tasktoclone->id);
1871
1872 //manage new parent clone task id
1873 // if the current task has child we store the original task id and the equivalent clone task id
1874 if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) {
1875 $tab_conv_child_parent[$tasktoclone->id] = $new_task_id;
1876 }
1877 }
1878 }
1879
1880 //Parse all clone node to be sure to update new parent
1881 $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0);
1882 foreach ($tasksarray as $task_cloned) {
1883 $taskstatic->fetch($task_cloned->id);
1884 if ($taskstatic->fk_task_parent != 0) {
1885 $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent];
1886 }
1887 $res = $taskstatic->update($user, $notrigger);
1888 if ($result_clone <= 0) {
1889 $this->error .= $taskstatic->error;
1890 $error++;
1891 }
1892 }
1893 }
1894 }
1895
1896 unset($clone_project->context['createfromclone']);
1897
1898 if (!$error) {
1899 $this->db->commit();
1900 return $clone_project_id;
1901 } else {
1902 $this->db->rollback();
1903 dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
1904 return -1;
1905 }
1906 }
1907
1908
1915 public function shiftTaskDate($old_project_dt_start)
1916 {
1917 global $user, $langs, $conf;
1918
1919 $error = 0;
1920 $result = 0;
1921
1922 $taskstatic = new Task($this->db);
1923
1924 // Security check
1925 $socid = 0;
1926 if ($user->socid > 0) {
1927 $socid = $user->socid;
1928 }
1929
1930 $tasksarray = $taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1931
1932 foreach ($tasksarray as $tasktoshiftdate) {
1933 $to_update = false;
1934 // Fetch only if update of date will be made
1935 if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) {
1936 //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1937 $to_update = true;
1938 $task = new Task($this->db);
1939 $result = $task->fetch($tasktoshiftdate->id);
1940 if (!$result) {
1941 $error++;
1942 $this->error .= $task->error;
1943 }
1944 }
1945 //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1946
1947 //Calcultate new task start date with difference between old proj start date and origin task start date
1948 if (!empty($tasktoshiftdate->date_start)) {
1949 $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
1950 }
1951
1952 //Calcultate new task end date with difference between origin proj end date and origin task end date
1953 if (!empty($tasktoshiftdate->date_end)) {
1954 $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1955 }
1956
1957 if ($to_update) {
1958 $result = $task->update($user);
1959 if (!$result) {
1960 $error++;
1961 $this->error .= $task->error;
1962 }
1963 }
1964 }
1965 if ($error != 0) {
1966 return -1;
1967 }
1968 return $result;
1969 }
1970
1971
1972 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1980 public function update_element($tableName, $elementSelectId)
1981 {
1982 // phpcs:enable
1983 $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName;
1984
1985 if ($tableName == "actioncomm") {
1986 $sql .= " SET fk_project=".$this->id;
1987 $sql .= " WHERE id=".((int) $elementSelectId);
1988 } elseif (in_array($tableName, ["entrepot","mrp_mo","stocktransfer_stocktransfer"])) {
1989 $sql .= " SET fk_project=".$this->id;
1990 $sql .= " WHERE rowid=".((int) $elementSelectId);
1991 } else {
1992 $sql .= " SET fk_projet=".$this->id;
1993 $sql .= " WHERE rowid=".((int) $elementSelectId);
1994 }
1995
1996 dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
1997 $resql = $this->db->query($sql);
1998 if (!$resql) {
1999 $this->error = $this->db->lasterror();
2000 return -1;
2001 } else {
2002 return 1;
2003 }
2004 }
2005
2006 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2016 public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet')
2017 {
2018 // phpcs:enable
2019 $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName;
2020
2021 if ($tableName == "actioncomm") {
2022 $sql .= " SET fk_project=NULL";
2023 $sql .= " WHERE id=".((int) $elementSelectId);
2024 } else {
2025 $sql .= " SET ".$projectfield."=NULL";
2026 $sql .= " WHERE rowid=".((int) $elementSelectId);
2027 }
2028
2029 dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
2030 $resql = $this->db->query($sql);
2031 if (!$resql) {
2032 $this->error = $this->db->lasterror();
2033 return -1;
2034 } else {
2035 return 1;
2036 }
2037 }
2038
2049 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2050 {
2051 global $conf, $langs;
2052
2053 $langs->load("projects");
2054
2055 if (!dol_strlen($modele)) {
2056 $modele = 'baleine';
2057
2058 if ($this->model_pdf) {
2059 $modele = $this->model_pdf;
2060 } elseif (getDolGlobalString('PROJECT_ADDON_PDF')) {
2061 $modele = $conf->global->PROJECT_ADDON_PDF;
2062 }
2063 }
2064
2065 $modelpath = "core/modules/project/doc/";
2066
2067 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2068 }
2069
2070
2080 public function loadTimeSpent($datestart, $taskid = 0, $userid = 0)
2081 {
2082 $error = 0;
2083
2084 $this->weekWorkLoad = array();
2085 $this->weekWorkLoadPerTask = array();
2086
2087 if (empty($datestart)) {
2088 dol_print_error('', 'Error datestart parameter is empty');
2089 }
2090
2091 $sql = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element";
2092 $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
2093 $sql .= " WHERE ptt.fk_element = pt.rowid";
2094 $sql .= " AND ptt.elementtype = 'task'";
2095 $sql .= " AND pt.fk_projet = ".((int) $this->id);
2096 $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' ";
2097 $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
2098 if ($taskid) {
2099 $sql .= " AND ptt.fk_element=".((int) $taskid);
2100 }
2101 if (is_numeric($userid)) {
2102 $sql .= " AND ptt.fk_user=".((int) $userid);
2103 }
2104
2105 //print $sql;
2106 $resql = $this->db->query($sql);
2107 if ($resql) {
2108 $daylareadyfound = array();
2109
2110 $num = $this->db->num_rows($resql);
2111 $i = 0;
2112 // Loop on each record found, so each couple (project id, task id)
2113 while ($i < $num) {
2114 $obj = $this->db->fetch_object($resql);
2115 $day = $this->db->jdate($obj->element_date); // task_date is date without hours
2116 if (empty($daylareadyfound[$day])) {
2117 $this->weekWorkLoad[$day] = $obj->element_duration;
2118 $this->weekWorkLoadPerTask[$day][$obj->fk_element] = $obj->element_duration;
2119 } else {
2120 $this->weekWorkLoad[$day] += $obj->element_duration;
2121 $this->weekWorkLoadPerTask[$day][$obj->fk_element] += $obj->element_duration;
2122 }
2123 $daylareadyfound[$day] = 1;
2124 $i++;
2125 }
2126 $this->db->free($resql);
2127 return 1;
2128 } else {
2129 $this->error = "Error ".$this->db->lasterror();
2130 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
2131 return -1;
2132 }
2133 }
2134
2144 public function loadTimeSpentMonth($datestart, $taskid = 0, $userid = 0)
2145 {
2146 $error = 0;
2147
2148 $this->monthWorkLoad = array();
2149 $this->monthWorkLoadPerTask = array();
2150
2151 if (empty($datestart)) {
2152 dol_print_error('', 'Error datestart parameter is empty');
2153 }
2154
2155 $sql = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element";
2156 $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
2157 $sql .= " WHERE ptt.fk_element = pt.rowid";
2158 $sql .= " AND ptt.elementtype = 'task'";
2159 $sql .= " AND pt.fk_projet = ".((int) $this->id);
2160 $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' ";
2161 $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'm') - 1)."')";
2162 if ($taskid) {
2163 $sql .= " AND ptt.fk_element=".((int) $taskid);
2164 }
2165 if (is_numeric($userid)) {
2166 $sql .= " AND ptt.fk_user=".((int) $userid);
2167 }
2168
2169 //print $sql;
2170 $resql = $this->db->query($sql);
2171 if ($resql) {
2172 $weekalreadyfound = array();
2173
2174 $num = $this->db->num_rows($resql);
2175 $i = 0;
2176 // Loop on each record found, so each couple (project id, task id)
2177 while ($i < $num) {
2178 $obj = $this->db->fetch_object($resql);
2179 if (!empty($obj->element_date)) {
2180 $date = explode('-', $obj->element_date);
2181 $week_number = getWeekNumber($date[2], $date[1], $date[0]);
2182 }
2183 if (empty($weekalreadyfound[$week_number])) {
2184 $this->monthWorkLoad[$week_number] = $obj->element_duration;
2185 $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] = $obj->element_duration;
2186 } else {
2187 $this->monthWorkLoad[$week_number] += $obj->element_duration;
2188 $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] += $obj->element_duration;
2189 }
2190 $weekalreadyfound[$week_number] = 1;
2191 $i++;
2192 }
2193 $this->db->free($resql);
2194 return 1;
2195 } else {
2196 $this->error = "Error ".$this->db->lasterror();
2197 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
2198 return -1;
2199 }
2200 }
2201
2202 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2209 public function load_board($user)
2210 {
2211 // phpcs:enable
2212 global $conf, $langs;
2213
2214 // For external user, no check is done on company because readability is managed by public status of project and assignement.
2215 //$socid=$user->socid;
2216
2217 $response = new WorkboardResponse();
2218 $response->warning_delay = $conf->project->warning_delay / 60 / 60 / 24;
2219 $response->label = $langs->trans("OpenedProjects");
2220 $response->labelShort = $langs->trans("Opened");
2221 $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
2222 $response->img = img_object('', "projectpub");
2223 $response->nbtodo = 0;
2224 $response->nbtodolate = 0;
2225
2226 $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
2227 $sql .= " FROM (".MAIN_DB_PREFIX."projet as p";
2228 $sql .= ")";
2229 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2230 // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
2231 //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2232 $sql .= " WHERE p.fk_statut = 1";
2233 $sql .= " AND p.entity IN (".getEntity('project').')';
2234
2235
2236 $projectsListId = null;
2237 if (!$user->hasRight("projet", "all", "lire")) {
2238 $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
2239 $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
2240 if (empty($projectsListId)) {
2241 return $response;
2242 }
2243
2244 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2245 }
2246
2247 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2248 //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).")";
2249 // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
2250 //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))";
2251
2252 //print $sql;
2253 $resql = $this->db->query($sql);
2254 if ($resql) {
2255 $project_static = new Project($this->db);
2256
2257
2258 // This assignment in condition is not a bug. It allows walking the results.
2259 while ($obj = $this->db->fetch_object($resql)) {
2260 $response->nbtodo++;
2261
2262 $project_static->statut = $obj->status;
2263 $project_static->opp_status = $obj->fk_opp_status;
2264 $project_static->date_end = $this->db->jdate($obj->datee);
2265
2266 if ($project_static->hasDelay()) {
2267 $response->nbtodolate++;
2268 }
2269 }
2270
2271 return $response;
2272 }
2273
2274 $this->error = $this->db->error();
2275 return -1;
2276 }
2277
2286 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2287 {
2288 $tables = array(
2289 'projet'
2290 );
2291
2292 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2293 }
2294
2295
2296 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2302 public function load_state_board()
2303 {
2304 // phpcs:enable
2305 global $user;
2306
2307 $this->nb = array();
2308
2309 $sql = "SELECT count(p.rowid) as nb";
2310 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2311 $sql .= " WHERE";
2312 $sql .= " p.entity IN (".getEntity('project').")";
2313 if (!$user->hasRight('projet', 'all', 'lire')) {
2314 $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
2315 $sql .= "AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2316 }
2317
2318 $resql = $this->db->query($sql);
2319 if ($resql) {
2320 while ($obj = $this->db->fetch_object($resql)) {
2321 $this->nb["projects"] = $obj->nb;
2322 }
2323 $this->db->free($resql);
2324 return 1;
2325 } else {
2326 dol_print_error($this->db);
2327 $this->error = $this->db->error();
2328 return -1;
2329 }
2330 }
2331
2332
2338 public function hasDelay()
2339 {
2340 global $conf;
2341
2342 if (!($this->statut == self::STATUS_VALIDATED)) {
2343 return false;
2344 }
2345 if (!$this->date_end) {
2346 return false;
2347 }
2348
2349 $now = dol_now();
2350
2351 return ($this->date_end) < ($now - $conf->project->warning_delay);
2352 }
2353
2354
2361 public function info($id)
2362 {
2363 $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
2364 $sql .= ' date_close as datecloture,';
2365 $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_user_cloture';
2366 $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c';
2367 $sql .= ' WHERE c.rowid = '.((int) $id);
2368 $result = $this->db->query($sql);
2369 if ($result) {
2370 if ($this->db->num_rows($result)) {
2371 $obj = $this->db->fetch_object($result);
2372
2373 $this->id = $obj->rowid;
2374
2375 $this->user_creation_id = $obj->fk_user_author;
2376 $this->user_closing_id = $obj->fk_user_cloture;
2377
2378 $this->date_creation = $this->db->jdate($obj->datec);
2379 $this->date_modification = $this->db->jdate($obj->datem);
2380 $this->date_cloture = $this->db->jdate($obj->datecloture);
2381 }
2382
2383 $this->db->free($result);
2384 } else {
2385 dol_print_error($this->db);
2386 }
2387 }
2388
2399 public function setCategories($categories)
2400 {
2401 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2402 return parent::setCategoriesCommon($categories, Categorie::TYPE_PROJECT);
2403 }
2404
2405
2413 public function getLinesArray($user, $loadRoleMode = 1)
2414 {
2415 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
2416 $taskstatic = new Task($this->db);
2417
2418 $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0, '', '-1', '', 0, 0, array(), 0, array(), 0, $loadRoleMode);
2419 return 1;
2420 }
2421
2439 public function sendEmail($text, $subject, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $addr_cc = "", $addr_bcc = "", $deliveryreceipt = 0, $msgishtml = -1, $errors_to = '', $moreinheader = '')
2440 {
2441 global $conf, $langs;
2442 // TODO EMAIL
2443
2444 return 1;
2445 }
2453 public function getKanbanView($option = '', $arraydata = null)
2454 {
2455 global $langs;
2456
2457 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2458
2459 $return = '<div class="box-flex-item box-flex-grow-zero">';
2460 $return .= '<div class="info-box info-box-sm">';
2461 $return .= '<span class="info-box-icon bg-infobox-action">';
2462 $return .= img_picto('', $this->public ? 'projectpub' : $this->picto);
2463 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2464 $return .= '</span>';
2465 $return .= '<div class="info-box-content">';
2466 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref);
2467 if ($this->hasDelay()) {
2468 $return .= img_warning($langs->trans('Late'));
2469 }
2470 $return .= '</span>';
2471 if ($selected >= 0) {
2472 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2473 }
2474 // Date
2475 /*
2476 if (property_exists($this, 'date_start') && $this->date_start) {
2477 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_start, 'day').'</>';
2478 }
2479 if (property_exists($this, 'date_end') && $this->date_end) {
2480 if ($this->date_start) {
2481 $return .= ' - ';
2482 } else {
2483 $return .= '<br>';
2484 }
2485 $return .= '<span class="info-box-label">'.dol_print_date($this->date_end, 'day').'</span>';
2486 }*/
2487 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
2488 $return .= '<br><div class="info-box-ref tdoverflowmax150 inline-block valignmiddle">'.$this->thirdparty->getNomUrl(1);
2489 $return .= '</div><div class="inline-block valignmiddle">';
2490 $return .= dol_print_phone($this->thirdparty->phone, $this->thirdparty->country_code, 0, $this->thirdparty->id, 'tel', 'hidenum', 'phone');
2491 $return .='</div>';
2492 }
2493 if (!empty($arraydata['assignedusers'])) {
2494 $return .= '<br>';
2495 if ($this->public) {
2496 $return .= img_picto($langs->trans('Visibility').': '.$langs->trans('SharedProject'), 'world', 'class="paddingrightonly valignmiddle"');
2497 //print $langs->trans('SharedProject');
2498 } else {
2499 $return .= img_picto($langs->trans('Visibility').': '.$langs->trans('PrivateProject'), 'private', 'class="paddingrightonly valignmiddle"');
2500 //print $langs->trans('PrivateProject');
2501 }
2502
2503 $return .= ' <span class="small valignmiddle">'.$arraydata['assignedusers'].'</span>';
2504 }
2505 /*if (property_exists($this, 'user_author_id')) {
2506 $return .= '<br><span class="info-box-label opacitymedium">'.$langs->trans("Author").'</span>';
2507 $return .= '<span> : '.$user->getNomUrl(1).'</span>';
2508 }*/
2509 $return .= '<br><div>'; // start div line status
2510 if ($this->usage_opportunity && $this->opp_status_code) {
2511 //$return .= '<br><span class="info-bo-label opacitymedium">'.$langs->trans("OpportunityStatusShort").'</span>';
2512 //$return .= '<div class="small inline-block">'.dol_trunc($langs->trans("OppStatus".$this->opp_status_code), 5).'</div>';
2513 $return .= '<div class="opacitymedium small marginrightonly inline-block" title="'.dol_escape_htmltag($langs->trans("OppStatus".$this->opp_status_code)).'">'.round($this->opp_percent).'%</div>';
2514 $return .= ' <div class="amount small marginrightonly inline-block">'.price($this->opp_amount).'</div>';
2515 }
2516 if (method_exists($this, 'getLibStatut')) {
2517 $return .= '<div class="info-box-status small inline-block valignmiddle">'.$this->getLibStatut(3).'</div>';
2518 }
2519 $return .= '</div>'; // end div line status
2520
2521 $return .= '</div>';
2522 $return .= '</div>';
2523 $return .= '</div>';
2524
2525 return $return;
2526 }
2527
2533 public function getChildren()
2534 {
2535 $children = [];
2536 $sql = 'SELECT rowid,title';
2537 $sql .= ' FROM '.MAIN_DB_PREFIX.'projet';
2538 $sql .= ' WHERE fk_project = '.((int) $this->id);
2539 $sql .= ' ORDER BY title';
2540 $result = $this->db->query($sql);
2541 if ($result) {
2542 $n = $this->db->num_rows($result);
2543 while ($n) {
2544 $children[] = $this->db->fetch_object($result);
2545 $n--;
2546 }
2547 $this->db->free($result);
2548 } else {
2549 dol_print_error($this->db);
2550 }
2551
2552 return $children;
2553 }
2554}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Definition security.php:604
$object ref
Definition info.php:79
Parent class of all other business classes (invoices, contracts, proposals, orders,...
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.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
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.
Class to manage Dolibarr database access.
Class to manage projects.
$user_author_id
Id of project creator. Not defined if shared project.
LibStatut($status, $mode=0)
Renvoi status label for a status.
getLibStatut($mode=0)
Return status label of object.
getElementCount($type, $tablename, $projectkey='fk_projet')
Return the count of a type of linked elements of this project.
getNomUrl($withpicto=0, $option='', $addlabel=0, $moreinpopup='', $sep=' - ', $notooltip=0, $save_lastsearch_value=-1, $morecss='', $save_pageforbacktolist='')
Return clickable name (with picto eventually)
fetchAndSetSubstitution($id, $key, $fetched=false)
Fetch object and substitute key.
getChildren()
Return array of sub-projects of the current project.
setValid($user, $notrigger=0)
Validate a project.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
const STATUS_VALIDATED
Open/Validated status.
$public
Tell if this is a public or private project.
getProjectsAuthorizedForUser($user, $mode=0, $list=0, $socid=0, $filter='')
Return array of projects a user has permission on, is affected to, or all projects.
create($user, $notrigger=0)
Create a project into database.
load_state_board()
Charge indicateurs this->nb pour le tableau de bord.
const STATUS_CLOSED
Closed status.
const STATUS_DRAFT
Draft status.
loadTimeSpentMonth($datestart, $taskid=0, $userid=0)
Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of projec...
__construct($db)
Constructor.
get_element_list($type, $tablename, $datefieldname='', $date_start='', $date_end='', $projectkey='fk_projet')
Return list of elements for type, linked to a project.
fetch($id, $ref='', $ref_ext='', $email_msgid='')
Get object from database.
shiftTaskDate($old_project_dt_start)
Shift project task date from current date to delta.
hasDelay()
Is the project delayed?
remove_element($tableName, $elementSelectId, $projectfield='fk_projet')
Associate element to a project.
setCategories($categories)
Sets object to supplied categories.
update_element($tableName, $elementSelectId)
Associate element to a project.
createFromClone(User $user, $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.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getLinesArray($user, $loadRoleMode=1)
Create an array of tasks of current project.
info($id)
Charge les informations d'ordre info dans l'objet commande.
getTooltipContentArray($params)
getTooltipContentArray
sendEmail($text, $subject, $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array(), $addr_cc="", $addr_bcc="", $deliveryreceipt=0, $msgishtml=-1, $errors_to='', $moreinheader='')
Function sending an email to the current member with the text supplied in parameter.
initAsSpecimen()
Initialise an instance with random values.
setClose($user)
Close a project.
loadTimeSpent($datestart, $taskid=0, $userid=0)
Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of projec...
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
Create an intervention document on disk using template defined into PROJECT_ADDON_PDF.
restrictedProjectArea(User $user, $mode='read')
Check if user has permission on current project.
deleteTasks($user)
Delete tasks with no children first, then task with children recursively.
update($user, $notrigger=0)
Update a project.
Class to manage tasks.
Class to manage Dolibarr users.
getWeekNumber($day, $month, $year)
Return week number.
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:124
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after 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)
dol_copy($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
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...
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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)
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_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a 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)
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1907