dolibarr 20.0.2
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 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
12 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <https://www.gnu.org/licenses/>.
26 */
27
33require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
34
38class Project extends CommonObject
39{
43 public $element = 'project';
44
48 public $table_element = 'projet';
49
53 public $table_element_line = 'projet_task';
54
58 public $table_element_date;
59
63 public $fk_element = 'fk_projet';
64
68 public $picto = 'project';
69
73 protected $table_ref_field = 'ref';
74
78 public $fk_project;
79
83 public $description;
84
88 public $title;
89
95 public $dateo;
96
100 public $date_start;
101
107 public $datee;
108
112 public $date_end;
113
117 public $date_start_event;
118
122 public $date_end_event;
123
127 public $location;
128
132 public $date_close;
133
134 public $socid; // To store id of thirdparty
135 public $thirdparty_name; // To store name of thirdparty (defined only in some cases)
136
138
142 public $fk_user_close;
143
144 public $public;
145
149 public $budget_amount;
150
154 public $usage_opportunity;
155
159 public $usage_task;
160
164 public $usage_bill_time; // Is the time spent on project must be invoiced or not
165
169 public $usage_organize_event;
170
174 public $accept_conference_suggestions;
175
179 public $accept_booth_suggestions;
180
184 public $price_registration;
185
189 public $price_booth;
190
194 public $max_attendees;
195
196 public $statut; // 0=draft, 1=opened, 2=closed
197
198 public $opp_status; // opportunity status, into table llx_c_lead_status
199 public $opp_status_code;
200 public $fk_opp_status; // opportunity status, into table llx_c_lead_status
201 public $opp_amount; // opportunity amount
202 public $opp_percent; // opportunity probability
203 public $opp_weighted_amount; // opportunity weighted amount
204
205 public $email_msgid;
206
207 public $oldcopy;
208
209 public $weekWorkLoad; // Used to store workload details of a projet
210 public $weekWorkLoadPerTask; // Used to store workload details of tasks of a projet
211
215 public $monthWorkLoad;
216
220 public $monthWorkLoadPerTask;
221
227 public $datec;
228
232 public $date_c;
233
239 public $datem;
240
244 public $date_m;
245
249 public $ip;
250
254 public $lines;
255
296 // BEGIN MODULEBUILDER PROPERTIES
300 public $fields = array(
301 'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
302 'fk_project' => array('type' => 'integer', 'label' => 'Parent', 'enabled' => 1, 'visible' => 1, 'notnull' => 0, 'position' => 12),
303 'ref' => array('type' => 'varchar(50)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 15, 'searchall' => 1),
304 'title' => array('type' => 'varchar(255)', 'label' => 'ProjectLabel', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 17, 'showoncombobox' => 2, 'searchall' => 1),
305 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 19),
306 'fk_soc' => array('type' => 'integer', 'label' => 'Thirdparty', 'enabled' => 1, 'visible' => 0, 'position' => 20),
307 'dateo' => array('type' => 'date', 'label' => 'DateStart', 'enabled' => 1, 'visible' => -1, 'position' => 30),
308 'datee' => array('type' => 'date', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => 1, 'position' => 35),
309 'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => 3, 'position' => 55, 'searchall' => 1),
310 'public' => array('type' => 'integer', 'label' => 'Visibility', 'enabled' => 1, 'visible' => -1, 'position' => 65),
311 'fk_opp_status' => array('type' => 'integer:CLeadStatus:core/class/cleadstatus.class.php', 'label' => 'OpportunityStatusShort', 'enabled' => 'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'visible' => 1, 'position' => 75),
312 'opp_percent' => array('type' => 'double(5,2)', 'label' => 'OpportunityProbabilityShort', 'enabled' => 'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'visible' => 1, 'position' => 80),
313 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 85, 'searchall' => 1),
314 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 90, 'searchall' => 1),
315 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPdf', 'enabled' => 1, 'visible' => 0, 'position' => 95),
316 'date_close' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => 0, 'position' => 105),
317 'fk_user_close' => array('type' => 'integer', 'label' => 'UserClosing', 'enabled' => 1, 'visible' => 0, 'position' => 110),
318 'opp_amount' => array('type' => 'double(24,8)', 'label' => 'OpportunityAmountShort', 'enabled' => 1, 'visible' => 'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'position' => 115),
319 'budget_amount' => array('type' => 'double(24,8)', 'label' => 'Budget', 'enabled' => 1, 'visible' => -1, 'position' => 119),
320 'usage_opportunity' => array('type' => 'integer', 'label' => 'UsageOpportunity', 'enabled' => 1, 'visible' => -1, 'position' => 130),
321 'usage_task' => array('type' => 'integer', 'label' => 'UsageTasks', 'enabled' => 1, 'visible' => -1, 'position' => 135),
322 'usage_bill_time' => array('type' => 'integer', 'label' => 'UsageBillTimeShort', 'enabled' => 1, 'visible' => -1, 'position' => 140),
323 'usage_organize_event' => array('type' => 'integer', 'label' => 'UsageOrganizeEvent', 'enabled' => 1, 'visible' => -1, 'position' => 145),
324 // Properties for event organization
325 'date_start_event' => array('type' => 'date', 'label' => 'DateStartEvent', 'enabled' => "isModEnabled('eventorganization')", 'visible' => 1, 'position' => 200),
326 'date_end_event' => array('type' => 'date', 'label' => 'DateEndEvent', 'enabled' => "isModEnabled('eventorganization')", 'visible' => 1, 'position' => 201),
327 'location' => array('type' => 'text', 'label' => 'Location', 'enabled' => 1, 'visible' => 3, 'position' => 55, 'searchall' => 202),
328 'accept_conference_suggestions' => array('type' => 'integer', 'label' => 'AllowUnknownPeopleSuggestConf', 'enabled' => 1, 'visible' => -1, 'position' => 210),
329 'accept_booth_suggestions' => array('type' => 'integer', 'label' => 'AllowUnknownPeopleSuggestBooth', 'enabled' => 1, 'visible' => -1, 'position' => 211),
330 'price_registration' => array('type' => 'double(24,8)', 'label' => 'PriceOfRegistration', 'enabled' => 1, 'visible' => -1, 'position' => 212),
331 'price_booth' => array('type' => 'double(24,8)', 'label' => 'PriceOfBooth', 'enabled' => 1, 'visible' => -1, 'position' => 215),
332 'max_attendees' => array('type' => 'integer', 'label' => 'MaxNbOfAttendees', 'enabled' => 1, 'visible' => -1, 'position' => 215),
333 // Generic
334 'datec' => array('type' => 'datetime', 'label' => 'DateCreationShort', 'enabled' => 1, 'visible' => -2, 'position' => 400),
335 'tms' => array('type' => 'timestamp', 'label' => 'DateModificationShort', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 405),
336 'fk_user_creat' => array('type' => 'integer', 'label' => 'UserCreation', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 410),
337 'fk_user_modif' => array('type' => 'integer', 'label' => 'UserModification', 'enabled' => 1, 'visible' => 0, 'position' => 415),
338 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 420),
339 'email_msgid' => array('type' => 'varchar(255)', 'label' => 'EmailMsgID', 'enabled' => 1, 'visible' => -1, 'position' => 450, 'help' => 'EmailMsgIDWhenSourceisEmail', 'csslist' => 'tdoverflowmax125'),
340 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'alias' => 'status', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 500, 'arrayofkeyval' => array(0 => 'Draft', 1 => 'Validated', 2 => 'Closed')),
341 );
342 // END MODULEBUILDER PROPERTIES
343
347 const STATUS_DRAFT = 0;
348
353
357 const STATUS_CLOSED = 2;
358
359
365 public function __construct($db)
366 {
367 global $conf;
368
369 $this->db = $db;
370
371 $this->ismultientitymanaged = 1;
372 $this->isextrafieldmanaged = 1;
373
374 $this->labelStatusShort = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
375 $this->labelStatus = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
376
377 global $conf;
378
379 if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID')) {
380 $this->fields['rowid']['visible'] = 0;
381 }
382
383 if (!getDolGlobalString('PROJECT_USE_OPPORTUNITIES')) {
384 $this->fields['fk_opp_status']['enabled'] = 0;
385 $this->fields['opp_percent']['enabled'] = 0;
386 $this->fields['opp_amount']['enabled'] = 0;
387 $this->fields['usage_opportunity']['enabled'] = 0;
388 }
389
390 if (getDolGlobalString('PROJECT_HIDE_TASKS')) {
391 $this->fields['usage_bill_time']['visible'] = 0;
392 $this->fields['usage_task']['visible'] = 0;
393 }
394
395 if (empty($conf->eventorganization->enabled)) {
396 $this->fields['usage_organize_event']['visible'] = 0;
397 $this->fields['accept_conference_suggestions']['enabled'] = 0;
398 $this->fields['accept_booth_suggestions']['enabled'] = 0;
399 $this->fields['price_registration']['enabled'] = 0;
400 $this->fields['price_booth']['enabled'] = 0;
401 $this->fields['max_attendees']['enabled'] = 0;
402 }
403 }
404
412 public function create($user, $notrigger = 0)
413 {
414 $error = 0;
415 $ret = 0;
416
417 $now = dol_now();
418
419 // Clean parameters
420 $this->note_private = dol_substr($this->note_private, 0, 65535);
421 $this->note_public = dol_substr($this->note_public, 0, 65535);
422
423 // Check parameters
424 if (!trim($this->ref)) {
425 $this->error = 'ErrorFieldsRequired';
426 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
427 return -1;
428 }
429 if (getDolGlobalString('PROJECT_THIRDPARTY_REQUIRED') && !($this->socid > 0)) {
430 $this->error = 'ErrorFieldsRequired';
431 dol_syslog(get_class($this)."::create error -1 thirdparty not defined and option PROJECT_THIRDPARTY_REQUIRED is set", LOG_ERR);
432 return -1;
433 }
434
435 // Create project
436 $this->db->begin();
437
438 $sql = "INSERT INTO ".MAIN_DB_PREFIX."projet (";
439 $sql .= "ref";
440 $sql .= ", fk_project";
441 $sql .= ", title";
442 $sql .= ", description";
443 $sql .= ", fk_soc";
444 $sql .= ", fk_user_creat";
445 $sql .= ", fk_statut";
446 $sql .= ", fk_opp_status";
447 $sql .= ", opp_percent";
448 $sql .= ", public";
449 $sql .= ", datec";
450 $sql .= ", dateo";
451 $sql .= ", datee";
452 $sql .= ", opp_amount";
453 $sql .= ", budget_amount";
454 $sql .= ", usage_opportunity";
455 $sql .= ", usage_task";
456 $sql .= ", usage_bill_time";
457 $sql .= ", usage_organize_event";
458 $sql .= ", accept_conference_suggestions";
459 $sql .= ", accept_booth_suggestions";
460 $sql .= ", price_registration";
461 $sql .= ", price_booth";
462 $sql .= ", max_attendees";
463 $sql .= ", date_start_event";
464 $sql .= ", date_end_event";
465 $sql .= ", location";
466 $sql .= ", email_msgid";
467 $sql .= ", note_private";
468 $sql .= ", note_public";
469 $sql .= ", entity";
470 $sql .= ", ip";
471 $sql .= ") VALUES (";
472 $sql .= "'".$this->db->escape($this->ref)."'";
473 $sql .= ", ".($this->fk_project ? ((int) $this->fk_project) : "null");
474 $sql .= ", '".$this->db->escape($this->title)."'";
475 $sql .= ", '".$this->db->escape($this->description)."'";
476 $sql .= ", ".($this->socid > 0 ? $this->socid : "null");
477 $sql .= ", ".((int) $user->id);
478 $sql .= ", ".(is_numeric($this->statut) ? ((int) $this->statut) : '0');
479 $sql .= ", ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? ((int) $this->opp_status) : 'NULL');
480 $sql .= ", ".(is_numeric($this->opp_percent) ? ((int) $this->opp_percent) : 'NULL');
481 $sql .= ", ".($this->public ? 1 : 0);
482 $sql .= ", '".$this->db->idate($now)."'";
483 $sql .= ", ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
484 $sql .= ", ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
485 $sql .= ", ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : 'null');
486 $sql .= ", ".(strcmp((string) $this->budget_amount, '') ? price2num($this->budget_amount) : 'null');
487 $sql .= ", ".($this->usage_opportunity ? 1 : 0);
488 $sql .= ", ".($this->usage_task ? 1 : 0);
489 $sql .= ", ".($this->usage_bill_time ? 1 : 0);
490 $sql .= ", ".($this->usage_organize_event ? 1 : 0);
491 $sql .= ", ".($this->accept_conference_suggestions ? 1 : 0);
492 $sql .= ", ".($this->accept_booth_suggestions ? 1 : 0);
493 $sql .= ", ".(strcmp((string) $this->price_registration, '') ? price2num($this->price_registration) : 'null');
494 $sql .= ", ".(strcmp((string) $this->price_booth, '') ? price2num($this->price_booth) : 'null');
495 $sql .= ", ".(strcmp((string) $this->max_attendees, '') ? ((int) $this->max_attendees) : 'null');
496 $sql .= ", ".($this->date_start_event != '' ? "'".$this->db->idate($this->date_start_event)."'" : 'null');
497 $sql .= ", ".($this->date_end_event != '' ? "'".$this->db->idate($this->date_end_event)."'" : 'null');
498 $sql .= ", ".($this->location ? "'".$this->db->escape($this->location)."'" : 'null');
499 $sql .= ", ".($this->email_msgid ? "'".$this->db->escape($this->email_msgid)."'" : 'null');
500 $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null');
501 $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null');
502 $sql .= ", ".setEntity($this);
503 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
504 $sql .= ")";
505
506 dol_syslog(get_class($this)."::create", LOG_DEBUG);
507 $resql = $this->db->query($sql);
508 if ($resql) {
509 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."projet");
510 $ret = $this->id;
511
512 if (!$notrigger) {
513 // Call trigger
514 $result = $this->call_trigger('PROJECT_CREATE', $user);
515 if ($result < 0) {
516 $error++;
517 }
518 // End call triggers
519 }
520 } else {
521 $this->error = $this->db->lasterror();
522 $error++;
523 }
524
525 // Update extrafield
526 if (!$error) {
527 $result = $this->insertExtraFields();
528 if ($result < 0) {
529 $error++;
530 }
531 }
532
533 if (!$error && (getDolGlobalString('MAIN_DISABLEDRAFTSTATUS') || getDolGlobalString('MAIN_DISABLEDRAFTSTATUS_PROJECT'))) {
534 $res = $this->setValid($user);
535 if ($res < 0) {
536 $error++;
537 }
538 }
539
540 if (!$error) {
541 $this->db->commit();
542 return $ret;
543 } else {
544 $this->db->rollback();
545 return -1;
546 }
547 }
548
556 public function update($user, $notrigger = 0)
557 {
558 global $langs, $conf;
559
560 $error = 0;
561
562 // Clean parameters
563 $this->title = trim($this->title);
564 $this->description = trim($this->description);
565 if ($this->opp_amount < 0) {
566 $this->opp_amount = '';
567 }
568 if ($this->opp_percent < 0) {
569 $this->opp_percent = '';
570 }
571 if ($this->date_end && $this->date_end < $this->date_start) {
572 $this->error = $langs->trans("ErrorDateEndLowerThanDateStart");
573 $this->errors[] = $this->error;
574 $this->db->rollback();
575 dol_syslog(get_class($this)."::update error -3 ".$this->error, LOG_ERR);
576 return -3;
577 }
578
579 $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
580
581 if (dol_strlen(trim($this->ref)) > 0) {
582 $this->db->begin();
583
584 $sql = "UPDATE ".MAIN_DB_PREFIX."projet SET";
585 $sql .= " ref='".$this->db->escape($this->ref)."'";
586 $sql .= ", fk_project=".($this->fk_project ? ((int) $this->fk_project) : "null");
587 $sql .= ", title = '".$this->db->escape($this->title)."'";
588 $sql .= ", description = '".$this->db->escape($this->description)."'";
589 $sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : "null");
590 $sql .= ", fk_statut = ".((int) $this->statut);
591 $sql .= ", fk_opp_status = ".((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null');
592 $sql .= ", opp_percent = ".((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null');
593 $sql .= ", public = ".($this->public ? 1 : 0);
594 $sql .= ", datec = ".($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null');
595 $sql .= ", dateo = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
596 $sql .= ", datee = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
597 $sql .= ", date_close = ".($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null');
598 $sql .= ", note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
599 $sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
600 $sql .= ", fk_user_close = ".($this->fk_user_close > 0 ? $this->fk_user_close : "null");
601 $sql .= ", opp_amount = ".(strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null");
602 $sql .= ", budget_amount = ".(strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null");
603 $sql .= ", fk_user_modif = ".$user->id;
604 $sql .= ", usage_opportunity = ".($this->usage_opportunity ? 1 : 0);
605 $sql .= ", usage_task = ".($this->usage_task ? 1 : 0);
606 $sql .= ", usage_bill_time = ".($this->usage_bill_time ? 1 : 0);
607 $sql .= ", usage_organize_event = ".($this->usage_organize_event ? 1 : 0);
608 $sql .= ", accept_conference_suggestions = ".($this->accept_conference_suggestions ? 1 : 0);
609 $sql .= ", accept_booth_suggestions = ".($this->accept_booth_suggestions ? 1 : 0);
610 $sql .= ", price_registration = ".(isset($this->price_registration) && strcmp($this->price_registration, '') ? price2num($this->price_registration) : "null");
611 $sql .= ", price_booth = ".(isset($this->price_booth) && strcmp((string) $this->price_booth, '') ? price2num($this->price_booth) : "null");
612 $sql .= ", max_attendees = ".(strcmp((string) $this->max_attendees, '') ? (int) $this->max_attendees : "null");
613 $sql .= ", date_start_event = ".($this->date_start_event != '' ? "'".$this->db->idate($this->date_start_event)."'" : 'null');
614 $sql .= ", date_end_event = ".($this->date_end_event != '' ? "'".$this->db->idate($this->date_end_event)."'" : 'null');
615 $sql .= ", location = '".$this->db->escape($this->location)."'";
616 $sql .= ", entity = ".((int) $this->entity);
617 $sql .= " WHERE rowid = ".((int) $this->id);
618
619 dol_syslog(get_class($this)."::update", LOG_DEBUG);
620 $resql = $this->db->query($sql);
621 if ($resql) {
622 // Update extrafield
623 if (!$error) {
624 $result = $this->insertExtraFields();
625 if ($result < 0) {
626 $error++;
627 }
628 }
629
630 if (!$error && !$notrigger) {
631 // Call trigger
632 $result = $this->call_trigger('PROJECT_MODIFY', $user);
633 if ($result < 0) {
634 $error++;
635 }
636 // End call triggers
637 }
638
639 if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
640 // We remove directory
641 if ($conf->project->dir_output) {
642 $olddir = $conf->project->dir_output."/".dol_sanitizeFileName($this->oldcopy->ref);
643 $newdir = $conf->project->dir_output."/".dol_sanitizeFileName($this->ref);
644 if (file_exists($olddir)) {
645 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
646 $res = @rename($olddir, $newdir);
647 if (!$res) {
648 $langs->load("errors");
649 $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
650 $error++;
651 }
652 }
653 }
654 }
655 if (!$error) {
656 $this->db->commit();
657 $result = 1;
658 } else {
659 $this->db->rollback();
660 $result = -1;
661 }
662 } else {
663 $this->error = $this->db->lasterror();
664 $this->errors[] = $this->error;
665 $this->db->rollback();
666 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
667 $result = -4;
668 } else {
669 $result = -2;
670 }
671 dol_syslog(get_class($this)."::update error ".$result." ".$this->error, LOG_ERR);
672 }
673 } else {
674 dol_syslog(get_class($this)."::update ref null");
675 $result = -1;
676 }
677
678 return $result;
679 }
680
690 public function fetch($id, $ref = '', $ref_ext = '', $email_msgid = '')
691 {
692 if (empty($id) && empty($ref) && empty($ref_ext) && empty($email_msgid)) {
693 dol_syslog(get_class($this)."::fetch Bad parameters", LOG_WARNING);
694 return -1;
695 }
696
697 $sql = "SELECT rowid, entity, fk_project, ref, title, description, public, datec, opp_amount, budget_amount,";
698 $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,";
699 $sql .= " note_private, note_public, model_pdf, usage_opportunity, usage_task, usage_bill_time, usage_organize_event, email_msgid,";
700 $sql .= " accept_conference_suggestions, accept_booth_suggestions, price_registration, price_booth, max_attendees, date_start_event, date_end_event, location, extraparams";
701 $sql .= " FROM ".MAIN_DB_PREFIX."projet";
702 if (!empty($id)) {
703 $sql .= " WHERE rowid = ".((int) $id);
704 } else {
705 $sql .= " WHERE entity IN (".getEntity('project').")";
706 if (!empty($ref)) {
707 $sql .= " AND ref = '".$this->db->escape($ref)."'";
708 } elseif (!empty($ref_ext)) {
709 $sql .= " AND ref_ext = '".$this->db->escape($ref_ext)."'";
710 } else {
711 $sql .= " AND email_msgid = '".$this->db->escape($email_msgid)."'";
712 }
713 }
714
715 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
716 $resql = $this->db->query($sql);
717 if ($resql) {
718 $num_rows = $this->db->num_rows($resql);
719
720 if ($num_rows) {
721 $obj = $this->db->fetch_object($resql);
722
723 $this->id = $obj->rowid;
724 $this->entity = $obj->entity;
725 $this->ref = $obj->ref;
726 $this->fk_project = $obj->fk_project;
727 $this->title = $obj->title;
728 $this->description = $obj->description;
729 $this->date_c = $this->db->jdate($obj->datec);
730 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
731 $this->date_m = $this->db->jdate($obj->tms);
732 $this->datem = $this->db->jdate($obj->tms); // TODO deprecated
733 $this->date_start = $this->db->jdate($obj->date_start);
734 $this->date_end = $this->db->jdate($obj->date_end);
735 $this->date_close = $this->db->jdate($obj->date_close);
736 $this->note_private = $obj->note_private;
737 $this->note_public = $obj->note_public;
738 $this->socid = $obj->fk_soc;
739 $this->user_author_id = $obj->fk_user_creat;
740 $this->user_modification_id = $obj->fk_user_modif;
741 $this->user_closing_id = $obj->fk_user_close;
742 $this->public = $obj->public;
743 $this->statut = $obj->status; // deprecated
744 $this->status = $obj->status;
745 $this->opp_status = $obj->fk_opp_status;
746 $this->opp_amount = $obj->opp_amount;
747 $this->opp_percent = $obj->opp_percent;
748 $this->budget_amount = $obj->budget_amount;
749 $this->model_pdf = $obj->model_pdf;
750 $this->usage_opportunity = (int) $obj->usage_opportunity;
751 $this->usage_task = (int) $obj->usage_task;
752 $this->usage_bill_time = (int) $obj->usage_bill_time;
753 $this->usage_organize_event = (int) $obj->usage_organize_event;
754 $this->accept_conference_suggestions = (int) $obj->accept_conference_suggestions;
755 $this->accept_booth_suggestions = (int) $obj->accept_booth_suggestions;
756 $this->price_registration = $obj->price_registration;
757 $this->price_booth = $obj->price_booth;
758 $this->max_attendees = $obj->max_attendees;
759 $this->date_start_event = $this->db->jdate($obj->date_start_event);
760 $this->date_end_event = $this->db->jdate($obj->date_end_event);
761 $this->location = $obj->location;
762 $this->email_msgid = $obj->email_msgid;
763 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
764
765 $this->db->free($resql);
766
767 // Retrieve all extrafield
768 // fetch optionals attributes and labels
769 $this->fetch_optionals();
770
771 return 1;
772 }
773
774 $this->db->free($resql);
775
776 return 0;
777 } else {
778 $this->error = $this->db->lasterror();
779 $this->errors[] = $this->db->lasterror();
780 return -1;
781 }
782 }
783
792 public function fetchAndSetSubstitution($id, $key, $fetched = false)
793 {
794 $substitution = '';
795
796 if ($fetched === false) {
797 $res = $this->fetch($id);
798 if ($res > 0) {
799 $fetched = true;
800 }
801 }
802
803 if ($fetched === true) {
804 if ($key == '__PROJECT_ID__') {
805 $substitution = ($this->id > 0 ? $this->id : '');
806 } elseif ($key == '__PROJECT_REF__') {
807 $substitution = $this->ref;
808 } elseif ($key == '__PROJECT_NAME__') {
809 $substitution = $this->title;
810 }
811 }
812
813 return $substitution;
814 }
815
816 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
828 public function get_element_list($type, $tablename, $datefieldname = '', $date_start = null, $date_end = null, $projectkey = 'fk_projet')
829 {
830 // phpcs:enable
831
832 global $hookmanager;
833
834 $elements = array();
835
836 if ($this->id <= 0) {
837 return $elements;
838 }
839
840 $ids = $this->id;
841
842 if ($type == 'agenda') {
843 $sql = "SELECT id as rowid FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project IN (".$this->db->sanitize($ids).") AND entity IN (".getEntity('agenda').")";
844 } elseif ($type == 'expensereport') {
845 $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).")";
846 } elseif ($type == 'project_task') {
847 $sql = "SELECT DISTINCT pt.rowid FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet IN (".$this->db->sanitize($ids).")";
848 } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user
849 $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).")";
850 } elseif ($type == 'stocktransfer_stocktransfer') {
851 $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";
852 } elseif ($type == 'loan') {
853 $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).")";
854 } else {
855 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." IN (".$this->db->sanitize($ids).") AND entity IN (".getEntity($type).")";
856 }
857
858 if (isDolTms($date_start) && $type == 'loan') {
859 $sql .= " AND (dateend > '".$this->db->idate($date_start)."' OR dateend IS NULL)";
860 } elseif (isDolTms($date_start) && ($type != 'project_task')) { // For table project_taks, we want the filter on date apply on project_time_spent table
861 if (empty($datefieldname) && !empty($this->table_element_date)) {
862 $datefieldname = $this->table_element_date;
863 }
864 if (empty($datefieldname)) {
865 return 'Error this object has no date field defined';
866 }
867 $sql .= " AND (".$datefieldname." >= '".$this->db->idate($date_start)."' OR ".$datefieldname." IS NULL)";
868 }
869
870 if (isDolTms($date_end) && $type == 'loan') {
871 $sql .= " AND (datestart < '".$this->db->idate($date_end)."' OR datestart IS NULL)";
872 } elseif (isDolTms($date_end) && ($type != 'project_task')) { // For table project_taks, we want the filter on date apply on project_time_spent table
873 if (empty($datefieldname) && !empty($this->table_element_date)) {
874 $datefieldname = $this->table_element_date;
875 }
876 if (empty($datefieldname)) {
877 return 'Error this object has no date field defined';
878 }
879 $sql .= " AND (".$datefieldname." <= '".$this->db->idate($date_end)."' OR ".$datefieldname." IS NULL)";
880 }
881
882 $parameters = array(
883 'sql' => $sql,
884 'type' => $type,
885 'tablename' => $tablename,
886 'datefieldname' => $datefieldname,
887 'dates' => $date_start,
888 'datee' => $date_end,
889 'fk_projet' => $projectkey,
890 'ids' => $ids,
891 );
892 $reshook = $hookmanager->executeHooks('getElementList', $parameters);
893 if ($reshook > 0) {
894 $sql = $hookmanager->resPrint;
895 } else {
896 $sql .= $hookmanager->resPrint;
897 }
898
899 if (!$sql) {
900 return -1;
901 }
902
903 //print $sql;
904 dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG);
905 $result = $this->db->query($sql);
906 if ($result) {
907 $nump = $this->db->num_rows($result);
908 if ($nump) {
909 $i = 0;
910 while ($i < $nump) {
911 $obj = $this->db->fetch_object($result);
912
913 $elements[$i] = $obj->rowid.(empty($obj->fk_user) ? '' : '_'.$obj->fk_user);
914
915 $i++;
916 }
917 $this->db->free($result);
918 }
919
920 /* Return array even if empty*/
921 return $elements;
922 } else {
923 dol_print_error($this->db);
924 }
925 return -1;
926 }
927
935 public function delete($user, $notrigger = 0)
936 {
937 global $langs, $conf;
938 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
939
940 $error = 0;
941
942 $this->db->begin();
943
944 if (!$error) {
945 // Delete linked contacts
946 $res = $this->delete_linked_contact();
947 if ($res < 0) {
948 $this->error = 'ErrorFailToDeleteLinkedContact';
949 //$error++;
950 $this->db->rollback();
951 return 0;
952 }
953 }
954
955 // Set fk_projet into elements to null
956 $listoftables = array(
957 'propal' => 'fk_projet',
958 'commande' => 'fk_projet',
959 'facture' => 'fk_projet',
960 'supplier_proposal' => 'fk_projet',
961 'commande_fournisseur' => 'fk_projet',
962 'facture_fourn' => 'fk_projet',
963 'expensereport_det' => 'fk_projet',
964 'contrat' => 'fk_projet',
965 'fichinter' => 'fk_projet',
966 'don' => array('field' => 'fk_projet', 'module' => 'don'),
967 'actioncomm' => 'fk_project',
968 'mrp_mo' => array('field' => 'fk_project', 'module' => 'mrp'),
969 'entrepot' => 'fk_project',
970 );
971 foreach ($listoftables as $key => $value) {
972 if (is_array($value)) {
973 if (!isModEnabled($value['module'])) {
974 continue;
975 }
976 $fieldname = $value['field'];
977 } else {
978 $fieldname = $value;
979 }
980 $sql = "UPDATE ".MAIN_DB_PREFIX.$key." SET ".$fieldname." = NULL where ".$fieldname." = ".((int) $this->id);
981
982 $resql = $this->db->query($sql);
983 if (!$resql) {
984 $this->errors[] = $this->db->lasterror();
985 $error++;
986 break;
987 }
988 }
989
990 // Remove linked categories.
991 if (!$error) {
992 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_project";
993 $sql .= " WHERE fk_project = ".((int) $this->id);
994
995 $result = $this->db->query($sql);
996 if (!$result) {
997 $error++;
998 $this->errors[] = $this->db->lasterror();
999 }
1000 }
1001
1002 // Fetch tasks
1003 $this->getLinesArray($user, 0);
1004
1005 // Delete tasks
1006 $ret = $this->deleteTasks($user);
1007 if ($ret < 0) {
1008 $error++;
1009 }
1010
1011
1012 // Delete all child tables
1013 if (!$error) {
1014 $elements = array('categorie_project'); // elements to delete. TODO Make goodway to delete
1015 foreach ($elements as $table) {
1016 if (!$error) {
1017 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$table;
1018 $sql .= " WHERE fk_project = ".((int) $this->id);
1019
1020 $result = $this->db->query($sql);
1021 if (!$result) {
1022 $error++;
1023 $this->errors[] = $this->db->lasterror();
1024 }
1025 }
1026 }
1027 }
1028
1029 if (!$error) {
1030 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet_extrafields";
1031 $sql .= " WHERE fk_object = ".((int) $this->id);
1032
1033 $resql = $this->db->query($sql);
1034 if (!$resql) {
1035 $this->errors[] = $this->db->lasterror();
1036 $error++;
1037 }
1038 }
1039
1040 // Delete project
1041 if (!$error) {
1042 $sql = "DELETE FROM ".MAIN_DB_PREFIX."projet";
1043 $sql .= " WHERE rowid=".((int) $this->id);
1044
1045 $resql = $this->db->query($sql);
1046 if (!$resql) {
1047 $this->errors[] = $langs->trans("CantRemoveProject", $langs->transnoentitiesnoconv("ProjectOverview"));
1048 $error++;
1049 }
1050 }
1051
1052
1053
1054 if (empty($error)) {
1055 // We remove directory
1056 $projectref = dol_sanitizeFileName($this->ref);
1057 if ($conf->project->dir_output) {
1058 $dir = $conf->project->dir_output."/".$projectref;
1059 if (file_exists($dir)) {
1060 $res = @dol_delete_dir_recursive($dir);
1061 if (!$res) {
1062 $this->errors[] = 'ErrorFailToDeleteDir';
1063 $error++;
1064 }
1065 }
1066 }
1067
1068 if (!$notrigger) {
1069 // Call trigger
1070 $result = $this->call_trigger('PROJECT_DELETE', $user);
1071
1072 if ($result < 0) {
1073 $error++;
1074 }
1075 // End call triggers
1076 }
1077 }
1078
1079 if (empty($error)) {
1080 $this->db->commit();
1081 return 1;
1082 } else {
1083 foreach ($this->errors as $errmsg) {
1084 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1085 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1086 }
1087 dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
1088 $this->db->rollback();
1089 return -1;
1090 }
1091 }
1092
1101 public function getElementCount($type, $tablename, $projectkey = 'fk_projet')
1102 {
1103 if ($this->id <= 0) {
1104 return 0;
1105 }
1106
1107 if ($type == 'agenda') {
1108 $sql = "SELECT COUNT(id) as nb FROM ".MAIN_DB_PREFIX."actioncomm WHERE fk_project = ".((int) $this->id)." AND entity IN (".getEntity('agenda').")";
1109 } elseif ($type == 'expensereport') {
1110 $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);
1111 } elseif ($type == 'project_task') {
1112 $sql = "SELECT DISTINCT COUNT(pt.rowid) as nb FROM ".MAIN_DB_PREFIX."projet_task as pt WHERE pt.fk_projet = ".((int) $this->id);
1113 } elseif ($type == 'element_time') { // Case we want to duplicate line foreach user
1114 $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);
1115 } elseif ($type == 'stock_mouvement') {
1116 $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";
1117 } elseif ($type == 'loan') {
1118 $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);
1119 } else {
1120 $sql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX.$tablename." WHERE ".$projectkey." = ".((int) $this->id)." AND entity IN (".getEntity($type).")";
1121 }
1122
1123 $result = $this->db->query($sql);
1124
1125 if (!$result) {
1126 return 0;
1127 }
1128
1129 $obj = $this->db->fetch_object($result);
1130
1131 $this->db->free($result);
1132
1133 return $obj->nb;
1134 }
1135
1142 public function deleteTasks($user)
1143 {
1144 $countTasks = count($this->lines);
1145 $deleted = false;
1146 if ($countTasks) {
1147 foreach ($this->lines as $task) {
1148 if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them)
1149 $deleted = true;
1150 $ret = $task->delete($user);
1151 if ($ret <= 0) {
1152 $this->errors[] = $this->db->lasterror();
1153 return -1;
1154 }
1155 }
1156 }
1157 }
1158 $this->getLinesArray($user);
1159 if ($deleted && count($this->lines) < $countTasks) {
1160 if (count($this->lines)) {
1161 $this->deleteTasks($user);
1162 }
1163 }
1164
1165 return 1;
1166 }
1167
1175 public function setValid($user, $notrigger = 0)
1176 {
1177 global $langs;
1178
1179 $error = 0;
1180
1181 // Protection
1182 if ($this->status == self::STATUS_VALIDATED) {
1183 dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
1184 return 0;
1185 }
1186
1187 // Check parameters
1188 if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title)) {
1189 $this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("Label")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1190 return -1;
1191 }
1192
1193 $this->db->begin();
1194
1195 $sql = "UPDATE ".MAIN_DB_PREFIX."projet";
1196 $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1197 $sql .= " WHERE rowid = ".((int) $this->id);
1198 //$sql .= " AND entity = ".((int) $conf->entity); // Disabled, when we use the ID for the where, we must not add any other search condition
1199
1200 dol_syslog(get_class($this)."::setValid", LOG_DEBUG);
1201 $resql = $this->db->query($sql);
1202 if ($resql) {
1203 // Call trigger
1204 if (empty($notrigger)) {
1205 $result = $this->call_trigger('PROJECT_VALIDATE', $user);
1206 if ($result < 0) {
1207 $error++;
1208 }
1209 // End call triggers
1210 }
1211
1212 if (!$error) {
1213 $this->statut = 1;
1214 $this->status = 1;
1215 $this->db->commit();
1216 return 1;
1217 } else {
1218 $this->db->rollback();
1219 $this->error = implode(',', $this->errors);
1220 dol_syslog(get_class($this)."::setValid ".$this->error, LOG_ERR);
1221 return -1;
1222 }
1223 } else {
1224 $this->db->rollback();
1225 $this->error = $this->db->lasterror();
1226 return -1;
1227 }
1228 }
1229
1236 public function setClose($user)
1237 {
1238 $now = dol_now();
1239
1240 $error = 0;
1241
1242 if ($this->statut != self::STATUS_CLOSED) {
1243 $this->db->begin();
1244
1245 $sql = "UPDATE ".MAIN_DB_PREFIX."projet";
1246 $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", fk_user_close = ".((int) $user->id).", date_close = '".$this->db->idate($now)."'";
1247 $sql .= " WHERE rowid = ".((int) $this->id);
1248 $sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
1249
1250 if (getDolGlobalString('PROJECT_USE_OPPORTUNITIES')) {
1251 // TODO What to do if fk_opp_status is not code 'WON' or 'LOST'
1252 }
1253
1254 dol_syslog(get_class($this)."::setClose", LOG_DEBUG);
1255 $resql = $this->db->query($sql);
1256 if ($resql) {
1257 // Call trigger
1258 $result = $this->call_trigger('PROJECT_CLOSE', $user);
1259 if ($result < 0) {
1260 $error++;
1261 }
1262 // End call triggers
1263
1264 if (!$error) {
1265 $this->statut = 2;
1266 $this->db->commit();
1267 return 1;
1268 } else {
1269 $this->db->rollback();
1270 $this->error = implode(',', $this->errors);
1271 dol_syslog(get_class($this)."::setClose ".$this->error, LOG_ERR);
1272 return -1;
1273 }
1274 } else {
1275 $this->db->rollback();
1276 $this->error = $this->db->lasterror();
1277 return -1;
1278 }
1279 }
1280
1281 return 0;
1282 }
1283
1290 public function getLibStatut($mode = 0)
1291 {
1292 return $this->LibStatut(isset($this->statut) ? $this->statut : $this->status, $mode);
1293 }
1294
1295 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1303 public function LibStatut($status, $mode = 0)
1304 {
1305 // phpcs:enable
1306 global $langs;
1307
1308 if (is_null($status)) {
1309 return '';
1310 }
1311
1312 $statustrans = array(
1313 0 => 'status0',
1314 1 => 'status4',
1315 2 => 'status6',
1316 );
1317
1318 $statusClass = 'status0';
1319 if (!empty($statustrans[$status])) {
1320 $statusClass = $statustrans[$status];
1321 }
1322
1323 return dolGetStatus($langs->transnoentitiesnoconv($this->labelStatus[$status]), $langs->transnoentitiesnoconv($this->labelStatusShort[$status]), '', $statusClass, $mode);
1324 }
1325
1333 public function getTooltipContentArray($params)
1334 {
1335 global $conf, $langs;
1336
1337 $langs->load('projects');
1338 $option = $params['option'] ?? '';
1339 $moreinpopup = $params['moreinpopup'] ?? '';
1340
1341 $datas = [];
1342 if ($option != 'nolink') {
1343 $datas['picto'] = img_picto('', $this->picto, 'class="pictofixedwidth"').' <u class="paddingrightonly">'.$langs->trans("Project").'</u>';
1344 }
1345 if (isset($this->status)) {
1346 $datas['picto'] .= ' '.$this->getLibStatut(5);
1347 }
1348 $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
1349 $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
1350 if (isset($this->public)) {
1351 $datas['visibility'] = '<br><b>'.$langs->trans("Visibility").":</b> ";
1352 $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"));
1353 }
1354 if (!empty($this->thirdparty_name)) {
1355 $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
1356 }
1357 if (!empty($this->date_start)) {
1358 $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
1359 }
1360 if (!empty($this->date_end)) {
1361 $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
1362 }
1363 if ($moreinpopup) {
1364 $datas['moreinpopup'] = '<br>'.$moreinpopup;
1365 }
1366
1367 return $datas;
1368 }
1369
1384 public function getNomUrl($withpicto = 0, $option = '', $addlabel = 0, $moreinpopup = '', $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1, $morecss = '', $save_pageforbacktolist = '')
1385 {
1386 global $conf, $langs, $user, $hookmanager;
1387
1388 if (!empty($conf->dol_no_mouse_hover)) {
1389 $notooltip = 1; // Force disable tooltips
1390 }
1391
1392 $result = '';
1393 if (getDolGlobalString('PROJECT_OPEN_ALWAYS_ON_TAB')) {
1394 $option = getDolGlobalString('PROJECT_OPEN_ALWAYS_ON_TAB');
1395 }
1396 $params = [
1397 'id' => $this->id,
1398 'objecttype' => $this->element,
1399 'moreinpopup' => $moreinpopup,
1400 'option' => $option,
1401 ];
1402 $classfortooltip = 'classfortooltip';
1403 $dataparams = '';
1404 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1405 $classfortooltip = 'classforajaxtooltip';
1406 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1407 $label = '';
1408 } else {
1409 $label = implode($this->getTooltipContentArray($params));
1410 }
1411
1412 $url = '';
1413 if ($option != 'nolink') {
1414 if (preg_match('/\.php$/', $option)) {
1415 $url = dol_buildpath($option, 1).'?id='.$this->id;
1416 } elseif ($option == 'task') {
1417 $url = DOL_URL_ROOT.'/projet/tasks.php?id='.$this->id;
1418 } elseif ($option == 'preview') {
1419 $url = DOL_URL_ROOT.'/projet/element.php?id='.$this->id;
1420 } elseif ($option == 'eventorganization') {
1421 $url = DOL_URL_ROOT.'/eventorganization/conferenceorbooth_list.php?projectid='.$this->id;
1422 } else {
1423 $url = DOL_URL_ROOT.'/projet/card.php?id='.$this->id;
1424 }
1425 // Add param to save lastsearch_values or not
1426 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1427 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1428 $add_save_lastsearch_values = 1;
1429 }
1430 if ($add_save_lastsearch_values) {
1431 $url .= '&save_lastsearch_values=1';
1432 }
1433 $add_save_backpagefor = ($save_pageforbacktolist ? 1 : 0);
1434 if ($add_save_backpagefor) {
1435 $url .= "&save_pageforbacktolist=".urlencode($save_pageforbacktolist);
1436 }
1437 }
1438
1439 $linkclose = '';
1440 if (empty($notooltip) && $user->hasRight('projet', 'lire')) {
1441 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1442 $label = $langs->trans("ShowProject");
1443 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1444 }
1445 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1446 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1447 } else {
1448 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1449 }
1450
1451 $picto = 'projectpub';
1452 if (!$this->public) {
1453 $picto = 'project';
1454 }
1455
1456 $linkstart = '<a href="'.$url.'"';
1457 $linkstart .= $linkclose.'>';
1458 $linkend = '</a>';
1459
1460 $result .= $linkstart;
1461 if ($withpicto) {
1462 $result .= img_object(($notooltip ? '' : $label), $picto, 'class="pictofixedwidth em088"', 0, 0, $notooltip ? 0 : 1);
1463 }
1464 if ($withpicto != 2) {
1465 $result .= $this->ref;
1466 }
1467 $result .= $linkend;
1468 if ($withpicto != 2) {
1469 $result .= (($addlabel && $this->title) ? '<span class="opacitymedium">'.$sep.dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)).'</span>' : '');
1470 }
1471
1472 global $action;
1473 $hookmanager->initHooks(array('projectdao'));
1474 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1475 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1476 if ($reshook > 0) {
1477 $result = $hookmanager->resPrint;
1478 } else {
1479 $result .= $hookmanager->resPrint;
1480 }
1481
1482 return $result;
1483 }
1484
1492 public function initAsSpecimen()
1493 {
1494 global $user, $langs, $conf;
1495
1496 $now = dol_now();
1497
1498 // Initialise parameters
1499 $this->id = 0;
1500 $this->ref = 'SPECIMEN';
1501 $this->entity = $conf->entity;
1502 $this->specimen = 1;
1503 $this->socid = 1;
1504 $this->date_c = $now;
1505 $this->date_m = $now;
1506 $this->date_start = $now;
1507 $this->date_end = $now + (3600 * 24 * 365);
1508 $this->note_public = 'SPECIMEN';
1509 $this->note_private = 'Private Note';
1510 $this->fk_project = 0;
1511 $this->opp_amount = 20000;
1512 $this->budget_amount = 10000;
1513
1514 $this->usage_opportunity = 1;
1515 $this->usage_task = 1;
1516 $this->usage_bill_time = 1;
1517 $this->usage_organize_event = 1;
1518
1519 /*
1520 $nbp = mt_rand(1, 9);
1521 $xnbp = 0;
1522 while ($xnbp < $nbp)
1523 {
1524 $line = new Task($this->db);
1525 $line->fk_project = 0;
1526 $line->label = $langs->trans("Label") . " " . $xnbp;
1527 $line->description = $langs->trans("Description") . " " . $xnbp;
1528
1529 $this->lines[]=$line;
1530 $xnbp++;
1531 }
1532 */
1533
1534 return 1;
1535 }
1536
1544 public function restrictedProjectArea(User $user, $mode = 'read')
1545 {
1546 // To verify role of users
1547 $userAccess = 0;
1548 if (($mode == 'read' && $user->hasRight('projet', 'all', 'lire')) || ($mode == 'write' && $user->hasRight('projet', 'all', 'creer')) || ($mode == 'delete' && $user->hasRight('projet', 'all', 'supprimer'))) {
1549 $userAccess = 1;
1550 } elseif ($this->public && (($mode == 'read' && $user->hasRight('projet', 'lire')) || ($mode == 'write' && $user->hasRight('projet', 'creer')) || ($mode == 'delete' && $user->hasRight('projet', 'supprimer')))) {
1551 $userAccess = 1;
1552 } else { // No access due to permission to read all projects, so we check if we are a contact of project
1553 foreach (array('internal', 'external') as $source) {
1554 $userRole = $this->liste_contact(4, $source);
1555 $num = count($userRole);
1556
1557 $nblinks = 0;
1558 while ($nblinks < $num) {
1559 if ($source == 'internal' && $user->id == $userRole[$nblinks]['id']) { // $userRole[$nblinks]['id'] is id of user (llx_user) for internal contacts
1560 if ($mode == 'read' && $user->hasRight('projet', 'lire')) {
1561 $userAccess++;
1562 }
1563 if ($mode == 'write' && $user->hasRight('projet', 'creer')) {
1564 $userAccess++;
1565 }
1566 if ($mode == 'delete' && $user->hasRight('projet', 'supprimer')) {
1567 $userAccess++;
1568 }
1569 }
1570 if ($source == 'external' && $user->socid > 0 && $user->socid == $userRole[$nblinks]['socid']) { // $userRole[$nblinks]['id'] is id of contact (llx_socpeople) or external contacts
1571 if ($mode == 'read' && $user->hasRight('projet', 'lire')) {
1572 $userAccess++;
1573 }
1574 if ($mode == 'write' && $user->hasRight('projet', 'creer')) {
1575 $userAccess++;
1576 }
1577 if ($mode == 'delete' && $user->hasRight('projet', 'supprimer')) {
1578 $userAccess++;
1579 }
1580 }
1581 $nblinks++;
1582 }
1583 }
1584 //if (empty($nblinks)) // If nobody has permission, we grant creator
1585 //{
1586 // if ((!empty($this->user_author_id) && $this->user_author_id == $user->id))
1587 // {
1588 // $userAccess = 1;
1589 // }
1590 //}
1591 }
1592
1593 return ($userAccess ? $userAccess : -1);
1594 }
1595
1606 public function getProjectsAuthorizedForUser($user, $mode = 0, $list = 0, $socid = 0, $filter = '')
1607 {
1608 $projects = array();
1609 $temp = array();
1610
1611 $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref";
1612 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
1613 if ($mode == 0) {
1614 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_contact as ec ON ec.element_id = p.rowid";
1615 } elseif ($mode == 1) {
1616 $sql .= ", ".MAIN_DB_PREFIX."element_contact as ec";
1617 } elseif ($mode == 2) {
1618 // No filter. Use this if user has permission to see all project
1619 }
1620 $sql .= " WHERE p.entity IN (".getEntity('project').")";
1621 // Internal users must see project he is contact to even if project linked to a third party he can't see.
1622 //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).")";
1623 if ($socid > 0) {
1624 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
1625 }
1626
1627 // Get id of types of contacts for projects (This list never contains a lot of elements)
1628 $listofprojectcontacttype = array();
1629 $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
1630 $sql2 .= " WHERE ctc.element = '".$this->db->escape($this->element)."'";
1631 $sql2 .= " AND ctc.source = 'internal'";
1632 $resql = $this->db->query($sql2);
1633 if ($resql) {
1634 while ($obj = $this->db->fetch_object($resql)) {
1635 $listofprojectcontacttype[$obj->rowid] = $obj->code;
1636 }
1637 } else {
1638 dol_print_error($this->db);
1639 }
1640 if (count($listofprojectcontacttype) == 0) {
1641 $listofprojectcontacttype[0] = '0'; // To avoid syntax error if not found
1642 }
1643
1644 if ($mode == 0) {
1645 $sql .= " AND ( p.public = 1";
1646 $sql .= " OR ( ec.fk_c_type_contact IN (".$this->db->sanitize(implode(',', array_keys($listofprojectcontacttype))).")";
1647 $sql .= " AND ec.fk_socpeople = ".((int) $user->id).")";
1648 $sql .= " )";
1649 } elseif ($mode == 1) {
1650 $sql .= " AND ec.element_id = p.rowid";
1651 $sql .= " AND (";
1652 $sql .= " ( ec.fk_c_type_contact IN (".$this->db->sanitize(implode(',', array_keys($listofprojectcontacttype))).")";
1653 $sql .= " AND ec.fk_socpeople = ".((int) $user->id).")";
1654 $sql .= " )";
1655 } elseif ($mode == 2) {
1656 // No filter. Use this if user has permission to see all project
1657 }
1658
1659 // Manage filter
1660 $errormessage = '';
1661 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1662 if ($errormessage) {
1663 $this->errors[] = $errormessage;
1664 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1665 $sql .= $filter;
1666 }
1667
1668 //print $sql;
1669
1670 $resql = $this->db->query($sql);
1671 if ($resql) {
1672 $num = $this->db->num_rows($resql);
1673 $i = 0;
1674 while ($i < $num) {
1675 $row = $this->db->fetch_row($resql);
1676 $projects[$row[0]] = $row[1];
1677 $temp[] = $row[0];
1678 $i++;
1679 }
1680
1681 $this->db->free($resql);
1682
1683 if ($list) {
1684 if (empty($temp)) {
1685 return '0';
1686 }
1687 $result = implode(',', $temp);
1688 return $result;
1689 }
1690 } else {
1691 dol_print_error($this->db);
1692 }
1693
1694 return $projects;
1695 }
1696
1712 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)
1713 {
1714 global $langs, $conf;
1715
1716 $error = 0;
1717 $clone_project_id = 0; // For static toolcheck
1718
1719 dol_syslog("createFromClone clone_contact=".json_encode($clone_contact)." clone_task=".json_encode($clone_task)." clone_project_file=".json_encode($clone_project_file)." clone_note=".json_encode($clone_note)." move_date=".json_encode($move_date), LOG_DEBUG);
1720
1721 $now = dol_mktime(0, 0, 0, idate('m', dol_now()), idate('d', dol_now()), idate('Y', dol_now()));
1722
1723 $clone_project = new Project($this->db);
1724
1725 $clone_project->context['createfromclone'] = 'createfromclone';
1726
1727 $this->db->begin();
1728
1729 // Load source object
1730 $clone_project->fetch($fromid);
1731 $clone_project->fetch_optionals();
1732 if ($newthirdpartyid > 0) {
1733 $clone_project->socid = $newthirdpartyid;
1734 }
1735 $clone_project->fetch_thirdparty();
1736
1737 $orign_dt_start = $clone_project->date_start;
1738 $orign_project_ref = $clone_project->ref;
1739
1740 $clone_project->id = 0;
1741 if ($move_date) {
1742 $clone_project->date_start = $now;
1743 if (!(empty($clone_project->date_end))) {
1744 $clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start);
1745 }
1746 }
1747
1748 $clone_project->date_c = $now;
1749
1750 if (!$clone_note) {
1751 $clone_project->note_private = '';
1752 $clone_project->note_public = '';
1753 }
1754
1755 //Generate next ref
1756 $defaultref = '';
1757 $obj = !getDolGlobalString('PROJECT_ADDON') ? 'mod_project_simple' : $conf->global->PROJECT_ADDON;
1758 // Search template files
1759 $file = '';
1760 $classname = '';
1761 $filefound = 0;
1762 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1763 foreach ($dirmodels as $reldir) {
1764 $file = dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0);
1765 if (file_exists($file)) {
1766 $filefound = 1;
1767 dol_include_once($reldir."core/modules/project/".$obj.'.php');
1768 $modProject = new $obj();
1769 $defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty) ? $clone_project->thirdparty : null, $clone_project);
1770 break;
1771 }
1772 }
1773 if (is_numeric($defaultref) && $defaultref <= 0) {
1774 $defaultref = '';
1775 }
1776
1777 $clone_project->ref = $defaultref;
1778 $clone_project->title = $langs->trans("CopyOf").' '.$clone_project->title;
1779
1780 // Create clone
1781 $result = $clone_project->create($user, $notrigger);
1782
1783 // Other options
1784 if ($result < 0) {
1785 $this->error .= $clone_project->error;
1786 $error++;
1787 }
1788
1789 if (!$error) {
1790 //Get the new project id
1791 $clone_project_id = $clone_project->id;
1792
1793 //Note Update
1794 if (!$clone_note) {
1795 $clone_project->note_private = '';
1796 $clone_project->note_public = '';
1797 } else {
1798 $this->db->begin();
1799 $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES | ENT_HTML5), '_public');
1800 if ($res < 0) {
1801 $this->error .= $clone_project->error;
1802 $error++;
1803 $this->db->rollback();
1804 } else {
1805 $this->db->commit();
1806 }
1807
1808 $this->db->begin();
1809 $res = $clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES | ENT_HTML5), '_private');
1810 if ($res < 0) {
1811 $this->error .= $clone_project->error;
1812 $error++;
1813 $this->db->rollback();
1814 } else {
1815 $this->db->commit();
1816 }
1817 }
1818
1819 //Duplicate contact
1820 if ($clone_contact) {
1821 $origin_project = new Project($this->db);
1822 $origin_project->fetch($fromid);
1823
1824 foreach (array('internal', 'external') as $source) {
1825 $tab = $origin_project->liste_contact(-1, $source);
1826 if (is_array($tab) && count($tab) > 0) {
1827 foreach ($tab as $contacttoadd) {
1828 $clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger);
1829 if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1830 $langs->load("errors");
1831 $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1832 $error++;
1833 } else {
1834 if ($clone_project->error != '') {
1835 $this->error .= $clone_project->error;
1836 $error++;
1837 }
1838 }
1839 }
1840 } elseif ($tab < 0) {
1841 $this->error .= $origin_project->error;
1842 $error++;
1843 }
1844 }
1845 }
1846
1847 //Duplicate file
1848 if ($clone_project_file) {
1849 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1850
1851 $clone_project_dir = $conf->project->dir_output."/".dol_sanitizeFileName($defaultref);
1852 $ori_project_dir = $conf->project->dir_output."/".dol_sanitizeFileName($orign_project_ref);
1853
1854 if (dol_mkdir($clone_project_dir) >= 0) {
1855 $filearray = dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1856 foreach ($filearray as $key => $file) {
1857 $rescopy = dol_copy($ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name'], 0, 1);
1858 if (is_numeric($rescopy) && $rescopy < 0) {
1859 $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_project_dir.'/'.$file['name'], $clone_project_dir.'/'.$file['name']);
1860 $error++;
1861 }
1862 }
1863 } else {
1864 $this->error .= $langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1865 $error++;
1866 }
1867 }
1868
1869 //Duplicate task
1870 if ($clone_task) {
1871 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
1872
1873 $taskstatic = new Task($this->db);
1874
1875 // Security check
1876 $socid = 0;
1877 if ($user->socid > 0) {
1878 $socid = $user->socid;
1879 }
1880
1881 $tasksarray = $taskstatic->getTasksArray(0, 0, $fromid, $socid, 0);
1882
1883 $tab_conv_child_parent = array();
1884
1885 // Loop on each task, to clone it
1886 foreach ($tasksarray as $tasktoclone) {
1887 $result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_task_parent, $move_date, true, false, $clone_task_file, true, false);
1888 if ($result_clone <= 0) {
1889 $this->error .= $taskstatic->error;
1890 $error++;
1891 } else {
1892 $new_task_id = $result_clone;
1893 $taskstatic->fetch($tasktoclone->id);
1894
1895 //manage new parent clone task id
1896 // if the current task has child we store the original task id and the equivalent clone task id
1897 if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent)) {
1898 $tab_conv_child_parent[$tasktoclone->id] = $new_task_id;
1899 }
1900 }
1901 }
1902
1903 //Parse all clone node to be sure to update new parent
1904 $tasksarray = $taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0);
1905 foreach ($tasksarray as $task_cloned) {
1906 $taskstatic->fetch($task_cloned->id);
1907 if ($taskstatic->fk_task_parent != 0) {
1908 $taskstatic->fk_task_parent = $tab_conv_child_parent[$taskstatic->fk_task_parent];
1909 }
1910 $res = $taskstatic->update($user, $notrigger);
1911 if ($result_clone <= 0) {
1912 $this->error .= $taskstatic->error;
1913 $error++;
1914 }
1915 }
1916 }
1917 }
1918
1919 unset($clone_project->context['createfromclone']);
1920
1921 if (!$error && $clone_project_id != 0) {
1922 $this->db->commit();
1923 return $clone_project_id;
1924 } else {
1925 $this->db->rollback();
1926 dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : ".$this->error, LOG_ERR);
1927 return -1;
1928 }
1929 }
1930
1931
1938 public function shiftTaskDate($old_project_dt_start)
1939 {
1940 global $user, $langs, $conf;
1941
1942 $error = 0;
1943 $result = 0;
1944
1945 $taskstatic = new Task($this->db);
1946
1947 // Security check
1948 $socid = 0;
1949 if ($user->socid > 0) {
1950 $socid = $user->socid;
1951 }
1952
1953 $tasksarray = $taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1954
1955 foreach ($tasksarray as $tasktoshiftdate) {
1956 $to_update = false;
1957 // Fetch only if update of date will be made
1958 if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) {
1959 //dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1960 $to_update = true;
1961 $task = new Task($this->db);
1962 $result = $task->fetch($tasktoshiftdate->id);
1963 if (!$result) {
1964 $error++;
1965 $this->error .= $task->error;
1966 }
1967 }
1968 //print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1969
1970 //Calculate new task start date with difference between old proj start date and origin task start date
1971 if (!empty($tasktoshiftdate->date_start)) {
1972 $task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
1973 }
1974
1975 //Calculate new task end date with difference between origin proj end date and origin task end date
1976 if (!empty($tasktoshiftdate->date_end)) {
1977 $task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1978 }
1979
1980 if ($to_update) {
1981 $result = $task->update($user);
1982 if (!$result) {
1983 $error++;
1984 $this->error .= $task->error;
1985 }
1986 }
1987 }
1988 if ($error != 0) {
1989 return -1;
1990 }
1991 return $result;
1992 }
1993
1994
1995 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2003 public function update_element($tableName, $elementSelectId)
2004 {
2005 // phpcs:enable
2006 $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName;
2007
2008 if ($tableName == "actioncomm") {
2009 $sql .= " SET fk_project=".$this->id;
2010 $sql .= " WHERE id=".((int) $elementSelectId);
2011 } elseif (in_array($tableName, ["entrepot","mrp_mo","stocktransfer_stocktransfer"])) {
2012 $sql .= " SET fk_project=".$this->id;
2013 $sql .= " WHERE rowid=".((int) $elementSelectId);
2014 } else {
2015 $sql .= " SET fk_projet=".$this->id;
2016 $sql .= " WHERE rowid=".((int) $elementSelectId);
2017 }
2018
2019 dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
2020 $resql = $this->db->query($sql);
2021 if (!$resql) {
2022 $this->error = $this->db->lasterror();
2023 return -1;
2024 } else {
2025 return 1;
2026 }
2027 }
2028
2029 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2039 public function remove_element($tableName, $elementSelectId, $projectfield = 'fk_projet')
2040 {
2041 // phpcs:enable
2042 $sql = "UPDATE ".MAIN_DB_PREFIX.$tableName;
2043
2044 if ($tableName == "actioncomm") {
2045 $sql .= " SET fk_project=NULL";
2046 $sql .= " WHERE id=".((int) $elementSelectId);
2047 } else {
2048 $sql .= " SET ".$projectfield."=NULL";
2049 $sql .= " WHERE rowid=".((int) $elementSelectId);
2050 }
2051
2052 dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
2053 $resql = $this->db->query($sql);
2054 if (!$resql) {
2055 $this->error = $this->db->lasterror();
2056 return -1;
2057 } else {
2058 return 1;
2059 }
2060 }
2061
2072 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2073 {
2074 global $conf, $langs;
2075
2076 $langs->load("projects");
2077
2078 if (!dol_strlen($modele)) {
2079 $modele = 'baleine';
2080
2081 if ($this->model_pdf) {
2082 $modele = $this->model_pdf;
2083 } elseif (getDolGlobalString('PROJECT_ADDON_PDF')) {
2084 $modele = getDolGlobalString('PROJECT_ADDON_PDF');
2085 }
2086 }
2087
2088 $modelpath = "core/modules/project/doc/";
2089
2090 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2091 }
2092
2093
2103 public function loadTimeSpent($datestart, $taskid = 0, $userid = 0)
2104 {
2105 $this->weekWorkLoad = array();
2106 $this->weekWorkLoadPerTask = array();
2107
2108 if (empty($datestart)) {
2109 dol_print_error(null, 'Error datestart parameter is empty');
2110 }
2111
2112 $sql = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element";
2113 $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
2114 $sql .= " WHERE ptt.fk_element = pt.rowid";
2115 $sql .= " AND ptt.elementtype = 'task'";
2116 $sql .= " AND pt.fk_projet = ".((int) $this->id);
2117 $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' ";
2118 $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
2119 if ($taskid) {
2120 $sql .= " AND ptt.fk_element=".((int) $taskid);
2121 }
2122 if (is_numeric($userid)) {
2123 $sql .= " AND ptt.fk_user=".((int) $userid);
2124 }
2125
2126 //print $sql;
2127 $resql = $this->db->query($sql);
2128 if ($resql) {
2129 $daylareadyfound = array();
2130
2131 $num = $this->db->num_rows($resql);
2132 $i = 0;
2133 // Loop on each record found, so each couple (project id, task id)
2134 while ($i < $num) {
2135 $obj = $this->db->fetch_object($resql);
2136 $day = $this->db->jdate($obj->element_date); // task_date is date without hours
2137 if (empty($daylareadyfound[$day])) {
2138 $this->weekWorkLoad[$day] = $obj->element_duration;
2139 $this->weekWorkLoadPerTask[$day][$obj->fk_element] = $obj->element_duration;
2140 } else {
2141 $this->weekWorkLoad[$day] += $obj->element_duration;
2142 $this->weekWorkLoadPerTask[$day][$obj->fk_element] += $obj->element_duration;
2143 }
2144 $daylareadyfound[$day] = 1;
2145 $i++;
2146 }
2147 $this->db->free($resql);
2148 return 1;
2149 } else {
2150 $this->error = "Error ".$this->db->lasterror();
2151 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
2152 return -1;
2153 }
2154 }
2155
2165 public function loadTimeSpentMonth($datestart, $taskid = 0, $userid = 0)
2166 {
2167 $this->monthWorkLoad = array();
2168 $this->monthWorkLoadPerTask = array();
2169
2170 if (empty($datestart)) {
2171 dol_print_error(null, 'Error datestart parameter is empty');
2172 }
2173
2174 $sql = "SELECT ptt.rowid as taskid, ptt.element_duration, ptt.element_date, ptt.element_datehour, ptt.fk_element";
2175 $sql .= " FROM ".MAIN_DB_PREFIX."element_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
2176 $sql .= " WHERE ptt.fk_element = pt.rowid";
2177 $sql .= " AND ptt.elementtype = 'task'";
2178 $sql .= " AND pt.fk_projet = ".((int) $this->id);
2179 $sql .= " AND (ptt.element_date >= '".$this->db->idate($datestart)."' ";
2180 $sql .= " AND ptt.element_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'm') - 1)."')";
2181 if ($taskid) {
2182 $sql .= " AND ptt.fk_element=".((int) $taskid);
2183 }
2184 if (is_numeric($userid)) {
2185 $sql .= " AND ptt.fk_user=".((int) $userid);
2186 }
2187
2188 //print $sql;
2189 $resql = $this->db->query($sql);
2190 if ($resql) {
2191 $weekalreadyfound = array();
2192
2193 $num = $this->db->num_rows($resql);
2194 $i = 0;
2195 // Loop on each record found, so each couple (project id, task id)
2196 while ($i < $num) {
2197 $obj = $this->db->fetch_object($resql);
2198 if (!empty($obj->element_date)) {
2199 $date = explode('-', $obj->element_date);
2200 $week_number = getWeekNumber($date[2], $date[1], $date[0]);
2201 }
2202 '@phan-var-force int $week_number'; // Needed because phan considers it might be null
2203 if (empty($weekalreadyfound[$week_number])) {
2204 $this->monthWorkLoad[$week_number] = $obj->element_duration;
2205 $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] = $obj->element_duration;
2206 } else {
2207 $this->monthWorkLoad[$week_number] += $obj->element_duration;
2208 $this->monthWorkLoadPerTask[$week_number][$obj->fk_element] += $obj->element_duration;
2209 }
2210 $weekalreadyfound[$week_number] = 1;
2211 $i++;
2212 }
2213 $this->db->free($resql);
2214 return 1;
2215 } else {
2216 $this->error = "Error ".$this->db->lasterror();
2217 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
2218 return -1;
2219 }
2220 }
2221
2222 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2229 public function load_board($user)
2230 {
2231 // phpcs:enable
2232 global $conf, $langs;
2233
2234 // For external user, no check is done on company because readability is managed by public status of project and assignment.
2235 //$socid=$user->socid;
2236
2237 $response = new WorkboardResponse();
2238 $response->warning_delay = $conf->project->warning_delay / 60 / 60 / 24;
2239 $response->label = $langs->trans("OpenedProjects");
2240 $response->labelShort = $langs->trans("Opened");
2241 $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
2242 $response->img = img_object('', "projectpub");
2243 $response->nbtodo = 0;
2244 $response->nbtodolate = 0;
2245
2246 $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
2247 $sql .= " FROM (".MAIN_DB_PREFIX."projet as p";
2248 $sql .= ")";
2249 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2250 // For external user, no check is done on company permission because readability is managed by public status of project and assignment.
2251 //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2252 $sql .= " WHERE p.fk_statut = 1";
2253 $sql .= " AND p.entity IN (".getEntity('project').')';
2254
2255
2256 $projectsListId = null;
2257 if (!$user->hasRight("projet", "all", "lire")) {
2258 $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
2259 $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
2260 if (empty($projectsListId)) {
2261 return $response;
2262 }
2263
2264 $sql .= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2265 }
2266
2267 // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2268 //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).")";
2269 // For external user, no check is done on company permission because readability is managed by public status of project and assignment.
2270 //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))";
2271
2272 //print $sql;
2273 $resql = $this->db->query($sql);
2274 if ($resql) {
2275 $project_static = new Project($this->db);
2276
2277
2278 // This assignment in condition is not a bug. It allows walking the results.
2279 while ($obj = $this->db->fetch_object($resql)) {
2280 $response->nbtodo++;
2281
2282 $project_static->statut = $obj->status;
2283 $project_static->opp_status = $obj->fk_opp_status;
2284 $project_static->date_end = $this->db->jdate($obj->datee);
2285
2286 if ($project_static->hasDelay()) {
2287 $response->nbtodolate++;
2288 }
2289 }
2290
2291 return $response;
2292 }
2293
2294 $this->error = $this->db->error();
2295 return -1;
2296 }
2297
2306 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2307 {
2308 $tables = array(
2309 'projet'
2310 );
2311
2312 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2313 }
2314
2315
2321 public function loadStateBoard()
2322 {
2323 global $user;
2324
2325 $this->nb = array();
2326
2327 $sql = "SELECT count(p.rowid) as nb";
2328 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2329 $sql .= " WHERE";
2330 $sql .= " p.entity IN (".getEntity('project').")";
2331 if (!$user->hasRight('projet', 'all', 'lire')) {
2332 $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
2333 $sql .= "AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
2334 }
2335
2336 $resql = $this->db->query($sql);
2337 if ($resql) {
2338 while ($obj = $this->db->fetch_object($resql)) {
2339 $this->nb["projects"] = $obj->nb;
2340 }
2341 $this->db->free($resql);
2342 return 1;
2343 } else {
2344 dol_print_error($this->db);
2345 $this->error = $this->db->error();
2346 return -1;
2347 }
2348 }
2349
2350
2356 public function hasDelay()
2357 {
2358 global $conf;
2359
2360 if (!($this->statut == self::STATUS_VALIDATED)) {
2361 return false;
2362 }
2363 if (!$this->date_end) {
2364 return false;
2365 }
2366
2367 $now = dol_now();
2368
2369 return ($this->date_end) < ($now - $conf->project->warning_delay);
2370 }
2371
2372
2379 public function info($id)
2380 {
2381 $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
2382 $sql .= ' date_close as datecloture,';
2383 $sql .= ' fk_user_creat as fk_user_author, fk_user_close as fk_user_cloture';
2384 $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as c';
2385 $sql .= ' WHERE c.rowid = '.((int) $id);
2386 $result = $this->db->query($sql);
2387 if ($result) {
2388 if ($this->db->num_rows($result)) {
2389 $obj = $this->db->fetch_object($result);
2390
2391 $this->id = $obj->rowid;
2392
2393 $this->user_creation_id = $obj->fk_user_author;
2394 $this->user_closing_id = $obj->fk_user_cloture;
2395
2396 $this->date_creation = $this->db->jdate($obj->datec);
2397 $this->date_modification = $this->db->jdate($obj->datem);
2398 $this->date_cloture = $this->db->jdate($obj->datecloture);
2399 }
2400
2401 $this->db->free($result);
2402 } else {
2403 dol_print_error($this->db);
2404 }
2405 }
2406
2417 public function setCategories($categories)
2418 {
2419 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2420 return parent::setCategoriesCommon($categories, Categorie::TYPE_PROJECT);
2421 }
2422
2423
2431 public function getLinesArray($user, $loadRoleMode = 1)
2432 {
2433 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
2434 $taskstatic = new Task($this->db);
2435
2436 $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0, '', '-1', '', 0, 0, array(), 0, array(), 0, $loadRoleMode);
2437 return 1;
2438 }
2439
2457 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 = '')
2458 {
2459 global $conf, $langs;
2460 // TODO EMAIL
2461
2462 return 1;
2463 }
2472 public function getKanbanView($option = '', $arraydata = null, $size = '')
2473 {
2474 global $conf, $langs;
2475
2476 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2477
2478 if (empty($size)) {
2479 if (empty($conf->dol_optimize_smallscreen)) {
2480 $size = 'large';
2481 } else {
2482 $size = 'small';
2483 }
2484 }
2485
2486 $return = '<div class="box-flex-item '.($size == 'small' ? 'box-flex-item-small' : '').' box-flex-grow-zero">';
2487 $return .= '<div class="info-box info-box-sm">';
2488 $return .= '<span class="info-box-icon bg-infobox-action">';
2489 $return .= img_picto('', $this->public ? 'projectpub' : $this->picto);
2490 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2491 $return .= '</span>';
2492 $return .= '<div class="info-box-content">';
2493 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref);
2494 if ($this->hasDelay()) {
2495 $return .= img_warning($langs->trans('Late'));
2496 }
2497 $return .= '</span>';
2498 if ($selected >= 0) {
2499 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2500 }
2501 // Date
2502 /*
2503 if (property_exists($this, 'date_start') && $this->date_start) {
2504 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_start, 'day').'</>';
2505 }
2506 if (property_exists($this, 'date_end') && $this->date_end) {
2507 if ($this->date_start) {
2508 $return .= ' - ';
2509 } else {
2510 $return .= '<br>';
2511 }
2512 $return .= '<span class="info-box-label">'.dol_print_date($this->date_end, 'day').'</span>';
2513 }*/
2514 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
2515 $return .= '<br><div class="info-box-ref tdoverflowmax150 inline-block valignmiddle">'.$this->thirdparty->getNomUrl(1);
2516 $return .= '</div><div class="inline-block valignmiddle">';
2517 $return .= dol_print_phone($this->thirdparty->phone, $this->thirdparty->country_code, 0, $this->thirdparty->id, 'tel', 'hidenum', 'phone', $this->thirdparty->phone, 0, 'marginleftonly');
2518 $return .= '</div>';
2519 }
2520 if (!empty($arraydata['assignedusers'])) {
2521 $return .= '<br>';
2522 if ($this->public) {
2523 $return .= img_picto($langs->trans('Visibility').': '.$langs->trans('SharedProject'), 'world', 'class="paddingrightonly valignmiddle"');
2524 //print $langs->trans('SharedProject');
2525 } else {
2526 $return .= img_picto($langs->trans('Visibility').': '.$langs->trans('PrivateProject'), 'private', 'class="paddingrightonly valignmiddle"');
2527 //print $langs->trans('PrivateProject');
2528 }
2529
2530 $return .= ' <span class="small valignmiddle">'.$arraydata['assignedusers'].'</span>';
2531 }
2532 /*if (property_exists($this, 'user_author_id')) {
2533 $return .= '<br><span class="info-box-label opacitymedium">'.$langs->trans("Author").'</span>';
2534 $return .= '<span> : '.$user->getNomUrl(1).'</span>';
2535 }*/
2536 $return .= '<br><div>'; // start div line status
2537 if ($this->usage_opportunity && $this->opp_status_code) {
2538 //$return .= '<br><span class="info-bo-label opacitymedium">'.$langs->trans("OpportunityStatusShort").'</span>';
2539 //$return .= '<div class="small inline-block">'.dol_trunc($langs->trans("OppStatus".$this->opp_status_code), 5).'</div>';
2540 $return .= '<div class="opacitymedium small marginrightonly inline-block" title="'.dol_escape_htmltag($langs->trans("OppStatus".$this->opp_status_code)).'">'.round($this->opp_percent).'%</div>';
2541 $return .= ' <div class="amount small marginrightonly inline-block">'.price($this->opp_amount).'</div>';
2542 }
2543 if (method_exists($this, 'getLibStatut')) {
2544 $return .= '<div class="info-box-status small inline-block valignmiddle">'.$this->getLibStatut(3).'</div>';
2545 }
2546 $return .= '</div>'; // end div line status
2547
2548 $return .= '</div>';
2549 $return .= '</div>';
2550 $return .= '</div>';
2551
2552 return $return;
2553 }
2554
2560 public function getChildren()
2561 {
2562 $children = [];
2563 $sql = 'SELECT rowid,title';
2564 $sql .= ' FROM '.MAIN_DB_PREFIX.'projet';
2565 $sql .= ' WHERE fk_project = '.((int) $this->id);
2566 $sql .= ' ORDER BY title';
2567 $result = $this->db->query($sql);
2568 if ($result) {
2569 $n = $this->db->num_rows($result);
2570 while ($n) {
2571 $children[] = $this->db->fetch_object($result);
2572 $n--;
2573 }
2574 $this->db->free($result);
2575 } else {
2576 dol_print_error($this->db);
2577 }
2578
2579 return $children;
2580 }
2581
2586 public function createWeeklyReport()
2587 {
2588 global $mysoc, $user;
2589
2590 $now = dol_now();
2591 $nowDate = dol_getdate($now, true);
2592
2593 $errormesg = '';
2594 $errorsMsg = array();
2595
2596 $firstDayOfWeekTS = dol_get_first_day_week($nowDate['mday'], $nowDate['mon'], $nowDate['year']);
2597
2598 $firstDayOfWeekDate = dol_mktime(0, 0, 0, $nowDate['mon'], $firstDayOfWeekTS['first_day'], $nowDate['year']);
2599
2600 $lastWeekStartTS = dol_time_plus_duree($firstDayOfWeekDate, -7, 'd');
2601
2602 $lastWeekEndTS = dol_time_plus_duree($lastWeekStartTS, 6, 'd');
2603
2604 $startDate = dol_print_date($lastWeekStartTS, '%Y-%m-%d 00:00:00');
2605 $endDate = dol_print_date($lastWeekEndTS, '%Y-%m-%d 23:59:59');
2606
2607 $sql = "SELECT
2608 u.rowid AS user_id,
2609 CONCAT(u.firstname, ' ', u.lastname) AS name,
2610 u.email,u.weeklyhours,
2611 SUM(et.element_duration) AS total_seconds
2612 FROM
2613 ".MAIN_DB_PREFIX."element_time AS et
2614 JOIN
2615 ".MAIN_DB_PREFIX."user AS u ON et.fk_user = u.rowid
2616 WHERE
2617 et.element_date BETWEEN '".$this->db->escape($startDate)."' AND '".$this->db->escape($endDate)."'
2618 AND et.elementtype = 'task'
2619 GROUP BY
2620 et.fk_user";
2621
2622 $resql = $this->db->query($sql);
2623 if (!$resql) {
2624 dol_print_error($this->db);
2625 return -1;
2626 } else {
2627 $reportContent = "<span>Weekly time report from $startDate to $endDate </span><br><br>";
2628 $reportContent .= '<table border="1" style="border-collapse: collapse;">';
2629 $reportContent .= '<tr><th>Nom d\'utilisateur</th><th>Temps saisi (heures)</th><th>Temps travaillé par semaine (heures)</th></tr>';
2630
2631 $weekendEnabled = 0;
2632 $to = '';
2633 $nbMailSend = 0;
2634 $error = 0;
2635 $errors_to = '';
2636 while ($obj = $this->db->fetch_object($resql)) {
2637 $to = $obj->email;
2638 $numHolidays = num_public_holiday($lastWeekStartTS, $lastWeekEndTS, $mysoc->country_code, 1);
2639 if (getDolGlobalString('MAIN_NON_WORKING_DAYS_INCLUDE_SATURDAY') && getDolGlobalString('MAIN_NON_WORKING_DAYS_INCLUDE_SUNDAY')) {
2640 $numHolidays = $numHolidays - 2;
2641 $weekendEnabled = 2;
2642 }
2643
2644 $dailyHours = $obj->weeklyhours / (7 - $weekendEnabled);
2645
2646 // Adjust total on seconde
2647 $adjustedSeconds = $obj->total_seconds + ($numHolidays * $dailyHours * 3600);
2648
2649 $totalHours = round($adjustedSeconds / 3600, 2);
2650
2651 $reportContent .= "<tr><td>{$obj->name}</td><td>{$totalHours}</td><td>".round($obj->weeklyhours, 2)."</td></tr>";
2652
2653 $reportContent .= '</table>';
2654
2655 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
2656
2657 // PREPARE EMAIL
2658 $errormesg = '';
2659
2660 $subject = 'Rapport hebdomadaire des temps travaillés';
2661
2662 $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
2663 if (empty($from)) {
2664 $errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM";
2665 $error++;
2666 }
2667
2668 $mail = new CMailFile($subject, $to, $from, $reportContent, array(), array(), array(), '', '', 0, -1, '', '', 0, 'text/html');
2669
2670 if ($mail->sendfile()) {
2671 $nbMailSend++;
2672
2673 // Add a line into event table
2674 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
2675
2676 // Insert record of emails sent
2677 $actioncomm = new ActionComm($this->db);
2678
2679 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
2680 $actioncomm->socid = $this->thirdparty->id; // To link to a company
2681 $actioncomm->contact_id = 0;
2682
2683 $actioncomm->code = 'AC_EMAIL';
2684 $actioncomm->label = 'createWeeklyReportOK()';
2685 $actioncomm->fk_project = $this->id;
2686 $actioncomm->datep = dol_now();
2687 $actioncomm->datef = $actioncomm->datep;
2688 $actioncomm->percentage = -1; // Not applicable
2689 $actioncomm->authorid = $user->id; // User saving action
2690 $actioncomm->userownerid = $user->id; // Owner of action
2691 // Fields when action is an email (content should be added into note)
2692 $actioncomm->email_msgid = $mail->msgid;
2693 $actioncomm->email_subject = $subject;
2694 $actioncomm->email_from = $from;
2695 $actioncomm->email_sender = '';
2696 $actioncomm->email_to = $to;
2697
2698 $actioncomm->errors_to = $errors_to;
2699
2700 $actioncomm->elementtype = 'project_task';
2701 $actioncomm->fk_element = (int) $this->element;
2702
2703 $actioncomm->create($user);
2704 } else {
2705 $errormesg = $mail->error.' : '.$to;
2706 $error++;
2707
2708 // Add a line into event table
2709 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
2710
2711 // Insert record of emails sent
2712 $actioncomm = new ActionComm($this->db);
2713
2714 $actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically
2715 $actioncomm->socid = $this->thirdparty->id; // To link to a company
2716 $actioncomm->contact_id = 0;
2717
2718 $actioncomm->code = 'AC_EMAIL';
2719 $actioncomm->label = 'createWeeklyReportKO()';
2720 $actioncomm->note_private = $errormesg;
2721 $actioncomm->fk_project = $this->id;
2722 $actioncomm->datep = dol_now();
2723 $actioncomm->datef = $actioncomm->datep;
2724 $actioncomm->authorid = $user->id; // User saving action
2725 $actioncomm->userownerid = $user->id; // Owner of action
2726 // Fields when action is an email (content should be added into note)
2727 $actioncomm->email_msgid = $mail->msgid;
2728 $actioncomm->email_from = $from;
2729 $actioncomm->email_sender = '';
2730 $actioncomm->email_to = $to;
2731
2732 $actioncomm->errors_to = $errors_to;
2733
2734 $actioncomm->elementtype = 'project_task';
2735 $actioncomm->fk_element = (int) $this->element;
2736
2737 $actioncomm->create($user);
2738 }
2739 $this->db->commit();
2740 }
2741 }
2742 if (!empty($errormesg)) {
2743 $errorsMsg[] = $errormesg;
2744 }
2745
2746 if (!$error) {
2747 $this->output .= 'Nb of emails sent : '.$nbMailSend;
2748 dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO);
2749 return 0;
2750 } else {
2751 $this->error = 'Nb of emails sent : '.$nbMailSend.', '.(empty($errorsMsg) ? $error : implode(', ', $errorsMsg));
2752 dol_syslog(__METHOD__." end - ".$this->error, LOG_INFO);
2753 return $error;
2754 }
2755 }
2756}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:637
$object ref
Definition info.php:79
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
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.
createWeeklyReport()
Method for calculating weekly hours worked and generating a report.
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.
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.
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.
loadStateBoard()
Load indicators this->nb for the state board.
load_board($user)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getLinesArray($user, $loadRoleMode=1)
Create an array of tasks of current project.
get_element_list($type, $tablename, $datefieldname='', $date_start=null, $date_end=null, $projectkey='fk_projet')
Return list of elements for type, linked to a project.
getKanbanView($option='', $arraydata=null, $size='')
Return clicable link of object (with eventually picto)
info($id)
Charge les information 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.
dol_get_first_day_week($day, $month, $year, $gm=false)
Return first day of week for a date.
Definition date.lib.php:670
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:125
num_public_holiday($timestampStart, $timestampEnd, $country_code='', $lastday=0, $includesaturday=-1, $includesunday=-1, $includefriday=-1, $includemonday=-1)
Return the number of non working days including Friday, Saturday and Sunday (or not) between 2 dates ...
Definition date.lib.php:764
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($utf8_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:63
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 information (by default a local PHP server timestamp) Rep...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='')
Format phone numbers according to country.
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
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 TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:2010