dolibarr 18.0.6
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{
38
42 public $element = 'project';
43
47 public $table_element = 'projet';
48
52 public $table_element_line = 'projet_task';
53
57 public $table_element_date;
58
62 public $fk_element = 'fk_projet';
63
68 public $ismultientitymanaged = 1;
69
73 public $isextrafieldmanaged = 1;
74
78 public $picto = 'project';
79
83 protected $table_ref_field = 'ref';
84
88 public $description;
89
93 public $title;
94
100 public $dateo;
101
105 public $date_start;
106
112 public $datee;
113
117 public $date_end;
118
122 public $date_start_event;
123
127 public $date_end_event;
128
132 public $location;
133
137 public $date_close;
138
139 public $socid; // To store id of thirdparty
140 public $thirdparty_name; // To store name of thirdparty (defined only in some cases)
141
143
147 public $fk_user_close;
148
152 public $user_close_id;
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 $statuts_short;
206 public $statuts_long;
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
229 public $datec;
230
234 public $date_c;
235
241 public $datem;
242
246 public $date_m;
247
251 public $lines;
252
277 // BEGIN MODULEBUILDER PROPERTIES
281 public $fields = array(
282 'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
283 'ref' =>array('type'=>'varchar(50)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'showoncombobox'=>1, 'position'=>15, 'searchall'=>1),
284 'title' =>array('type'=>'varchar(255)', 'label'=>'ProjectLabel', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>17, 'showoncombobox'=>2, 'searchall'=>1),
285 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>19),
286 'fk_soc' =>array('type'=>'integer', 'label'=>'Thirdparty', 'enabled'=>1, 'visible'=>0, 'position'=>20),
287 'dateo' =>array('type'=>'date', 'label'=>'DateStart', 'enabled'=>1, 'visible'=>1, 'position'=>30),
288 'datee' =>array('type'=>'date', 'label'=>'DateEnd', 'enabled'=>1, 'visible'=>1, 'position'=>35),
289 'description' =>array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>3, 'position'=>55, 'searchall'=>1),
290 'public' =>array('type'=>'integer', 'label'=>'Visibility', 'enabled'=>1, 'visible'=>1, 'position'=>65),
291 'fk_opp_status' =>array('type'=>'integer', 'label'=>'OpportunityStatusShort', 'enabled'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'visible'=>1, 'position'=>75),
292 'opp_percent' =>array('type'=>'double(5,2)', 'label'=>'OpportunityProbabilityShort', 'enabled'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'visible'=>1, 'position'=>80),
293 'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>85, 'searchall'=>1),
294 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>90, 'searchall'=>1),
295 'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPdf', 'enabled'=>1, 'visible'=>0, 'position'=>95),
296 'date_close' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>0, 'position'=>105),
297 'fk_user_close' =>array('type'=>'integer', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>0, 'position'=>110),
298 'opp_amount' =>array('type'=>'double(24,8)', 'label'=>'OpportunityAmountShort', 'enabled'=>1, 'visible'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'position'=>115),
299 'budget_amount' =>array('type'=>'double(24,8)', 'label'=>'Budget', 'enabled'=>1, 'visible'=>-1, 'position'=>119),
300 'usage_opportunity' =>array('type'=>'integer', 'label'=>'UsageOpportunity', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
301 'usage_task' =>array('type'=>'integer', 'label'=>'UsageTasks', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
302 'usage_bill_time' =>array('type'=>'integer', 'label'=>'UsageBillTimeShort', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
303 'usage_organize_event' =>array('type'=>'integer', 'label'=>'UsageOrganizeEvent', 'enabled'=>1, 'visible'=>-1, 'position'=>145),
304 // Properties for event organization
305 'date_start_event' =>array('type'=>'date', 'label'=>'DateStartEvent', 'enabled'=>"isModEnabled('eventorganization')", 'visible'=>1, 'position'=>200),
306 'date_end_event' =>array('type'=>'date', 'label'=>'DateEndEvent', 'enabled'=>"isModEnabled('eventorganization')", 'visible'=>1, 'position'=>201),
307 'location' =>array('type'=>'text', 'label'=>'Location', 'enabled'=>1, 'visible'=>3, 'position'=>55, 'searchall'=>202),
308 'accept_conference_suggestions' =>array('type'=>'integer', 'label'=>'AllowUnknownPeopleSuggestConf', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
309 'accept_booth_suggestions' =>array('type'=>'integer', 'label'=>'AllowUnknownPeopleSuggestBooth', 'enabled'=>1, 'visible'=>-1, 'position'=>211),
310 'price_registration' =>array('type'=>'double(24,8)', 'label'=>'PriceOfRegistration', 'enabled'=>1, 'visible'=>-1, 'position'=>212),
311 'price_booth' =>array('type'=>'double(24,8)', 'label'=>'PriceOfBooth', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
312 'max_attendees' =>array('type'=>'integer', 'label'=>'MaxNbOfAttendees', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
313 // Generic
314 'datec' =>array('type'=>'datetime', 'label'=>'DateCreationShort', 'enabled'=>1, 'visible'=>-2, 'position'=>400),
315 'tms' =>array('type'=>'timestamp', 'label'=>'DateModificationShort', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>405),
316 'fk_user_creat' =>array('type'=>'integer', 'label'=>'UserCreation', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>410),
317 'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModification', 'enabled'=>1, 'visible'=>0, 'position'=>415),
318 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'position'=>420),
319 'email_msgid'=>array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'enabled'=>1, 'visible'=>-1, 'position'=>450, 'help'=>'EmailMsgIDWhenSourceisEmail', 'csslist'=>'tdoverflowmax125'),
320 'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>500),
321 );
322 // END MODULEBUILDER PROPERTIES
323
327 const STATUS_DRAFT = 0;
328
333
337 const STATUS_CLOSED = 2;
338
344 public function __construct($db)
345 {
346 global $conf;
347
348 $this->db = $db;
349
350 $this->statuts_short = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
351 $this->statuts_long = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
352
353 global $conf;
354
355 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID)) {
356 $this->fields['rowid']['visible'] = 0;
357 }
358
359 if (empty($conf->global->PROJECT_USE_OPPORTUNITIES)) {
360 $this->fields['fk_opp_status']['enabled'] = 0;
361 $this->fields['opp_percent']['enabled'] = 0;
362 $this->fields['opp_amount']['enabled'] = 0;
363 $this->fields['usage_opportunity']['enabled'] = 0;
364 }
365
366 if (!empty($conf->global->PROJECT_HIDE_TASKS)) {
367 $this->fields['usage_bill_time']['visible'] = 0;
368 $this->fields['usage_task']['visible'] = 0;
369 }
370
371 if (empty($conf->eventorganization->enabled)) {
372 $this->fields['usage_organize_event']['visible'] = 0;
373 $this->fields['accept_conference_suggestions']['enabled'] = 0;
374 $this->fields['accept_booth_suggestions']['enabled'] = 0;
375 $this->fields['price_registration']['enabled'] = 0;
376 $this->fields['price_booth']['enabled'] = 0;
377 $this->fields['max_attendees']['enabled'] = 0;
378 }
379 }
380
388 public function create($user, $notrigger = 0)
389 {
390 global $conf, $langs;
391
392 $error = 0;
393 $ret = 0;
394
395 $now = dol_now();
396
397 // Clean parameters
398 $this->note_private = dol_substr($this->note_private, 0, 65535);
399 $this->note_public = dol_substr($this->note_public, 0, 65535);
400
401 // Check parameters
402 if (!trim($this->ref)) {
403 $this->error = 'ErrorFieldsRequired';
404 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
405 return -1;
406 }
407 if (!empty($conf->global->PROJECT_THIRDPARTY_REQUIRED) && !($this->socid > 0)) {
408 $this->error = 'ErrorFieldsRequired';
409 dol_syslog(get_class($this)."::create error -1 thirdparty not defined and option PROJECT_THIRDPARTY_REQUIRED is set", LOG_ERR);
410 return -1;
411 }
412
413 // Create project
414 $this->db->begin();
415
416 $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet (";
417 $sql .= "ref";
418 $sql .= ", title";
419 $sql .= ", description";
420 $sql .= ", fk_soc";
421 $sql .= ", fk_user_creat";
422 $sql .= ", fk_statut";
423 $sql .= ", fk_opp_status";
424 $sql .= ", opp_percent";
425 $sql .= ", public";
426 $sql .= ", datec";
427 $sql .= ", dateo";
428 $sql .= ", datee";
429 $sql .= ", opp_amount";
430 $sql .= ", budget_amount";
431 $sql .= ", usage_opportunity";
432 $sql .= ", usage_task";
433 $sql .= ", usage_bill_time";
434 $sql .= ", usage_organize_event";
435 $sql .= ", accept_conference_suggestions";
436 $sql .= ", accept_booth_suggestions";
437 $sql .= ", price_registration";
438 $sql .= ", price_booth";
439 $sql .= ", max_attendees";
440 $sql .= ", date_start_event";
441 $sql .= ", date_end_event";
442 $sql .= ", location";
443 $sql .= ", email_msgid";
444 $sql .= ", note_private";
445 $sql .= ", note_public";
446 $sql .= ", entity";
447 $sql .= ", ip";
448 $sql .= ") VALUES (";
449 $sql .= "'".$this->db->escape($this->ref)."'";
450 $sql .= ", '".$this->db->escape($this->title)."'";
451 $sql .= ", '".$this->db->escape($this->description)."'";
452 $sql .= ", ".($this->socid > 0 ? $this->socid : "null");
453 $sql .= ", ".((int) $user->id);
454 $sql .= ", ".(is_numeric($this->statut) ? ((int) $this->statut) : '0');
455 $sql .= ", ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? ((int) $this->opp_status) : 'NULL');
456 $sql .= ", ".(is_numeric($this->opp_percent) ? ((int) $this->opp_percent) : 'NULL');
457 $sql .= ", ".($this->public ? 1 : 0);
458 $sql .= ", '".$this->db->idate($now)."'";
459 $sql .= ", ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
460 $sql .= ", ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
461 $sql .= ", ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : 'null');
462 $sql .= ", ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : 'null');
463 $sql .= ", ".($this->usage_opportunity ? 1 : 0);
464 $sql .= ", ".($this->usage_task ? 1 : 0);
465 $sql .= ", ".($this->usage_bill_time ? 1 : 0);
466 $sql .= ", ".($this->usage_organize_event ? 1 : 0);
467 $sql .= ", ".($this->accept_conference_suggestions ? 1 : 0);
468 $sql .= ", ".($this->accept_booth_suggestions ? 1 : 0);
469 $sql .= ", ".(strcmp($this->price_registration, '') ? price2num($this->price_registration) : 'null');
470 $sql .= ", ".(strcmp($this->price_booth, '') ? price2num($this->price_booth) : 'null');
471 $sql .= ", ".(strcmp($this->max_attendees, '') ? ((int) $this->max_attendees) : 'null');
472 $sql .= ", ".($this->date_start_event != '' ? "'".$this->db->idate($this->date_start_event)."'" : 'null');
473 $sql .= ", ".($this->date_end_event != '' ? "'".$this->db->idate($this->date_end_event)."'" : 'null');
474 $sql .= ", ".($this->location ? "'".$this->db->escape($this->location)."'" : 'null');
475 $sql .= ", ".($this->email_msgid ? "'".$this->db->escape($this->email_msgid)."'" : 'null');
476 $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null');
477 $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null');
478 $sql .= ", ".((int) $conf->entity);
479 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
480 $sql .= ")";
481
482 dol_syslog(get_class($this)."::create", LOG_DEBUG);
483 $resql = $this->db->query($sql);
484 if ($resql) {
485 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet");
486 $ret = $this->id;
487
488 if (!$notrigger) {
489 // Call trigger
490 $result = $this->call_trigger('PROJECT_CREATE', $user);
491 if ($result < 0) {
492 $error++;
493 }
494 // End call triggers
495 }
496 } else {
497 $this->error = $this->db->lasterror();
498 $this->errno = $this->db->lasterrno();
499 $error++;
500 }
501
502 // Update extrafield
503 if (!$error) {
504 $result = $this->insertExtraFields();
505 if ($result < 0) {
506 $error++;
507 }
508 }
509
510 if (!$error && (getDolGlobalString('MAIN_DISABLEDRAFTSTATUS') || getDolGlobalString('MAIN_DISABLEDRAFTSTATUS_PROJECT'))) {
511 $res = $this->setValid($user);
512 if ($res < 0) {
513 $error++;
514 }
515 }
516
517 if (!$error) {
518 $this->db->commit();
519 return $ret;
520 } else {
521 $this->db->rollback();
522 return -1;
523 }
524 }
525
533 public function update($user, $notrigger = 0)
534 {
535 global $langs, $conf;
536
537 $error = 0;
538
539 // Clean parameters
540 $this->title = trim($this->title);
541 $this->description = trim($this->description);
542 if ($this->opp_amount < 0) {
543 $this->opp_amount = '';
544 }
545 if ($this->opp_percent < 0) {
546 $this->opp_percent = '';
547 }
548 if ($this->date_end && $this->date_end < $this->date_start) {
549 $this->error = $langs->trans("ErrorDateEndLowerThanDateStart");
550 $this->errors[] = $this->error;
551 $this->db->rollback();
552 dol_syslog(get_class($this)."::update error -3 ".$this->error, LOG_ERR);
553 return -3;
554 }
555
556 $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
557
558 if (dol_strlen(trim($this->ref)) > 0) {
559 $this->db->begin();
560
561 $sql = "UPDATE ".MAIN_DB_PREFIX."projet SET";
562 $sql .= " ref='".$this->db->escape($this->ref)."'";
563 $sql .= ", title = '".$this->db->escape($this->title)."'";
564 $sql .= ", description = '".$this->db->escape($this->description)."'";
565 $sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : "null");
566 $sql .= ", fk_statut = ".((int) $this->statut);
567 $sql .= ", fk_opp_status = ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null');
568 $sql .= ", opp_percent = ".((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null');
569 $sql .= ", public = ".($this->public ? 1 : 0);
570 $sql .= ", datec = ".($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null');
571 $sql .= ", dateo = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
572 $sql .= ", datee = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
573 $sql .= ", date_close = ".($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null');
574 $sql .= ", note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
575 $sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
576 $sql .= ", fk_user_close = ".($this->fk_user_close > 0 ? $this->fk_user_close : "null");
577 $sql .= ", opp_amount = ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null");
578 $sql .= ", budget_amount = ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null");
579 $sql .= ", fk_user_modif = ".$user->id;
580 $sql .= ", usage_opportunity = ".($this->usage_opportunity ? 1 : 0);
581 $sql .= ", usage_task = ".($this->usage_task ? 1 : 0);
582 $sql .= ", usage_bill_time = ".($this->usage_bill_time ? 1 : 0);
583 $sql .= ", usage_organize_event = ".($this->usage_organize_event ? 1 : 0);
584 $sql .= ", accept_conference_suggestions = ".($this->accept_conference_suggestions ? 1 : 0);
585 $sql .= ", accept_booth_suggestions = ".($this->accept_booth_suggestions ? 1 : 0);
586 $sql .= ", price_registration = ".(strcmp($this->price_registration, '') ? price2num($this->price_registration) : "null");
587 $sql .= ", price_booth = ".(strcmp($this->price_booth, '') ? price2num($this->price_booth) : "null");
588 $sql .= ", max_attendees = ".(strcmp($this->max_attendees, '') ? price2num($this->max_attendees) : "null");
589 $sql .= ", date_start_event = ".($this->date_start_event != '' ? "'".$this->db->idate($this->date_start_event)."'" : 'null');
590 $sql .= ", date_end_event = ".($this->date_end_event != '' ? "'".$this->db->idate($this->date_end_event)."'" : 'null');
591 $sql .= ", location = '".$this->db->escape($this->location)."'";
592 $sql .= ", entity = ".((int) $this->entity);
593 $sql .= " WHERE rowid = ".((int) $this->id);
594
595 dol_syslog(get_class($this)."::update", LOG_DEBUG);
596 $resql = $this->db->query($sql);
597 if ($resql) {
598 // Update extrafield
599 if (!$error) {
600 $result = $this->insertExtraFields();
601 if ($result < 0) {
602 $error++;
603 }
604 }
605
606 if (!$error && !$notrigger) {
607 // Call trigger
608 $result = $this->call_trigger('PROJECT_MODIFY', $user);
609 if ($result < 0) {
610 $error++;
611 }
612 // End call triggers
613 }
614
615 if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
616 // We remove directory
617 if ($conf->project->dir_output) {
618 $olddir = $conf->project->dir_output."/".dol_sanitizeFileName($this->oldcopy->ref);
619 $newdir = $conf->project->dir_output."/".dol_sanitizeFileName($this->ref);
620 if (file_exists($olddir)) {
621 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
622 $res = @rename($olddir, $newdir);
623 if (!$res) {
624 $langs->load("errors");
625 $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
626 $error++;
627 }
628 }
629 }
630 }
631 if (!$error) {
632 $this->db->commit();
633 $result = 1;
634 } else {
635 $this->db->rollback();
636 $result = -1;
637 }
638 } else {
639 $this->error = $this->db->lasterror();
640 $this->errors[] = $this->error;
641 $this->db->rollback();
642 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
643 $result = -4;
644 } else {
645 $result = -2;
646 }
647 dol_syslog(get_class($this)."::update error ".$result." ".$this->error, LOG_ERR);
648 }
649 } else {
650 dol_syslog(get_class($this)."::update ref null");
651 $result = -1;
652 }
653
654 return $result;
655 }
656
666 public function fetch($id, $ref = '', $ref_ext = '', $email_msgid = '')
667 {
668 if (empty($id) && empty($ref) && empty($ref_ext) && empty($email_msgid)) {
669 dol_syslog(get_class($this)."::fetch Bad parameters", LOG_WARNING);
670 return -1;
671 }
672
673 $sql = "SELECT rowid, entity, ref, title, description, public, datec, opp_amount, budget_amount,";
674 $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,";
675 $sql .= " note_private, note_public, model_pdf, usage_opportunity, usage_task, usage_bill_time, usage_organize_event, email_msgid,";
676 $sql .= " accept_conference_suggestions, accept_booth_suggestions, price_registration, price_booth, max_attendees, date_start_event, date_end_event, location, extraparams";
677 $sql .= " FROM ".MAIN_DB_PREFIX."projet";
678 if (!empty($id)) {
679 $sql .= " WHERE rowid = ".((int) $id);
680 } else {
681 $sql .= " WHERE entity IN (".getEntity('project').")";
682 if (!empty($ref)) {
683 $sql .= " AND ref = '".$this->db->escape($ref)."'";
684 } elseif (!empty($ref_ext)) {
685 $sql .= " AND ref_ext = '".$this->db->escape($ref_ext)."'";
686 } else {
687 $sql .= " AND email_msgid = '".$this->db->escape($email_msgid)."'";
688 }
689 }
690
691 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
692 $resql = $this->db->query($sql);
693 if ($resql) {
694 $num_rows = $this->db->num_rows($resql);
695
696 if ($num_rows) {
697 $obj = $this->db->fetch_object($resql);
698
699 $this->id = $obj->rowid;
700 $this->entity = $obj->entity;
701 $this->ref = $obj->ref;
702 $this->title = $obj->title;
703 $this->description = $obj->description;
704 $this->date_c = $this->db->jdate($obj->datec);
705 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
706 $this->date_m = $this->db->jdate($obj->tms);
707 $this->datem = $this->db->jdate($obj->tms); // TODO deprecated
708 $this->date_start = $this->db->jdate($obj->date_start);
709 $this->date_end = $this->db->jdate($obj->date_end);
710 $this->date_close = $this->db->jdate($obj->date_close);
711 $this->note_private = $obj->note_private;
712 $this->note_public = $obj->note_public;
713 $this->socid = $obj->fk_soc;
714 $this->user_author_id = $obj->fk_user_creat;
715 $this->user_modification_id = $obj->fk_user_modif;
716 $this->user_close_id = $obj->fk_user_close;
717 $this->public = $obj->public;
718 $this->statut = $obj->status; // deprecated
719 $this->status = $obj->status;
720 $this->opp_status = $obj->fk_opp_status;
721 $this->opp_amount = $obj->opp_amount;
722 $this->opp_percent = $obj->opp_percent;
723 $this->budget_amount = $obj->budget_amount;
724 $this->model_pdf = $obj->model_pdf;
725 $this->modelpdf = $obj->model_pdf; // deprecated
726 $this->usage_opportunity = (int) $obj->usage_opportunity;
727 $this->usage_task = (int) $obj->usage_task;
728 $this->usage_bill_time = (int) $obj->usage_bill_time;
729 $this->usage_organize_event = (int) $obj->usage_organize_event;
730 $this->accept_conference_suggestions = (int) $obj->accept_conference_suggestions;
731 $this->accept_booth_suggestions = (int) $obj->accept_booth_suggestions;
732 $this->price_registration = $obj->price_registration;
733 $this->price_booth = $obj->price_booth;
734 $this->max_attendees = $obj->max_attendees;
735 $this->date_start_event = $this->db->jdate($obj->date_start_event);
736 $this->date_end_event = $this->db->jdate($obj->date_end_event);
737 $this->location = $obj->location;
738 $this->email_msgid = $obj->email_msgid;
739 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
740
741 $this->db->free($resql);
742
743 // Retrieve all extrafield
744 // fetch optionals attributes and labels
745 $this->fetch_optionals();
746
747 return 1;
748 }
749
750 $this->db->free($resql);
751
752 return 0;
753 } else {
754 $this->error = $this->db->lasterror();
755 $this->errors[] = $this->db->lasterror();
756 return -1;
757 }
758 }
759
760 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
772 public function get_element_list($type, $tablename, $datefieldname = '', $date_start = '', $date_end = '', $projectkey = 'fk_projet')
773 {
774 // phpcs:enable
775
776 global $hookmanager;
777
778 $elements = array();
779
780 if ($this->id <= 0) {
781 return $elements;
782 }
783
784 $ids = $this->id;
785
786 if ($type == 'agenda') {
787 $sql = "SELECT id as rowid FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project IN (".$this->db->sanitize($ids).") AND entity IN (".getEntity('agenda').")";
788 } elseif ($type == 'expensereport') {
789 $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).")";
790 } elseif ($type == 'project_task') {
791 $sql = "SELECT DISTINCT pt.rowid FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet IN (".$this->db->sanitize($ids).")";
792 } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user
793 $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).")";
794 } elseif ($type == 'stock_mouvement') {
795 $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 (".$this->db->sanitize($ids).") AND ms.type_mouvement = 1";
796 } elseif ($type == 'loan') {
797 $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).")";
798 } else {
799 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." IN (".$this->db->sanitize($ids).") AND entity IN (".getEntity($type).")";
800 }
801
802 if ($date_start > 0 && $type == 'loan') {
803 $sql .= " AND (dateend > '".$this->db->idate($date_start)."' OR dateend IS NULL)";
804 } elseif ($date_start > 0 && ($type != 'project_task')) { // For table project_taks, we want the filter on date apply on project_time_spent table
805 if (empty($datefieldname) && !empty($this->table_element_date)) {
806 $datefieldname = $this->table_element_date;
807 }
808 if (empty($datefieldname)) {
809 return 'Error this object has no date field defined';
810 }
811 $sql .= " AND (".$datefieldname." >= '".$this->db->idate($date_start)."' OR ".$datefieldname." IS NULL)";
812 }
813
814 if ($date_end > 0 && $type == 'loan') {
815 $sql .= " AND (datestart < '".$this->db->idate($date_end)."' OR datestart IS NULL)";
816 } elseif ($date_end > 0 && ($type != 'project_task')) { // For table project_taks, we want the filter on date apply on project_time_spent table
817 if (empty($datefieldname) && !empty($this->table_element_date)) {
818 $datefieldname = $this->table_element_date;
819 }
820 if (empty($datefieldname)) {
821 return 'Error this object has no date field defined';
822 }
823 $sql .= " AND (".$datefieldname." <= '".$this->db->idate($date_end)."' OR ".$datefieldname." IS NULL)";
824 }
825
826 $parameters = array(
827 'sql'=>$sql,
828 'type' => $type,
829 'tablename' => $tablename,
830 'datefieldname' => $datefieldname,
831 'dates' => $date_start,
832 'datee' => $date_end,
833 'fk_projet' => $projectkey,
834 'ids' => $ids,
835 );
836 $reshook = $hookmanager->executeHooks('getElementList', $parameters);
837 if ($reshook > 0) {
838 $sql = $hookmanager->resPrint;
839 } else {
840 $sql .= $hookmanager->resPrint;
841 }
842
843 if (!$sql) {
844 return -1;
845 }
846
847 //print $sql;
848 dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG);
849 $result = $this->db->query($sql);
850 if ($result) {
851 $nump = $this->db->num_rows($result);
852 if ($nump) {
853 $i = 0;
854 while ($i < $nump) {
855 $obj = $this->db->fetch_object($result);
856
857 $elements[$i] = $obj->rowid.(empty($obj->fk_user) ? '' : '_'.$obj->fk_user);
858
859 $i++;
860 }
861 $this->db->free($result);
862 }
863
864 /* Return array even if empty*/
865 return $elements;
866 } else {
867 dol_print_error($this->db);
868 }
869 }
870
878 public function delete($user, $notrigger = 0)
879 {
880 global $langs, $conf;
881 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
882
883 $error = 0;
884
885 $this->db->begin();
886
887 if (!$error) {
888 // Delete linked contacts
889 $res = $this->delete_linked_contact();
890 if ($res < 0) {
891 $this->error = 'ErrorFailToDeleteLinkedContact';
892 //$error++;
893 $this->db->rollback();
894 return 0;
895 }
896 }
897
898 // Set fk_projet into elements to null
899 $listoftables = array(
900 'propal' => 'fk_projet',
901 'commande' => 'fk_projet',
902 'facture' => 'fk_projet',
903 'supplier_proposal' => 'fk_projet',
904 'commande_fournisseur' => 'fk_projet',
905 'facture_fourn' => 'fk_projet',
906 'expensereport_det' => 'fk_projet',
907 'contrat' => 'fk_projet',
908 'fichinter' => 'fk_projet',
909 'don' => array('field' => 'fk_projet', 'module' => 'don'),
910 'actioncomm' => 'fk_project',
911 'mrp_mo' => array('field' => 'fk_project', 'module' => 'mrp'),
912 'entrepot' => 'fk_project',
913 );
914 foreach ($listoftables as $key => $value) {
915 if (is_array($value)) {
916 if (!isModEnabled($value['module'])) {
917 continue;
918 }
919 $fieldname = $value['field'];
920 } else {
921 $fieldname = $value;
922 }
923 $sql = "UPDATE ".MAIN_DB_PREFIX.$key." SET ".$fieldname." = NULL where ".$fieldname." = ".((int) $this->id);
924
925 $resql = $this->db->query($sql);
926 if (!$resql) {
927 $this->errors[] = $this->db->lasterror();
928 $error++;
929 break;
930 }
931 }
932
933 // Remove linked categories.
934 if (!$error) {
935 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_project";
936 $sql .= " WHERE fk_project = ".((int) $this->id);
937
938 $result = $this->db->query($sql);
939 if (!$result) {
940 $error++;
941 $this->errors[] = $this->db->lasterror();
942 }
943 }
944
945 // Fetch tasks
946 $this->getLinesArray($user, 0);
947
948 // Delete tasks
949 $ret = $this->deleteTasks($user);
950 if ($ret < 0) {
951 $error++;
952 }
953
954
955 // Delete all child tables
956 if (!$error) {
957 $elements = array('categorie_project'); // elements to delete. TODO Make goodway to delete
958 foreach ($elements as $table) {
959 if (!$error) {
960 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$table;
961 $sql .= " WHERE fk_project = ".((int) $this->id);
962
963 $result = $this->db->query($sql);
964 if (!$result) {
965 $error++;
966 $this->errors[] = $this->db->lasterror();
967 }
968 }
969 }
970 }
971
972 if (!$error) {
973 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_extrafields";
974 $sql .= " WHERE fk_object = ".((int) $this->id);
975
976 $resql = $this->db->query($sql);
977 if (!$resql) {
978 $this->errors[] = $this->db->lasterror();
979 $error++;
980 }
981 }
982
983 // Delete project
984 if (!$error) {
985 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet";
986 $sql .= " WHERE rowid=".((int) $this->id);
987
988 $resql = $this->db->query($sql);
989 if (!$resql) {
990 $this->errors[] = $langs->trans("CantRemoveProject", $langs->transnoentitiesnoconv("ProjectOverview"));
991 $error++;
992 }
993 }
994
995
996
997 if (empty($error)) {
998 // We remove directory
999 $projectref = dol_sanitizeFileName($this->ref);
1000 if ($conf->project->dir_output) {
1001 $dir = $conf->project->dir_output."/".$projectref;
1002 if (file_exists($dir)) {
1003 $res = @dol_delete_dir_recursive($dir);
1004 if (!$res) {
1005 $this->errors[] = 'ErrorFailToDeleteDir';
1006 $error++;
1007 }
1008 }
1009 }
1010
1011 if (!$notrigger) {
1012 // Call trigger
1013 $result = $this->call_trigger('PROJECT_DELETE', $user);
1014
1015 if ($result < 0) {
1016 $error++;
1017 }
1018 // End call triggers
1019 }
1020 }
1021
1022 if (empty($error)) {
1023 $this->db->commit();
1024 return 1;
1025 } else {
1026 foreach ($this->errors as $errmsg) {
1027 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1028 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1029 }
1030 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
1031 $this->db->rollback();
1032 return -1;
1033 }
1034 }
1035
1044 public function getElementCount($type, $tablename, $projectkey = 'fk_projet')
1045 {
1046 if ($this->id <= 0) {
1047 return 0;
1048 }
1049
1050 if ($type == 'agenda') {
1051 $sql = "SELECT COUNT(id) as nb FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project = ".((int) $this->id)." AND entity IN (".getEntity('agenda').")";
1052 } elseif ($type == 'expensereport') {
1053 $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);
1054 } elseif ($type == 'project_task') {
1055 $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet = ".((int) $this->id);
1056 } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user
1057 $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);
1058 } elseif ($type == 'stock_mouvement') {
1059 $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";
1060 } elseif ($type == 'loan') {
1061 $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);
1062 } else {
1063 $sql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." = ".((int) $this->id)." AND entity IN (".getEntity($type).")";
1064 }
1065
1066 $result = $this->db->query($sql);
1067
1068 if (!$result) {
1069 return 0;
1070 }
1071
1072 $obj = $this->db->fetch_object($result);
1073
1074 $this->db->free($result);
1075
1076 return $obj->nb;
1077 }
1078
1085 public function deleteTasks($user)
1086 {
1087 $countTasks = count($this->lines);
1088 $deleted = false;
1089 if ($countTasks) {
1090 foreach ($this->lines as $task) {
1091 if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them)
1092 $deleted = true;
1093 $ret = $task->delete($user);
1094 if ($ret <= 0) {
1095 $this->errors[] = $this->db->lasterror();
1096 return -1;
1097 }
1098 }
1099 }
1100 }
1101 $this->getLinesArray($user);
1102 if ($deleted && count($this->lines) < $countTasks) {
1103 if (count($this->lines)) {
1104 $this->deleteTasks($this->lines);
1105 }
1106 }
1107
1108 return 1;
1109 }
1110
1118 public function setValid($user, $notrigger = 0)
1119 {
1120 global $langs, $conf;
1121
1122 $error = 0;
1123
1124 // Protection
1125 if ($this->status == self::STATUS_VALIDATED) {
1126 dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
1127 return 0;
1128 }
1129
1130 // Check parameters
1131 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title)) {
1132 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("Label")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1133 return -1;
1134 }
1135
1136 $this->db->begin();
1137
1138 $sql = "UPDATE ".MAIN_DB_PREFIX."projet";
1139 $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1140 $sql .= " WHERE rowid = ".((int) $this->id);
1141 //$sql .= " AND entity = ".((int) $conf->entity); // Disabled, when we use the ID for the where, we must not add any other search condition
1142
1143 dol_syslog(get_class($this)."::setValid", LOG_DEBUG);
1144 $resql = $this->db->query($sql);
1145 if ($resql) {
1146 // Call trigger
1147 if (empty($notrigger)) {
1148 $result = $this->call_trigger('PROJECT_VALIDATE', $user);
1149 if ($result < 0) {
1150 $error++;
1151 }
1152 // End call triggers
1153 }
1154
1155 if (!$error) {
1156 $this->statut = 1;
1157 $this->db->commit();
1158 return 1;
1159 } else {
1160 $this->db->rollback();
1161 $this->error = join(',', $this->errors);
1162 dol_syslog(get_class($this)."::setValid ".$this->error, LOG_ERR);
1163 return -1;
1164 }
1165 } else {
1166 $this->db->rollback();
1167 $this->error = $this->db->lasterror();
1168 return -1;
1169 }
1170 }
1171
1178 public function setClose($user)
1179 {
1180 global $langs, $conf;
1181
1182 $now = dol_now();
1183
1184 $error = 0;
1185
1186 if ($this->statut != self::STATUS_CLOSED) {
1187 $this->db->begin();
1188
1189 $sql = "UPDATE ".MAIN_DB_PREFIX."projet";
1190 $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", fk_user_close = ".((int) $user->id).", date_close = '".$this->db->idate($now)."'";
1191 $sql .= " WHERE rowid = ".((int) $this->id);
1192 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1193
1194 if (!empty($conf->global->PROJECT_USE_OPPORTUNITIES)) {
1195 // TODO What to do if fk_opp_status is not code 'WON' or 'LOST'
1196 }
1197
1198 dol_syslog(get_class($this)."::setClose", LOG_DEBUG);
1199 $resql = $this->db->query($sql);
1200 if ($resql) {
1201 // Call trigger
1202 $result = $this->call_trigger('PROJECT_CLOSE', $user);
1203 if ($result < 0) {
1204 $error++;
1205 }
1206 // End call triggers
1207
1208 if (!$error) {
1209 $this->statut = 2;
1210 $this->db->commit();
1211 return 1;
1212 } else {
1213 $this->db->rollback();
1214 $this->error = join(',', $this->errors);
1215 dol_syslog(get_class($this)."::setClose ".$this->error, LOG_ERR);
1216 return -1;
1217 }
1218 } else {
1219 $this->db->rollback();
1220 $this->error = $this->db->lasterror();
1221 return -1;
1222 }
1223 }
1224
1225 return 0;
1226 }
1227
1234 public function getLibStatut($mode = 0)
1235 {
1236 return $this->LibStatut(isset($this->statut) ? $this->statut : $this->status, $mode);
1237 }
1238
1239 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1247 public function LibStatut($status, $mode = 0)
1248 {
1249 // phpcs:enable
1250 global $langs;
1251
1252 $statustrans = array(
1253 0 => 'status0',
1254 1 => 'status4',
1255 2 => 'status6',
1256 );
1257
1258 $statusClass = 'status0';
1259 if (!empty($statustrans[$status])) {
1260 $statusClass = $statustrans[$status];
1261 }
1262
1263 return dolGetStatus($langs->transnoentitiesnoconv($this->statuts_long[$status]), $langs->transnoentitiesnoconv($this->statuts_short[$status]), '', $statusClass, $mode);
1264 }
1265
1273 public function getTooltipContentArray($params)
1274 {
1275 global $conf, $langs;
1276
1277 $langs->load('projects');
1278 $option = $params['option'] ?? '';
1279 $moreinpopup = $params['morinpopup'] ?? '';
1280
1281 $datas = [];
1282 if ($option != 'nolink') {
1283 $datas['picto'] = img_picto('', $this->picto, 'class="pictofixedwidth"').' <u class="paddingrightonly">'.$langs->trans("Project").'</u>';
1284 }
1285 if (isset($this->status)) {
1286 $datas['picto'] .= ' '.$this->getLibStatut(5);
1287 }
1288 $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
1289 $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
1290 if (isset($this->public)) {
1291 $datas['visibility'] = '<br><b>'.$langs->trans("Visibility").":</b> ";
1292 $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"));
1293 }
1294 if (!empty($this->thirdparty_name)) {
1295 $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
1296 }
1297 if (!empty($this->date_start)) {
1298 $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
1299 }
1300 if (!empty($this->date_end)) {
1301 $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
1302 }
1303 if ($moreinpopup) {
1304 $datas['moreinpopup'] = '<br>'.$moreinpopup;
1305 }
1306
1307 return $datas;
1308 }
1309
1324 public function getNomUrl($withpicto = 0, $option = '', $addlabel = 0, $moreinpopup = '', $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1, $morecss = '', $save_pageforbacktolist = '')
1325 {
1326 global $conf, $langs, $user, $hookmanager;
1327
1328 if (!empty($conf->dol_no_mouse_hover)) {
1329 $notooltip = 1; // Force disable tooltips
1330 }
1331
1332 $result = '';
1333 if (!empty($conf->global->PROJECT_OPEN_ALWAYS_ON_TAB)) {
1334 $option = $conf->global->PROJECT_OPEN_ALWAYS_ON_TAB;
1335 }
1336 $params = [
1337 'id' => $this->id,
1338 'objecttype' => $this->element,
1339 'moreinpopup' => $moreinpopup,
1340 'option' => $option,
1341 ];
1342 $classfortooltip = 'classfortooltip';
1343 $dataparams = '';
1344 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1345 $classfortooltip = 'classforajaxtooltip';
1346 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1347 $label = '';
1348 } else {
1349 $label = implode($this->getTooltipContentArray($params));
1350 }
1351
1352 $url = '';
1353 if ($option != 'nolink') {
1354 if (preg_match('/\.php$/', $option)) {
1355 $url = dol_buildpath($option, 1).'?id='.$this->id;
1356 } elseif ($option == 'task') {
1357 $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id;
1358 } elseif ($option == 'preview') {
1359 $url = DOL_URL_ROOT.'/projet/element.php?id='.$this->id;
1360 } elseif ($option == 'eventorganization') {
1361 $url = DOL_URL_ROOT.'/eventorganization/conferenceorbooth_list.php?projectid='.$this->id;
1362 } else {
1363 $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id;
1364 }
1365 // Add param to save lastsearch_values or not
1366 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1367 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1368 $add_save_lastsearch_values = 1;
1369 }
1370 if ($add_save_lastsearch_values) {
1371 $url .= '&save_lastsearch_values=1';
1372 }
1373 $add_save_backpagefor = ($save_pageforbacktolist ? 1 : 0);
1374 if ($add_save_backpagefor) {
1375 $url .= "&save_pageforbacktolist=".urlencode($save_pageforbacktolist);
1376 }
1377 }
1378
1379 $linkclose = '';
1380 if (empty($notooltip) && $user->hasRight('projet', 'lire')) {
1381 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1382 $label = $langs->trans("ShowProject");
1383 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1384 }
1385 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1386 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1387 } else {
1388 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1389 }
1390
1391 $picto = 'projectpub';
1392 if (!$this->public) {
1393 $picto = 'project';
1394 }
1395
1396 $linkstart = '<a href="'.$url.'"';
1397 $linkstart .= $linkclose.'>';
1398 $linkend = '</a>';
1399
1400 $result .= $linkstart;
1401 if ($withpicto) {
1402 $result .= img_object(($notooltip ? '' : $label), $picto, 'class="pictofixedwidth em088"', 0, 0, $notooltip ? 0 : 1);
1403 }
1404 if ($withpicto != 2) {
1405 $result .= $this->ref;
1406 }
1407 $result .= $linkend;
1408 if ($withpicto != 2) {
1409 $result .= (($addlabel && $this->title) ? '<span class="opacitymedium">'.$sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)).'</span>' : '');
1410 }
1411
1412 global $action;
1413 $hookmanager->initHooks(array('projectdao'));
1414 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1415 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1416 if ($reshook > 0) {
1417 $result = $hookmanager->resPrint;
1418 } else {
1419 $result .= $hookmanager->resPrint;
1420 }
1421
1422 return $result;
1423 }
1424
1432 public function initAsSpecimen()
1433 {
1434 global $user, $langs, $conf;
1435
1436 $now = dol_now();
1437
1438 // Initialise parameters
1439 $this->id = 0;
1440 $this->ref = 'SPECIMEN';
1441 $this->entity = $conf->entity;
1442 $this->specimen = 1;
1443 $this->socid = 1;
1444 $this->date_c = $now;
1445 $this->date_m = $now;
1446 $this->date_start = $now;
1447 $this->date_end = $now + (3600 * 24 * 365);
1448 $this->note_public = 'SPECIMEN';
1449 $this->fk_ele = 20000;
1450 $this->opp_amount = 20000;
1451 $this->budget_amount = 10000;
1452
1453 $this->usage_opportunity = 1;
1454 $this->usage_task = 1;
1455 $this->usage_bill_time = 1;
1456 $this->usage_organize_event = 1;
1457
1458 /*
1459 $nbp = mt_rand(1, 9);
1460 $xnbp = 0;
1461 while ($xnbp < $nbp)
1462 {
1463 $line = new Task($this->db);
1464 $line->fk_project = 0;
1465 $line->label = $langs->trans("Label") . " " . $xnbp;
1466 $line->description = $langs->trans("Description") . " " . $xnbp;
1467
1468 $this->lines[]=$line;
1469 $xnbp++;
1470 }
1471 */
1472 }
1473
1481 public function restrictedProjectArea(User $user, $mode = 'read')
1482 {
1483 // To verify role of users
1484 $userAccess = 0;
1485 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))) {
1486 $userAccess = 1;
1487 } elseif ($this->public && (($mode == 'read' && !empty($user->rights->projet->lire)) || ($mode == 'write' && !empty($user->rights->projet->creer)) || ($mode == 'delete' && !empty($user->rights->projet->supprimer)))) {
1488 $userAccess = 1;
1489 } else { // No access due to permission to read all projects, so we check if we are a contact of project
1490 foreach (array('internal', 'external') as $source) {
1491 $userRole = $this->liste_contact(4, $source);
1492 $num = count($userRole);
1493
1494 $nblinks = 0;
1495 while ($nblinks < $num) {
1496 if ($source == 'internal' && $user->id == $userRole[$nblinks]['id']) { // $userRole[$nblinks]['id'] is id of user (llx_user) for internal contacts
1497 if ($mode == 'read' && $user->rights->projet->lire) {
1498 $userAccess++;
1499 }
1500 if ($mode == 'write' && $user->rights->projet->creer) {
1501 $userAccess++;
1502 }
1503 if ($mode == 'delete' && $user->rights->projet->supprimer) {
1504 $userAccess++;
1505 }
1506 }
1507 if ($source == 'external' && $user->socid > 0 && $user->socid == $userRole[$nblinks]['socid']) { // $userRole[$nblinks]['id'] is id of contact (llx_socpeople) or external contacts
1508 if ($mode == 'read' && $user->rights->projet->lire) {
1509 $userAccess++;
1510 }
1511 if ($mode == 'write' && $user->rights->projet->creer) {
1512 $userAccess++;
1513 }
1514 if ($mode == 'delete' && $user->rights->projet->supprimer) {
1515 $userAccess++;
1516 }
1517 }
1518 $nblinks++;
1519 }
1520 }
1521 //if (empty($nblinks)) // If nobody has permission, we grant creator
1522 //{
1523 // if ((!empty($this->user_author_id) && $this->user_author_id == $user->id))
1524 // {
1525 // $userAccess = 1;
1526 // }
1527 //}
1528 }
1529
1530 return ($userAccess ? $userAccess : -1);
1531 }
1532
1543 public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '')
1544 {
1545 $projects = array();
1546 $temp = array();
1547
1548 $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref";
1549 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1550 if ($mode == 0) {
1551 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_contact as ec ON ec.element_id = p.rowid";
1552 } elseif ($mode == 1) {
1553 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1554 } elseif ($mode == 2) {
1555 // No filter. Use this if user has permission to see all project
1556 }
1557 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1558 // Internal users must see project he is contact to even if project linked to a third party he can't see.
1559 //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).")";
1560 if ($socid > 0) {
1561 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
1562 }
1563
1564 // Get id of types of contacts for projects (This list never contains a lot of elements)
1565 $listofprojectcontacttype = array();
1566 $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
1567 $sql2 .= " WHERE ctc.element = '".$this->db->escape($this->element)."'";
1568 $sql2 .= " AND ctc.source = 'internal'";
1569 $resql = $this->db->query($sql2);
1570 if ($resql) {
1571 while ($obj = $this->db->fetch_object($resql)) {
1572 $listofprojectcontacttype[$obj->rowid] = $obj->code;
1573 }
1574 } else {
1575 dol_print_error($this->db);
1576 }
1577 if (count($listofprojectcontacttype) == 0) {
1578 $listofprojectcontacttype[0] = '0'; // To avoid syntax error if not found
1579 }
1580
1581 if ($mode == 0) {
1582 $sql .= " AND ( p.public = 1";
1583 $sql .= " OR ( ec.fk_c_type_contact IN (".$this->db->sanitize(join(',', array_keys($listofprojectcontacttype))).")";
1584 $sql .= " AND ec.fk_socpeople = ".((int) $user->id).")";
1585 $sql .= " )";
1586 } elseif ($mode == 1) {
1587 $sql .= " AND ec.element_id = p.rowid";
1588 $sql .= " AND (";
1589 $sql .= " ( ec.fk_c_type_contact IN (".$this->db->sanitize(join(',', array_keys($listofprojectcontacttype))).")";
1590 $sql .= " AND ec.fk_socpeople = ".((int) $user->id).")";
1591 $sql .= " )";
1592 } elseif ($mode == 2) {
1593 // No filter. Use this if user has permission to see all project
1594 }
1595
1596 $sql .= $filter;
1597 //print $sql;
1598
1599 $resql = $this->db->query($sql);
1600 if ($resql) {
1601 $num = $this->db->num_rows($resql);
1602 $i = 0;
1603 while ($i < $num) {
1604 $row = $this->db->fetch_row($resql);
1605 $projects[$row[0]] = $row[1];
1606 $temp[] = $row[0];
1607 $i++;
1608 }
1609
1610 $this->db->free($resql);
1611
1612 if ($list) {
1613 if (empty($temp)) {
1614 return '0';
1615 }
1616 $result = implode(',', $temp);
1617 return $result;
1618 }
1619 } else {
1620 dol_print_error($this->db);
1621 }
1622
1623 return $projects;
1624 }
1625
1641 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)
1642 {
1643 global $langs, $conf;
1644
1645 $error = 0;
1646
1647 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);
1648
1649 $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now()));
1650
1651 $clone_project = new Project($this->db);
1652
1653 $clone_project->context['createfromclone'] = 'createfromclone';
1654
1655 $this->db->begin();
1656
1657 // Load source object
1658 $clone_project->fetch($fromid);
1659 $clone_project->fetch_optionals();
1660 if ($newthirdpartyid > 0) {
1661 $clone_project->socid = $newthirdpartyid;
1662 }
1663 $clone_project->fetch_thirdparty();
1664
1665 $orign_dt_start = $clone_project->date_start;
1666 $orign_project_ref = $clone_project->ref;
1667
1668 $clone_project->id = 0;
1669 if ($move_date) {
1670 $clone_project->date_start = $now;
1671 if (!(empty($clone_project->date_end))) {
1672 $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start);
1673 }
1674 }
1675
1676 $clone_project->date_c = $now;
1677
1678 if (!$clone_note) {
1679 $clone_project->note_private = '';
1680 $clone_project->note_public = '';
1681 }
1682
1683 //Generate next ref
1684 $defaultref = '';
1685 $obj = empty($conf->global->PROJECT_ADDON) ? 'mod_project_simple' : $conf->global->PROJECT_ADDON;
1686 // Search template files
1687 $file = ''; $classname = ''; $filefound = 0;
1688 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1689 foreach ($dirmodels as $reldir) {
1690 $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0);
1691 if (file_exists($file)) {
1692 $filefound = 1;
1693 dol_include_once($reldir."core/modules/project/".$obj.'.php');
1694 $modProject = new $obj;
1695 $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project);
1696 break;
1697 }
1698 }
1699 if (is_numeric($defaultref) && $defaultref <= 0) {
1700 $defaultref = '';
1701 }
1702
1703 $clone_project->ref = $defaultref;
1704 $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title;
1705
1706 // Create clone
1707 $result = $clone_project->create($user, $notrigger);
1708
1709 // Other options
1710 if ($result < 0) {
1711 $this->error .= $clone_project->error;
1712 $error++;
1713 }
1714
1715 if (!$error) {
1716 //Get the new project id
1717 $clone_project_id = $clone_project->id;
1718
1719 //Note Update
1720 if (!$clone_note) {
1721 $clone_project->note_private = '';
1722 $clone_project->note_public = '';
1723 } else {
1724 $this->db->begin();
1725 $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES | ENT_HTML5), '_public');
1726 if ($res < 0) {
1727 $this->error .= $clone_project->error;
1728 $error++;
1729 $this->db->rollback();
1730 } else {
1731 $this->db->commit();
1732 }
1733
1734 $this->db->begin();
1735 $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES | ENT_HTML5), '_private');
1736 if ($res < 0) {
1737 $this->error .= $clone_project->error;
1738 $error++;
1739 $this->db->rollback();
1740 } else {
1741 $this->db->commit();
1742 }
1743 }
1744
1745 //Duplicate contact
1746 if ($clone_contact) {
1747 $origin_project = new Project($this->db);
1748 $origin_project->fetch($fromid);
1749
1750 foreach (array('internal', 'external') as $source) {
1751 $tab = $origin_project->liste_contact(-1, $source);
1752 if (is_array($tab) && count($tab)>0) {
1753 foreach ($tab as $contacttoadd) {
1754 $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger);
1755 if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1756 $langs->load("errors");
1757 $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1758 $error++;
1759 } else {
1760 if ($clone_project->error != '') {
1761 $this->error .= $clone_project->error;
1762 $error++;
1763 }
1764 }
1765 }
1766 } elseif ($tab < 0) {
1767 $this->error .= $origin_project->error;
1768 $error++;
1769 }
1770 }
1771 }
1772
1773 //Duplicate file
1774 if ($clone_project_file) {
1775 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1776
1777 $clone_project_dir = $conf->project->dir_output."/".dol_sanitizeFileName($defaultref);
1778 $ori_project_dir = $conf->project->dir_output."/".dol_sanitizeFileName($orign_project_ref);
1779
1780 if (dol_mkdir($clone_project_dir) >= 0) {
1781 $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1782 foreach ($filearray as $key => $file) {
1783 $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1);
1784 if (is_numeric($rescopy) && $rescopy < 0) {
1785 $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']);
1786 $error++;
1787 }
1788 }
1789 } else {
1790 $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1791 $error++;
1792 }
1793 }
1794
1795 //Duplicate task
1796 if ($clone_task) {
1797 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
1798
1799 $taskstatic = new Task($this->db);
1800
1801 // Security check
1802 $socid = 0;
1803 if ($user->socid > 0) {
1804 $socid = $user->socid;
1805 }
1806
1807 $tasksarray = $taskstatic->getTasksArray(0, 0, $fromid, $socid, 0);
1808
1809 $tab_conv_child_parent = array();
1810
1811 // Loop on each task, to clone it
1812 foreach ($tasksarray as $tasktoclone) {
1813 $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_task_parent, $move_date, true, false, $clone_task_file, true, false);
1814 if ($result_clone <= 0) {
1815 $this->error .= $taskstatic->error;
1816 $error++;
1817 } else {
1818 $new_task_id = $result_clone;
1819 $taskstatic->fetch($tasktoclone->id);
1820
1821 //manage new parent clone task id
1822 // if the current task has child we store the original task id and the equivalent clone task id
1823 if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) {
1824 $tab_conv_child_parent[$tasktoclone->id] = $new_task_id;
1825 }
1826 }
1827 }
1828
1829 //Parse all clone node to be sure to update new parent
1830 $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0);
1831 foreach ($tasksarray as $task_cloned) {
1832 $taskstatic->fetch($task_cloned->id);
1833 if ($taskstatic->fk_task_parent != 0) {
1834 $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent];
1835 }
1836 $res = $taskstatic->update($user, $notrigger);
1837 if ($result_clone <= 0) {
1838 $this->error .= $taskstatic->error;
1839 $error++;
1840 }
1841 }
1842 }
1843 }
1844
1845 unset($clone_project->context['createfromclone']);
1846
1847 if (!$error) {
1848 $this->db->commit();
1849 return $clone_project_id;
1850 } else {
1851 $this->db->rollback();
1852 dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
1853 return -1;
1854 }
1855 }
1856
1857
1864 public function shiftTaskDate($old_project_dt_start)
1865 {
1866 global $user, $langs, $conf;
1867
1868 $error = 0;
1869 $result = 0;
1870
1871 $taskstatic = new Task($this->db);
1872
1873 // Security check
1874 $socid = 0;
1875 if ($user->socid > 0) {
1876 $socid = $user->socid;
1877 }
1878
1879 $tasksarray = $taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1880
1881 foreach ($tasksarray as $tasktoshiftdate) {
1882 $to_update = false;
1883 // Fetch only if update of date will be made
1884 if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) {
1885 //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1886 $to_update = true;
1887 $task = new Task($this->db);
1888 $result = $task->fetch($tasktoshiftdate->id);
1889 if (!$result) {
1890 $error++;
1891 $this->error .= $task->error;
1892 }
1893 }
1894 //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1895
1896 //Calcultate new task start date with difference between old proj start date and origin task start date
1897 if (!empty($tasktoshiftdate->date_start)) {
1898 $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
1899 }
1900
1901 //Calcultate new task end date with difference between origin proj end date and origin task end date
1902 if (!empty($tasktoshiftdate->date_end)) {
1903 $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1904 }
1905
1906 if ($to_update) {
1907 $result = $task->update($user);
1908 if (!$result) {
1909 $error++;
1910 $this->error .= $task->error;
1911 }
1912 }
1913 }
1914 if ($error != 0) {
1915 return -1;
1916 }
1917 return $result;
1918 }
1919
1920
1921 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1929 public function update_element($tableName, $elementSelectId)
1930 {
1931 // phpcs:enable
1932 $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName;
1933
1934 if ($tableName == "actioncomm") {
1935 $sql .= " SET fk_project=".$this->id;
1936 $sql .= " WHERE id=".((int) $elementSelectId);
1937 } elseif ($tableName == "entrepot") {
1938 $sql .= " SET fk_project=".$this->id;
1939 $sql .= " WHERE rowid=".((int) $elementSelectId);
1940 } else {
1941 $sql .= " SET fk_projet=".$this->id;
1942 $sql .= " WHERE rowid=".((int) $elementSelectId);
1943 }
1944
1945 dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
1946 $resql = $this->db->query($sql);
1947 if (!$resql) {
1948 $this->error = $this->db->lasterror();
1949 return -1;
1950 } else {
1951 return 1;
1952 }
1953 }
1954
1955 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1965 public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet')
1966 {
1967 // phpcs:enable
1968 $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName;
1969
1970 if ($tableName == "actioncomm") {
1971 $sql .= " SET fk_project=NULL";
1972 $sql .= " WHERE id=".((int) $elementSelectId);
1973 } else {
1974 $sql .= " SET ".$projectfield."=NULL";
1975 $sql .= " WHERE rowid=".((int) $elementSelectId);
1976 }
1977
1978 dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
1979 $resql = $this->db->query($sql);
1980 if (!$resql) {
1981 $this->error = $this->db->lasterror();
1982 return -1;
1983 } else {
1984 return 1;
1985 }
1986 }
1987
1998 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1999 {
2000 global $conf, $langs;
2001
2002 $langs->load("projects");
2003
2004 if (!dol_strlen($modele)) {
2005 $modele = 'baleine';
2006
2007 if ($this->model_pdf) {
2008 $modele = $this->model_pdf;
2009 } elseif (!empty($conf->global->PROJECT_ADDON_PDF)) {
2010 $modele = $conf->global->PROJECT_ADDON_PDF;
2011 }
2012 }
2013
2014 $modelpath = "core/modules/project/doc/";
2015
2016 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2017 }
2018
2019
2029 public function loadTimeSpent($datestart, $taskid = 0, $userid = 0)
2030 {
2031 $error = 0;
2032
2033 $this->weekWorkLoad = array();
2034 $this->weekWorkLoadPerTask = array();
2035
2036 if (empty($datestart)) {
2037 dol_print_error('', 'Error datestart parameter is empty');
2038 }
2039
2040 $sql = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element";
2041 $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
2042 $sql .= " WHERE ptt.fk_element = pt.rowid";
2043 $sql .= " AND ptt.elementtype = 'task'";
2044 $sql .= " AND pt.fk_projet = ".((int) $this->id);
2045 $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' ";
2046 $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
2047 if ($taskid) {
2048 $sql .= " AND ptt.fk_element=".((int) $taskid);
2049 }
2050 if (is_numeric($userid)) {
2051 $sql .= " AND ptt.fk_user=".((int) $userid);
2052 }
2053
2054 //print $sql;
2055 $resql = $this->db->query($sql);
2056 if ($resql) {
2057 $daylareadyfound = array();
2058
2059 $num = $this->db->num_rows($resql);
2060 $i = 0;
2061 // Loop on each record found, so each couple (project id, task id)
2062 while ($i < $num) {
2063 $obj = $this->db->fetch_object($resql);
2064 $day = $this->db->jdate($obj->element_date); // task_date is date without hours
2065 if (empty($daylareadyfound[$day])) {
2066 $this->weekWorkLoad[$day] = $obj->element_duration;
2067 $this->weekWorkLoadPerTask[$day][$obj->fk_element] = $obj->element_duration;
2068 } else {
2069 $this->weekWorkLoad[$day] += $obj->element_duration;
2070 $this->weekWorkLoadPerTask[$day][$obj->fk_element] += $obj->element_duration;
2071 }
2072 $daylareadyfound[$day] = 1;
2073 $i++;
2074 }
2075 $this->db->free($resql);
2076 return 1;
2077 } else {
2078 $this->error = "Error ".$this->db->lasterror();
2079 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
2080 return -1;
2081 }
2082 }
2083
2093 public function loadTimeSpentMonth($datestart, $taskid = 0, $userid = 0)
2094 {
2095 $error = 0;
2096
2097 $this->monthWorkLoad = array();
2098 $this->monthWorkLoadPerTask = array();
2099
2100 if (empty($datestart)) {
2101 dol_print_error('', 'Error datestart parameter is empty');
2102 }
2103
2104 $sql = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element";
2105 $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
2106 $sql .= " WHERE ptt.fk_element = pt.rowid";
2107 $sql .= " AND ptt.elementtype = 'task'";
2108 $sql .= " AND pt.fk_projet = ".((int) $this->id);
2109 $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' ";
2110 $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'm') - 1)."')";
2111 if ($taskid) {
2112 $sql .= " AND ptt.fk_element=".((int) $taskid);
2113 }
2114 if (is_numeric($userid)) {
2115 $sql .= " AND ptt.fk_user=".((int) $userid);
2116 }
2117
2118 //print $sql;
2119 $resql = $this->db->query($sql);
2120 if ($resql) {
2121 $weekalreadyfound = array();
2122
2123 $num = $this->db->num_rows($resql);
2124 $i = 0;
2125 // Loop on each record found, so each couple (project id, task id)
2126 while ($i < $num) {
2127 $obj = $this->db->fetch_object($resql);
2128 if (!empty($obj->element_date)) {
2129 $date = explode('-', $obj->element_date);
2130 $week_number = getWeekNumber($date[2], $date[1], $date[0]);
2131 }
2132 if (empty($weekalreadyfound[$week_number])) {
2133 $this->monthWorkLoad[$week_number] = $obj->element_duration;
2134 $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] = $obj->element_duration;
2135 } else {
2136 $this->monthWorkLoad[$week_number] += $obj->element_duration;
2137 $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] += $obj->element_duration;
2138 }
2139 $weekalreadyfound[$week_number] = 1;
2140 $i++;
2141 }
2142 $this->db->free($resql);
2143 return 1;
2144 } else {
2145 $this->error = "Error ".$this->db->lasterror();
2146 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
2147 return -1;
2148 }
2149 }
2150
2151 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2158 public function load_board($user)
2159 {
2160 // phpcs:enable
2161 global $conf, $langs;
2162
2163 // For external user, no check is done on company because readability is managed by public status of project and assignement.
2164 //$socid=$user->socid;
2165
2166 $response = new WorkboardResponse();
2167 $response->warning_delay = $conf->project->warning_delay / 60 / 60 / 24;
2168 $response->label = $langs->trans("OpenedProjects");
2169 $response->labelShort = $langs->trans("Opened");
2170 $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
2171 $response->img = img_object('', "projectpub");
2172 $response->nbtodo = 0;
2173 $response->nbtodolate = 0;
2174
2175 $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
2176 $sql .= " FROM (".MAIN_DB_PREFIX."projet as p";
2177 $sql .= ")";
2178 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2179 // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
2180 //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2181 $sql .= " WHERE p.fk_statut = 1";
2182 $sql .= " AND p.entity IN (".getEntity('project').')';
2183
2184
2185 $projectsListId = null;
2186 if (!$user->hasRight("projet", "all", "lire")) {
2187 $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
2188 $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
2189 if (empty($projectsListId)) {
2190 return $response;
2191 }
2192
2193 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2194 }
2195
2196 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2197 //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).")";
2198 // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
2199 //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))";
2200
2201 //print $sql;
2202 $resql = $this->db->query($sql);
2203 if ($resql) {
2204 $project_static = new Project($this->db);
2205
2206
2207 // This assignment in condition is not a bug. It allows walking the results.
2208 while ($obj = $this->db->fetch_object($resql)) {
2209 $response->nbtodo++;
2210
2211 $project_static->statut = $obj->status;
2212 $project_static->opp_status = $obj->fk_opp_status;
2213 $project_static->date_end = $this->db->jdate($obj->datee);
2214
2215 if ($project_static->hasDelay()) {
2216 $response->nbtodolate++;
2217 }
2218 }
2219
2220 return $response;
2221 }
2222
2223 $this->error = $this->db->error();
2224 return -1;
2225 }
2226
2235 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2236 {
2237 $tables = array(
2238 'projet'
2239 );
2240
2241 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2242 }
2243
2244
2245 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2251 public function load_state_board()
2252 {
2253 // phpcs:enable
2254 global $user;
2255
2256 $this->nb = array();
2257
2258 $sql = "SELECT count(p.rowid) as nb";
2259 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2260 $sql .= " WHERE";
2261 $sql .= " p.entity IN (".getEntity('project').")";
2262 if (empty($user->rights->projet->all->lire)) {
2263 $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
2264 $sql .= "AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2265 }
2266
2267 $resql = $this->db->query($sql);
2268 if ($resql) {
2269 while ($obj = $this->db->fetch_object($resql)) {
2270 $this->nb["projects"] = $obj->nb;
2271 }
2272 $this->db->free($resql);
2273 return 1;
2274 } else {
2275 dol_print_error($this->db);
2276 $this->error = $this->db->error();
2277 return -1;
2278 }
2279 }
2280
2281
2287 public function hasDelay()
2288 {
2289 global $conf;
2290
2291 if (!($this->statut == self::STATUS_VALIDATED)) {
2292 return false;
2293 }
2294 if (!$this->date_end) {
2295 return false;
2296 }
2297
2298 $now = dol_now();
2299
2300 return ($this->date_end) < ($now - $conf->project->warning_delay);
2301 }
2302
2303
2310 public function info($id)
2311 {
2312 $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
2313 $sql .= ' date_close as datecloture,';
2314 $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture';
2315 $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c';
2316 $sql .= ' WHERE c.rowid = '.((int) $id);
2317 $result = $this->db->query($sql);
2318 if ($result) {
2319 if ($this->db->num_rows($result)) {
2320 $obj = $this->db->fetch_object($result);
2321 $this->id = $obj->rowid;
2322 if ($obj->fk_user_author) {
2323 $cuser = new User($this->db);
2324 $cuser->fetch($obj->fk_user_author);
2325 $this->user_creation = $cuser;
2326 }
2327
2328 if (!empty($obj->fk_user_cloture)) {
2329 $cluser = new User($this->db);
2330 $cluser->fetch($obj->fk_user_cloture);
2331 $this->user_cloture = $cluser;
2332 }
2333
2334 $this->date_creation = $this->db->jdate($obj->datec);
2335 $this->date_modification = $this->db->jdate($obj->datem);
2336 $this->date_cloture = $this->db->jdate($obj->datecloture);
2337 }
2338
2339 $this->db->free($result);
2340 } else {
2341 dol_print_error($this->db);
2342 }
2343 }
2344
2355 public function setCategories($categories)
2356 {
2357 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2358 return parent::setCategoriesCommon($categories, Categorie::TYPE_PROJECT);
2359 }
2360
2361
2369 public function getLinesArray($user, $loadRoleMode = 1)
2370 {
2371 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
2372 $taskstatic = new Task($this->db);
2373
2374 $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0, '', '-1', '', 0, 0, array(), 0, array(), 0, $loadRoleMode);
2375 return 1;
2376 }
2377
2395 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 = '')
2396 {
2397 global $conf, $langs;
2398 // TODO EMAIL
2399
2400 return 1;
2401 }
2409 public function getKanbanView($option = '', $arraydata = null)
2410 {
2411 global $langs;
2412
2413 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2414
2415 $return = '<div class="box-flex-item box-flex-grow-zero">';
2416 $return .= '<div class="info-box info-box-sm">';
2417 $return .= '<span class="info-box-icon bg-infobox-action">';
2418 $return .= img_picto('', $this->public ? 'projectpub' : $this->picto);
2419 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2420 $return .= '</span>';
2421 $return .= '<div class="info-box-content">';
2422 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref);
2423 if ($this->hasDelay()) {
2424 $return .= img_warning($langs->trans('Late'));
2425 }
2426 $return .= '</span>';
2427 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2428 // Date
2429 /*
2430 if (property_exists($this, 'date_start') && $this->date_start) {
2431 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_start, 'day').'</>';
2432 }
2433 if (property_exists($this, 'date_end') && $this->date_end) {
2434 if ($this->date_start) {
2435 $return .= ' - ';
2436 } else {
2437 $return .= '<br>';
2438 }
2439 $return .= '<span class="info-box-label">'.dol_print_date($this->date_end, 'day').'</span>';
2440 }*/
2441 if (!empty($arraydata['assignedusers'])) {
2442 $return .= '<br>';
2443 if ($this->public) {
2444 $return .= img_picto($langs->trans('Visibility').': '.$langs->trans('SharedProject'), 'world', 'class="paddingrightonly valignmiddle"');
2445 //print $langs->trans('SharedProject');
2446 } else {
2447 $return .= img_picto($langs->trans('Visibility').': '.$langs->trans('PrivateProject'), 'private', 'class="paddingrightonly valignmiddle"');
2448 //print $langs->trans('PrivateProject');
2449 }
2450
2451 $return .= ' <span class="small valignmiddle">'.$arraydata['assignedusers'].'</span>';
2452 }
2453 /*if (property_exists($this, 'user_author_id')) {
2454 $return .= '<br><span class="info-box-label opacitymedium">'.$langs->trans("Author").'</span>';
2455 $return .= '<span> : '.$user->getNomUrl(1).'</span>';
2456 }*/
2457 if ($this->usage_opportunity && $this->opp_status_code) {
2458 //$return .= '<br><span class="info-bo-label opacitymedium">'.$langs->trans("OpportunityStatusShort").'</span>';
2459 $return .= '<br><span class="info-box-label small">'.$langs->trans("OppStatus".$this->opp_status_code).'</span>';
2460 $return .= ' <span class="opacitymedium small">('.round($this->opp_percent).'%)</span>';
2461 $return .= '<br><span class="amount small">'.price($this->opp_amount).'</span>';
2462 }
2463 if (method_exists($this, 'getLibStatut')) {
2464 $return .= '<br><div class="info-box-status small">'.$this->getLibStatut(3).'</div>';
2465 }
2466 $return .= '</div>';
2467 $return .= '</div>';
2468 $return .= '</div>';
2469 return $return;
2470 }
2471}
$object ref
Definition info.php:78
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)
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.
print $langs trans("Ref").' m m m statut
Definition index.php:152
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:123
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_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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)