Passed
Pull Request — master (#2)
by
unknown
26:19
created

Project::update_element()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 4
nop 2
dl 0
loc 23
rs 9.7998
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2002-2005 Rodolphe Quiedeville <[email protected]>
3
 * Copyright (C) 2005-2016 Laurent Destailleur  <[email protected]>
4
 * Copyright (C) 2005-2010 Regis Houssin        <[email protected]>
5
 * Copyright (C) 2013	   Florian Henry        <[email protected]>
6
 * Copyright (C) 2014-2017 Marcos García        <[email protected]>
7
 * Copyright (C) 2017      Ferran Marcet        <[email protected]>
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * 		\file       htdocs/projet/class/project.class.php
25
 * 		\ingroup    projet
26
 * 		\brief      File of class to manage projects
27
 */
28
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
29
30
/**
31
 *	Class to manage projects
32
 */
33
class Project extends CommonObject
34
{
35
36
    /**
37
	 * @var string ID to identify managed object
38
	 */
39
	public $element = 'project';
40
41
    /**
42
	 * @var string Name of table without prefix where object is stored
43
	 */
44
	public $table_element = 'projet';
45
46
    /**
47
	 * @var int    Name of subtable line
48
	 */
49
	public $table_element_line = 'projet_task';
50
51
    /**
52
	 * @var int Field with ID of parent key if this field has a parent
53
	 */
54
	public $fk_element = 'fk_projet';
55
56
	/**
57
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
58
	 * @var int
59
	 */
60
    public $ismultientitymanaged = 1;
61
62
    public $picto = 'projectpub';
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    protected $table_ref_field = 'ref';
68
69
    /**
70
	 * @var string description
71
	 */
72
	public $description;
73
74
	/**
75
	 * @var string
76
	 * @deprecated
77
	 * @see title
78
	 */
79
	public $titre;
80
81
    public $title;
82
    public $date_start;
83
    public $date_end;
84
    public $date_close;
85
86
    public $socid;             // To store id of thirdparty
87
    public $thirdparty_name;   // To store name of thirdparty (defined only in some cases)
88
89
    public $user_author_id;    //!< Id of project creator. Not defined if shared project.
90
	public $user_close_id;
91
    public $public;      //!< Tell if this is a public or private project
92
    public $budget_amount;
93
    public $bill_time;			// Is the time spent on project must be invoiced or not
94
95
    public $statuts_short;
96
    public $statuts_long;
97
98
    public $statut;			// 0=draft, 1=opened, 2=closed
99
    public $opp_status;		// opportunity status, into table llx_c_lead_status
100
	public $opp_percent;		// opportunity probability
101
102
    public $oldcopy;
103
104
    public $weekWorkLoad;			// Used to store workload details of a projet
105
    public $weekWorkLoadPerTask;	// Used to store workload details of tasks of a projet
106
107
	/**
108
	 * @var int Creation date
109
	 * @deprecated
110
	 * @see date_c
111
	 */
112
	public $datec;
113
114
	/**
115
	 * @var int Creation date
116
	 */
117
	public $date_c;
118
119
	/**
120
	 * @var int Modification date
121
	 * @deprecated
122
	 * @see date_m
123
	 */
124
	public $datem;
125
126
	/**
127
	 * @var int Modification date
128
	 */
129
	public $date_m;
130
131
	/**
132
	 * @var Task[]
133
	 */
134
	public $lines;
135
136
	/**
137
	 * Draft status
138
	 */
139
	const STATUS_DRAFT = 0;
140
141
	/**
142
	 * Open/Validated status
143
	 */
144
	const STATUS_VALIDATED = 1;
145
146
	/**
147
	 * Closed status
148
	 */
149
	const STATUS_CLOSED = 2;
150
151
152
153
    /**
154
     *  Constructor
155
     *
156
     *  @param      DoliDB		$db      Database handler
157
     */
158
    function __construct($db)
159
    {
160
        $this->db = $db;
161
162
        $this->statuts_short = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
163
        $this->statuts_long = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed');
164
    }
165
166
    /**
167
     *    Create a project into database
168
     *
169
     *    @param    User	$user       	User making creation
170
     *    @param	int		$notrigger		Disable triggers
171
     *    @return   int         			<0 if KO, id of created project if OK
172
     */
173
    function create($user, $notrigger=0)
174
    {
175
        global $conf, $langs;
176
177
        $error = 0;
178
        $ret = 0;
179
180
        $now=dol_now();
181
182
        // Clean parameters
183
        $this->note_private = dol_substr($this->note_private, 0, 65535);
184
        $this->note_public = dol_substr($this->note_public, 0, 65535);
185
186
        // Check parameters
187
        if (!trim($this->ref))
188
        {
189
            $this->error = 'ErrorFieldsRequired';
190
            dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
191
            return -1;
192
        }
193
        if (! empty($conf->global->PROJECT_THIRDPARTY_REQUIRED) && ! $this->socid > 0)
194
        {
195
            $this->error = 'ErrorFieldsRequired';
196
            dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
197
            return -1;
198
        }
199
200
        // Create project
201
        $this->db->begin();
202
203
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet (";
204
        $sql.= "ref";
205
        $sql.= ", title";
206
        $sql.= ", description";
207
        $sql.= ", fk_soc";
208
        $sql.= ", fk_user_creat";
209
        $sql.= ", fk_statut";
210
        $sql.= ", fk_opp_status";
211
        $sql.= ", opp_percent";
212
        $sql.= ", public";
213
        $sql.= ", datec";
214
        $sql.= ", dateo";
215
        $sql.= ", datee";
216
        $sql.= ", opp_amount";
217
        $sql.= ", budget_amount";
218
        $sql.= ", bill_time";
219
        $sql.= ", note_private";
220
        $sql.= ", note_public";
221
        $sql.= ", entity";
222
        $sql.= ") VALUES (";
223
        $sql.= "'" . $this->db->escape($this->ref) . "'";
224
        $sql.= ", '" . $this->db->escape($this->title) . "'";
225
        $sql.= ", '" . $this->db->escape($this->description) . "'";
226
        $sql.= ", " . ($this->socid > 0 ? $this->socid : "null");
227
        $sql.= ", " . $user->id;
228
        $sql.= ", ".(is_numeric($this->statut) ? $this->statut : '0');
229
        $sql.= ", ".(is_numeric($this->opp_status) ? $this->opp_status : 'NULL');
230
        $sql.= ", ".(is_numeric($this->opp_percent) ? $this->opp_percent : 'NULL');
231
        $sql.= ", " . ($this->public ? 1 : 0);
232
        $sql.= ", '".$this->db->idate($now)."'";
233
        $sql.= ", " . ($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
234
        $sql.= ", " . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
235
        $sql.= ", " . (strcmp($this->opp_amount,'') ? price2num($this->opp_amount) : 'null');
236
        $sql.= ", " . (strcmp($this->budget_amount,'') ? price2num($this->budget_amount) : 'null');
237
        $sql.= ", " . ($this->bill_time ? 1 : 0);
238
        $sql.= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null');
239
        $sql.= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null');
240
        $sql.= ", ".$conf->entity;
241
        $sql.= ")";
242
243
        dol_syslog(get_class($this)."::create", LOG_DEBUG);
244
        $resql = $this->db->query($sql);
245
        if ($resql)
246
        {
247
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "projet");
248
            $ret = $this->id;
249
250
            if (!$notrigger)
251
            {
252
                // Call trigger
253
                $result=$this->call_trigger('PROJECT_CREATE',$user);
254
                if ($result < 0) { $error++; }
255
                // End call triggers
256
            }
257
        }
258
        else
259
        {
260
            $this->error = $this->db->lasterror();
261
            $this->errno = $this->db->lasterrno();
262
            $error++;
263
        }
264
265
        // Update extrafield
266
        if (! $error) {
267
        	if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
268
        	{
269
        		$result=$this->insertExtraFields();
270
        		if ($result < 0)
271
        		{
272
        			$error++;
273
        		}
274
        	}
275
        }
276
277
        if (! $error && !empty($conf->global->MAIN_DISABLEDRAFTSTATUS))
278
        {
279
            $res = $this->setValid($user);
280
            if ($res < 0) $error++;
281
        }
282
283
        if (! $error)
284
        {
285
            $this->db->commit();
286
            return $ret;
287
        }
288
        else
289
        {
290
            $this->db->rollback();
291
            return -1;
292
        }
293
    }
294
295
    /**
296
     * Update a project
297
     *
298
     * @param  User		$user       User object of making update
299
     * @param  int		$notrigger  1=Disable all triggers
300
     * @return int                  <=0 if KO, >0 if OK
301
     */
302
    function update($user, $notrigger=0)
303
    {
304
        global $langs, $conf;
305
306
		$error=0;
307
308
        // Clean parameters
309
        $this->title = trim($this->title);
310
        $this->description = trim($this->description);
311
		if ($this->opp_amount < 0) $this->opp_amount='';
312
		if ($this->opp_percent < 0) $this->opp_percent='';
313
        if ($this->date_end && $this->date_end < $this->date_start)
314
        {
315
            $this->error = $langs->trans("ErrorDateEndLowerThanDateStart");
316
            $this->errors[] = $this->error;
317
            $this->db->rollback();
318
            dol_syslog(get_class($this)."::update error -3 " . $this->error, LOG_ERR);
319
            return -3;
320
        }
321
322
        if (dol_strlen(trim($this->ref)) > 0)
323
        {
324
            $this->db->begin();
325
326
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet SET";
327
            $sql.= " ref='" . $this->db->escape($this->ref) . "'";
328
            $sql.= ", title = '" . $this->db->escape($this->title) . "'";
329
            $sql.= ", description = '" . $this->db->escape($this->description) . "'";
330
            $sql.= ", fk_soc = " . ($this->socid > 0 ? $this->socid : "null");
331
            $sql.= ", fk_statut = " . $this->statut;
332
            $sql.= ", fk_opp_status = " . ((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null');
333
			$sql.= ", opp_percent = " . ((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null');
334
            $sql.= ", public = " . ($this->public ? 1 : 0);
335
            $sql.= ", datec=" . ($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null');
336
            $sql.= ", dateo=" . ($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null');
337
            $sql.= ", datee=" . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null');
338
            $sql.= ", date_close=" . ($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null');
339
            $sql.= ", fk_user_close=" . ($this->fk_user_close > 0 ? $this->fk_user_close : "null");
0 ignored issues
show
Bug Best Practice introduced by
The property fk_user_close does not exist on Project. Did you maybe forget to declare it?
Loading history...
340
            $sql.= ", opp_amount = " . (strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null");
341
            $sql.= ", budget_amount = " . (strcmp($this->budget_amount, '')  ? price2num($this->budget_amount) : "null");
342
            $sql.= ", fk_user_modif = " . $user->id;
343
            $sql.= ", bill_time = " . ($this->bill_time ? 1 : 0);
344
            $sql.= " WHERE rowid = " . $this->id;
345
346
            dol_syslog(get_class($this)."::update", LOG_DEBUG);
347
            $resql=$this->db->query($sql);
348
            if ($resql)
349
            {
350
                // Update extrafield
351
                if (! $error)
352
                {
353
                	if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
354
                	{
355
                		$result=$this->insertExtraFields();
356
                		if ($result < 0)
357
                		{
358
                			$error++;
359
                		}
360
                	}
361
                }
362
363
                if (! $error && ! $notrigger)
364
                {
365
                	// Call trigger
366
                	$result=$this->call_trigger('PROJECT_MODIFY',$user);
367
                	if ($result < 0) { $error++; }
368
                	// End call triggers
369
                }
370
371
                if (! $error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref))
372
                {
373
                	// We remove directory
374
                	if ($conf->projet->dir_output)
375
                	{
376
                		$olddir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($this->oldcopy->ref);
377
                		$newdir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($this->ref);
378
                		if (file_exists($olddir))
379
                		{
380
							include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
381
							$res=dol_move($olddir, $newdir);
382
							if (! $res)
383
                			{
384
							    $langs->load("errors");
385
								$this->error=$langs->trans('ErrorFailToRenameDir',$olddir,$newdir);
386
                				$error++;
387
                			}
388
                		}
389
                	}
390
                }
391
                if (! $error )
392
                {
393
                    $this->db->commit();
394
                    $result = 1;
395
                }
396
                else
397
              {
398
                    $this->db->rollback();
399
                    $result = -1;
400
                }
401
            }
402
            else
403
			{
404
		        $this->error = $this->db->lasterror();
405
		        $this->errors[] = $this->error;
406
		        $this->db->rollback();
407
			    if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
408
			    {
409
			        $result = -4;
410
			    }
411
			    else
412
			    {
413
			        $result = -2;
414
			    }
415
		        dol_syslog(get_class($this)."::update error " . $result . " " . $this->error, LOG_ERR);
416
			}
417
        }
418
        else
419
        {
420
            dol_syslog(get_class($this)."::update ref null");
421
            $result = -1;
422
        }
423
424
        return $result;
425
    }
426
427
    /**
428
     * 	Get object from database
429
     *
430
     * 	@param      int		$id       	Id of object to load
431
     * 	@param		string	$ref		Ref of project
432
     * 	@return     int      		   	>0 if OK, 0 if not found, <0 if KO
433
     */
434
    function fetch($id, $ref='')
435
    {
436
    	global $conf;
437
438
        if (empty($id) && empty($ref)) return -1;
439
440
        $sql = "SELECT rowid, ref, title, description, public, datec, opp_amount, budget_amount,";
441
        $sql.= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent,";
442
        $sql.= " note_private, note_public, model_pdf, bill_time";
443
        $sql.= " FROM " . MAIN_DB_PREFIX . "projet";
444
        if (! empty($id))
445
        {
446
        	$sql.= " WHERE rowid=".$id;
447
        }
448
        else if (! empty($ref))
449
        {
450
        	$sql.= " WHERE ref='".$this->db->escape($ref)."'";
451
        	$sql.= " AND entity IN (".getEntity('project').")";
452
        }
453
454
        dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
455
        $resql = $this->db->query($sql);
456
        if ($resql)
457
        {
458
            $num_rows = $this->db->num_rows($resql);
459
460
            if ($num_rows)
461
            {
462
                $obj = $this->db->fetch_object($resql);
463
464
                $this->id = $obj->rowid;
465
                $this->ref = $obj->ref;
466
                $this->title = $obj->title;
467
                $this->titre = $obj->title; // TODO deprecated
468
                $this->description = $obj->description;
469
                $this->date_c = $this->db->jdate($obj->datec);
470
                $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
471
                $this->date_m = $this->db->jdate($obj->tms);
472
                $this->datem = $this->db->jdate($obj->tms);  // TODO deprecated
473
                $this->date_start = $this->db->jdate($obj->dateo);
474
                $this->date_end = $this->db->jdate($obj->datee);
475
                $this->date_close = $this->db->jdate($obj->date_close);
476
                $this->note_private = $obj->note_private;
477
                $this->note_public = $obj->note_public;
478
                $this->socid = $obj->fk_soc;
479
                $this->user_author_id = $obj->fk_user_creat;
480
                $this->user_modification_id = $obj->fk_user_modif;
481
                $this->user_close_id = $obj->fk_user_close;
482
                $this->public = $obj->public;
483
                $this->statut = $obj->fk_statut;
484
                $this->opp_status = $obj->fk_opp_status;
485
                $this->opp_amount	= $obj->opp_amount;
486
                $this->opp_percent	= $obj->opp_percent;
487
                $this->budget_amount	= $obj->budget_amount;
488
                $this->modelpdf	= $obj->model_pdf;
489
                $this->bill_time = (int) $obj->bill_time;
490
491
                $this->db->free($resql);
492
493
                // Retreive all extrafield
494
                // fetch optionals attributes and labels
495
                $this->fetch_optionals();
496
497
                return 1;
498
            }
499
500
            $this->db->free($resql);
501
502
            return 0;
503
        }
504
        else
505
        {
506
            $this->error = $this->db->lasterror();
507
            return -1;
508
        }
509
    }
510
511
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
512
    /**
513
     * 	Return list of projects
514
     *
515
     * 	@param		int		$socid		To filter on a particular third party
516
     * 	@return		array				List of projects
517
     */
518
    function liste_array($socid='')
519
    {
520
        // phpcs:enable
521
        global $conf;
522
523
        $projects = array();
524
525
        $sql = "SELECT rowid, title";
526
        $sql.= " FROM " . MAIN_DB_PREFIX . "projet";
527
        $sql.= " WHERE entity = " . $conf->entity;
528
        if (! empty($socid)) $sql.= " AND fk_soc = " . $socid;
529
530
        $resql = $this->db->query($sql);
531
        if ($resql)
532
        {
533
            $nump = $this->db->num_rows($resql);
534
535
            if ($nump)
536
            {
537
                $i = 0;
538
                while ($i < $nump)
539
                {
540
                    $obj = $this->db->fetch_object($resql);
541
542
                    $projects[$obj->rowid] = $obj->title;
543
                    $i++;
544
                }
545
            }
546
            return $projects;
547
        }
548
        else
549
        {
550
            print $this->db->lasterror();
551
        }
552
    }
553
554
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
555
    /**
556
     * 	Return list of elements for type, linked to a project
557
     *
558
     * 	@param		string		$type			'propal','order','invoice','order_supplier','invoice_supplier',...
559
     * 	@param		string		$tablename		name of table associated of the type
560
     * 	@param		string		$datefieldname	name of date field for filter
561
     *  @param		int			$dates			Start date
562
     *  @param		int			$datee			End date
563
	 *	@param		string		$projectkey		Equivalent key  to fk_projet for actual type
564
     * 	@return		mixed						Array list of object ids linked to project, < 0 or string if error
565
     */
566
    function get_element_list($type, $tablename, $datefieldname='', $dates='', $datee='', $projectkey='fk_projet')
567
    {
568
        // phpcs:enable
569
        $elements = array();
570
571
        if ($this->id <= 0) return $elements;
572
573
        $ids = $this->id;
574
575
		if ($type == 'agenda')
576
        {
577
        	$sql = "SELECT id as rowid FROM " . MAIN_DB_PREFIX . "actioncomm WHERE fk_project IN (". $ids .") AND entity IN (".getEntity('agenda').")";
578
        }
579
        elseif ($type == 'expensereport')
580
		{
581
            $sql = "SELECT ed.rowid FROM " . MAIN_DB_PREFIX . "expensereport as e, " . MAIN_DB_PREFIX . "expensereport_det as ed WHERE e.rowid = ed.fk_expensereport AND e.entity IN (".getEntity('expensereport').") AND ed.fk_projet IN (". $ids .")";
582
		}
583
        elseif ($type == 'project_task')
584
		{
585
			$sql = "SELECT DISTINCT pt.rowid FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (". $ids .")";
586
		}
587
		elseif ($type == 'project_task_time')	// Case we want to duplicate line foreach user
588
		{
589
			$sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (". $ids .")";
590
		}
591
		elseif ($type == 'stock_mouvement')
592
		{
593
			$sql = 'SELECT ms.rowid, ms.fk_user_author as fk_user FROM ' . MAIN_DB_PREFIX . "stock_mouvement as ms, " . MAIN_DB_PREFIX . "entrepot as e WHERE e.rowid = ms.fk_entrepot AND e.entity IN (".getEntity('stock').") AND ms.origintype = 'project' AND ms.fk_origin IN (". $ids .") AND ms.type_mouvement = 1";
594
		}
595
        else
596
		{
597
            $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $tablename." WHERE ".$projectkey." IN (". $ids .") AND entity IN (".getEntity($type).")";
598
		}
599
600
		if ($dates > 0)
601
		{
602
			if (empty($datefieldname) && ! empty($this->table_element_date)) $datefieldname=$this->table_element_date;
0 ignored issues
show
Bug introduced by
The property table_element_date does not exist on Project. Did you mean table_element?
Loading history...
603
			if (empty($datefieldname)) return 'Error this object has no date field defined';
604
			$sql.=" AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)";
605
		}
606
    	if ($datee > 0)
607
		{
608
			if (empty($datefieldname) && ! empty($this->table_element_date)) $datefieldname=$this->table_element_date;
609
			if (empty($datefieldname)) return 'Error this object has no date field defined';
610
			$sql.=" AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)";
611
		}
612
		if (! $sql) return -1;
613
614
        //print $sql;
615
        dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG);
616
        $result = $this->db->query($sql);
617
        if ($result)
618
        {
619
            $nump = $this->db->num_rows($result);
620
            if ($nump)
621
            {
622
                $i = 0;
623
                while ($i < $nump)
624
                {
625
                    $obj = $this->db->fetch_object($result);
626
627
                    $elements[$i] = $obj->rowid.(empty($obj->fk_user)?'':'_'.$obj->fk_user);
628
629
                    $i++;
630
                }
631
                $this->db->free($result);
632
            }
633
634
            /* Return array even if empty*/
635
            return $elements;
636
        }
637
        else
638
        {
639
            dol_print_error($this->db);
640
        }
641
    }
642
643
    /**
644
     *    Delete a project from database
645
     *
646
     *    @param       User		$user            User
647
     *    @param       int		$notrigger       Disable triggers
648
     *    @return      int       			      <0 if KO, 0 if not possible, >0 if OK
649
     */
650
    function delete($user, $notrigger=0)
651
    {
652
        global $langs, $conf;
653
        require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
654
655
        $error = 0;
656
657
        $this->db->begin();
658
659
        if (!$error)
660
        {
661
            // Delete linked contacts
662
            $res = $this->delete_linked_contact();
663
            if ($res < 0)
664
            {
665
                $this->error = 'ErrorFailToDeleteLinkedContact';
666
                //$error++;
667
                $this->db->rollback();
668
                return 0;
669
            }
670
        }
671
672
        // Set fk_projet into elements to null
673
        $listoftables=array(
674
        		'propal'=>'fk_projet', 'commande'=>'fk_projet', 'facture'=>'fk_projet',
675
        		'supplier_proposal'=>'fk_projet', 'commande_fournisseur'=>'fk_projet', 'facture_fourn'=>'fk_projet',
676
        		'expensereport_det'=>'fk_projet', 'contrat'=>'fk_projet', 'fichinter'=>'fk_projet', 'don'=>'fk_projet',
677
        		'actioncomm'=>'fk_project'
678
        		);
679
        foreach($listoftables as $key => $value)
680
        {
681
   	        $sql = "UPDATE " . MAIN_DB_PREFIX . $key . " SET ".$value." = NULL where ".$value." = ". $this->id;
682
	        $resql = $this->db->query($sql);
683
	        if (!$resql)
684
	        {
685
	        	$this->errors[] = $this->db->lasterror();
686
	        	$error++;
687
	        	break;
688
	        }
689
        }
690
691
		// Fetch tasks
692
		$this->getLinesArray($user);
693
694
		// Delete tasks
695
		$ret = $this->deleteTasks($user);
696
		if ($ret < 0) $error++;
697
698
        // Delete project
699
        if (! $error)
700
        {
701
	        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet";
702
	        $sql.= " WHERE rowid=" . $this->id;
703
704
	        $resql = $this->db->query($sql);
705
	        if (!$resql)
706
	        {
707
	        	$this->errors[] = $langs->trans("CantRemoveProject");
708
	        	$error++;
709
	        }
710
        }
711
712
        if (! $error)
713
        {
714
	        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_extrafields";
715
	        $sql.= " WHERE fk_object=" . $this->id;
716
717
	        $resql = $this->db->query($sql);
718
	        if (! $resql)
719
	        {
720
	        	$this->errors[] = $this->db->lasterror();
721
	        	$error++;
722
	        }
723
        }
724
725
        if (empty($error))
726
        {
727
            // We remove directory
728
            $projectref = dol_sanitizeFileName($this->ref);
729
            if ($conf->projet->dir_output)
730
            {
731
                $dir = $conf->projet->dir_output . "/" . $projectref;
732
                if (file_exists($dir))
733
                {
734
                    $res = @dol_delete_dir_recursive($dir);
735
                    if (!$res)
736
                    {
737
                        $this->errors[] = 'ErrorFailToDeleteDir';
738
                        $error++;
739
                    }
740
                }
741
            }
742
743
            if (!$notrigger)
744
            {
745
                // Call trigger
746
                $result=$this->call_trigger('PROJECT_DELETE',$user);
747
748
                if ($result < 0) {
749
                    $error++;
750
                }
751
                // End call triggers
752
            }
753
        }
754
755
    	if (empty($error))
756
    	{
757
            $this->db->commit();
758
            return 1;
759
        }
760
        else
761
       {
762
        	foreach ( $this->errors as $errmsg )
763
        	{
764
				dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
765
				$this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
766
			}
767
            dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
768
            $this->db->rollback();
769
            return -1;
770
        }
771
    }
772
773
    /**
774
     * 		Delete tasks with no children first, then task with children recursively
775
     *
776
     *  	@param     	User		$user		User
777
     *		@return		int				<0 if KO, 1 if OK
778
     */
779
    function deleteTasks($user)
780
    {
781
        $countTasks = count($this->lines);
782
        $deleted = false;
783
        if ($countTasks)
784
        {
785
            foreach($this->lines as $task)
786
            {
787
                if ($task->hasChildren() <= 0) {		// If there is no children (or error to detect them)
788
                    $deleted = true;
789
                    $ret = $task->delete($user);
790
                    if ($ret <= 0)
791
                    {
792
                        $this->errors[] = $this->db->lasterror();
793
                        return -1;
794
                    }
795
                }
796
            }
797
        }
798
        $this->getLinesArray($user);
799
        if ($deleted && count($this->lines) < $countTasks)
800
        {
801
            if (count($this->lines)) $this->deleteTasks($this->lines);
802
        }
803
804
        return 1;
805
    }
806
807
    /**
808
     * 		Validate a project
809
     *
810
     * 		@param		User	$user		   User that validate
811
     *      @param      int     $notrigger     1=Disable triggers
812
     * 		@return		int					   <0 if KO, >0 if OK
813
     */
814
    function setValid($user, $notrigger=0)
815
    {
816
        global $langs, $conf;
817
818
		$error=0;
819
820
        if ($this->statut != 1)
821
        {
822
            // Check parameters
823
            if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title))
824
            {
825
                $this->error=$langs->trans("ErrorFieldFormat",$langs->transnoentities("Label")).'. '.$langs->trans('RemoveString',$langs->transnoentitiesnoconv("CopyOf"));
826
                return -1;
827
            }
828
829
            $this->db->begin();
830
831
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet";
832
            $sql.= " SET fk_statut = 1";
833
            $sql.= " WHERE rowid = " . $this->id;
834
            $sql.= " AND entity = " . $conf->entity;
835
836
            dol_syslog(get_class($this)."::setValid", LOG_DEBUG);
837
            $resql = $this->db->query($sql);
838
            if ($resql)
839
            {
840
                // Call trigger
841
                if (empty($notrigger))
842
                {
843
                    $result=$this->call_trigger('PROJECT_VALIDATE',$user);
844
                    if ($result < 0) { $error++; }
845
                    // End call triggers
846
                }
847
848
                if (!$error)
849
                {
850
                	$this->statut=1;
851
                	$this->db->commit();
852
                    return 1;
853
                }
854
                else
855
                {
856
                    $this->db->rollback();
857
                    $this->error = join(',', $this->errors);
858
                    dol_syslog(get_class($this)."::setValid " . $this->error, LOG_ERR);
859
                    return -1;
860
                }
861
            }
862
            else
863
            {
864
                $this->db->rollback();
865
                $this->error = $this->db->lasterror();
866
                return -1;
867
            }
868
        }
869
    }
870
871
    /**
872
     * 		Close a project
873
     *
874
     * 		@param		User	$user		User that close project
875
     * 		@return		int					<0 if KO, 0 if already closed, >0 if OK
876
     */
877
    function setClose($user)
878
    {
879
        global $langs, $conf;
880
881
        $now = dol_now();
882
883
		$error=0;
884
885
        if ($this->statut != 2)
886
        {
887
            $this->db->begin();
888
889
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet";
890
            $sql.= " SET fk_statut = 2, fk_user_close = ".$user->id.", date_close = '".$this->db->idate($now)."'";
891
            $sql.= " WHERE rowid = " . $this->id;
892
            $sql.= " AND entity = " . $conf->entity;
893
            $sql.= " AND fk_statut = 1";
894
895
            if (! empty($conf->global->PROJECT_USE_OPPORTUNITIES))
896
            {
897
            	// TODO What to do if fk_opp_status is not code 'WON' or 'LOST'
898
            }
899
900
            dol_syslog(get_class($this)."::setClose", LOG_DEBUG);
901
            $resql = $this->db->query($sql);
902
            if ($resql)
903
            {
904
                // Call trigger
905
                $result=$this->call_trigger('PROJECT_CLOSE',$user);
906
                if ($result < 0) { $error++; }
907
                // End call triggers
908
909
                if (!$error)
910
                {
911
                    $this->statut = 2;
912
                    $this->db->commit();
913
                    return 1;
914
                }
915
                else
916
                {
917
                    $this->db->rollback();
918
                    $this->error = join(',', $this->errors);
919
                    dol_syslog(get_class($this)."::setClose " . $this->error, LOG_ERR);
920
                    return -1;
921
                }
922
            }
923
            else
924
            {
925
                $this->db->rollback();
926
                $this->error = $this->db->lasterror();
927
                return -1;
928
            }
929
        }
930
931
        return 0;
932
    }
933
934
    /**
935
     *  Return status label of object
936
     *
937
     *  @param  int			$mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
938
     * 	@return string      			Label
939
     */
940
    function getLibStatut($mode=0)
941
    {
942
        return $this->LibStatut($this->statut, $mode);
943
    }
944
945
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
946
    /**
947
     *  Renvoi status label for a status
948
     *
949
     *  @param	int		$statut     id statut
950
     *  @param  int		$mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
951
     * 	@return string				Label
952
     */
953
    function LibStatut($statut, $mode=0)
954
    {
955
        // phpcs:enable
956
        global $langs;
957
958
        if ($mode == 0)
959
        {
960
            return $langs->trans($this->statuts_long[$statut]);
961
        }
962
        if ($mode == 1)
963
        {
964
            return $langs->trans($this->statuts_short[$statut]);
965
        }
966
        if ($mode == 2)
967
        {
968
            if ($statut == 0)
969
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0') . ' ' . $langs->trans($this->statuts_short[$statut]);
970
            if ($statut == 1)
971
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4') . ' ' . $langs->trans($this->statuts_short[$statut]);
972
            if ($statut == 2)
973
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6') . ' ' . $langs->trans($this->statuts_short[$statut]);
974
        }
975
        if ($mode == 3)
976
        {
977
            if ($statut == 0)
978
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0');
979
            if ($statut == 1)
980
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4');
981
            if ($statut == 2)
982
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6');
983
        }
984
        if ($mode == 4)
985
        {
986
            if ($statut == 0)
987
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0') . ' ' . $langs->trans($this->statuts_long[$statut]);
988
            if ($statut == 1)
989
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4') . ' ' . $langs->trans($this->statuts_long[$statut]);
990
            if ($statut == 2)
991
                return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6') . ' ' . $langs->trans($this->statuts_long[$statut]);
992
        }
993
        if ($mode == 5)
994
        {
995
            if ($statut == 0)
996
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut0');
997
            if ($statut == 1)
998
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut4');
999
            if ($statut == 2)
1000
                return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut6');
1001
        }
1002
    }
1003
1004
    /**
1005
     * 	Return clicable name (with picto eventually)
1006
     *
1007
     * 	@param	int		$withpicto		          0=No picto, 1=Include picto into link, 2=Only picto
1008
     * 	@param	string	$option			          Variant ('', 'nolink')
1009
     * 	@param	int		$addlabel		          0=Default, 1=Add label into string, >1=Add first chars into string
1010
     *  @param	string	$moreinpopup	          Text to add into popup
1011
     *  @param	string	$sep			          Separator between ref and label if option addlabel is set
1012
     *  @param	int   	$notooltip		          1=Disable tooltip
1013
     *  @param  int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1014
     * 	@return	string					          String with URL
1015
     */
1016
    function getNomUrl($withpicto=0, $option='', $addlabel=0, $moreinpopup='', $sep=' - ', $notooltip=0, $save_lastsearch_value=-1)
1017
    {
1018
        global $conf, $langs, $user, $hookmanager;
1019
1020
        if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
1021
1022
        $result = '';
1023
1024
        $label='';
1025
        if ($option != 'nolink') $label = '<u>' . $langs->trans("ShowProject") . '</u>';
1026
        $label .= ($label?'<br>':'').'<b>' . $langs->trans('Ref') . ': </b>' . $this->ref;	// The space must be after the : to not being explode when showing the title in img_picto
1027
        $label .= ($label?'<br>':'').'<b>' . $langs->trans('Label') . ': </b>' . $this->title;	// The space must be after the : to not being explode when showing the title in img_picto
1028
        if (! empty($this->thirdparty_name))
1029
            $label .= ($label?'<br>':'').'<b>' . $langs->trans('ThirdParty') . ': </b>' . $this->thirdparty_name;	// The space must be after the : to not being explode when showing the title in img_picto
1030
        if (! empty($this->dateo))
0 ignored issues
show
Bug introduced by
The property dateo does not exist on Project. Did you mean datem?
Loading history...
1031
            $label .= ($label?'<br>':'').'<b>' . $langs->trans('DateStart') . ': </b>' . dol_print_date($this->dateo, 'day');	// The space must be after the : to not being explode when showing the title in img_picto
1032
        if (! empty($this->datee))
1033
            $label .= ($label?'<br>':'').'<b>' . $langs->trans('DateEnd') . ': </b>' . dol_print_date($this->datee, 'day');	// The space must be after the : to not being explode when showing the title in img_picto
1034
        if ($moreinpopup) $label.='<br>'.$moreinpopup;
1035
1036
        $url='';
1037
        if ($option != 'nolink')
1038
        {
1039
            if (preg_match('/\.php$/',$option)) {
1040
                $url = dol_buildpath($option,1) . '?id=' . $this->id;
1041
            }
1042
            else if ($option == 'task')
1043
            {
1044
                $url = DOL_URL_ROOT . '/projet/tasks.php?id=' . $this->id;
1045
            }
1046
            else
1047
            {
1048
                $url = DOL_URL_ROOT . '/projet/card.php?id=' . $this->id;
1049
            }
1050
            // Add param to save lastsearch_values or not
1051
            $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1052
            if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1053
            if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1054
        }
1055
1056
        $linkclose='';
1057
        if (empty($notooltip) && $user->rights->projet->lire)
1058
        {
1059
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1060
            {
1061
                $label=$langs->trans("ShowProject");
1062
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1063
            }
1064
            $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"';
1065
            $linkclose.=' class="classfortooltip"';
1066
1067
			/*
1068
			$hookmanager->initHooks(array('projectdao'));
1069
			$parameters=array('id'=>$this->id);
1070
			// Note that $action and $object may have been modified by some hooks
1071
			$reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action);
1072
			if ($reshook > 0)
1073
				$linkclose = $hookmanager->resPrint;
1074
			*/
1075
		}
1076
1077
        $picto = 'projectpub';
1078
        if (! $this->public) $picto = 'project';
1079
1080
        $linkstart = '<a href="'.$url.'"';
1081
        $linkstart.=$linkclose.'>';
1082
        $linkend='</a>';
1083
1084
        $result .= $linkstart;
1085
        if ($withpicto) $result.=img_object(($notooltip?'':$label), $picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1086
        if ($withpicto != 2) $result.= $this->ref;
1087
        $result .= $linkend;
1088
        if ($withpicto != 2) $result.=(($addlabel && $this->title) ? $sep . dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : '');
1089
1090
        global $action;
1091
        $hookmanager->initHooks(array('projectdao'));
1092
        $parameters=array('id'=>$this->id, 'getnomurl'=>$result);
1093
        $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
1094
        if ($reshook > 0) $result = $hookmanager->resPrint;
1095
        else $result .= $hookmanager->resPrint;
1096
1097
        return $result;
1098
    }
1099
1100
    /**
1101
     *  Initialise an instance with random values.
1102
     *  Used to build previews or test instances.
1103
     * 	id must be 0 if object instance is a specimen.
1104
     *
1105
     *  @return	void
1106
     */
1107
    function initAsSpecimen()
1108
    {
1109
        global $user, $langs, $conf;
1110
1111
        $now=dol_now();
1112
1113
        // Initialise parameters
1114
        $this->id = 0;
1115
        $this->ref = 'SPECIMEN';
1116
        $this->specimen = 1;
1117
        $this->socid = 1;
1118
        $this->date_c = $now;
1119
        $this->date_m = $now;
1120
        $this->date_start = $now;
1121
        $this->date_end = $now + (3600 * 24 * 365);
1122
        $this->note_public = 'SPECIMEN';
1123
		$this->fk_ele = 20000;
1124
        $this->opp_amount = 20000;
1125
        $this->budget_amount = 10000;
1126
1127
        /*
1128
        $nbp = mt_rand(1, 9);
1129
        $xnbp = 0;
1130
        while ($xnbp < $nbp)
1131
        {
1132
            $line = new Task($this->db);
1133
            $line->fk_project = 0;
1134
            $line->label = $langs->trans("Label") . " " . $xnbp;
1135
            $line->description = $langs->trans("Description") . " " . $xnbp;
1136
1137
            $this->lines[]=$line;
1138
            $xnbp++;
1139
        }
1140
        */
1141
    }
1142
1143
    /**
1144
     * 	Check if user has permission on current project
1145
     *
1146
     * 	@param	User	$user		Object user to evaluate
1147
     * 	@param  string	$mode		Type of permission we want to know: 'read', 'write'
1148
     * 	@return	int					>0 if user has permission, <0 if user has no permission
1149
     */
1150
    function restrictedProjectArea($user, $mode='read')
1151
    {
1152
        // To verify role of users
1153
        $userAccess = 0;
1154
        if (($mode == 'read' && ! empty($user->rights->projet->all->lire)) || ($mode == 'write' && ! empty($user->rights->projet->all->creer)) || ($mode == 'delete' && ! empty($user->rights->projet->all->supprimer)))
1155
        {
1156
            $userAccess = 1;
1157
        }
1158
        else if ($this->public && (($mode == 'read' && ! empty($user->rights->projet->lire)) || ($mode == 'write' && ! empty($user->rights->projet->creer)) || ($mode == 'delete' && ! empty($user->rights->projet->supprimer))))
1159
        {
1160
            $userAccess = 1;
1161
        }
1162
        else
1163
		{
1164
            foreach (array('internal', 'external') as $source)
1165
            {
1166
                $userRole = $this->liste_contact(4, $source);
1167
                $num = count($userRole);
1168
1169
                $nblinks = 0;
1170
                while ($nblinks < $num)
1171
                {
1172
                    if ($source == 'internal' && preg_match('/^PROJECT/', $userRole[$nblinks]['code']) && $user->id == $userRole[$nblinks]['id'])
1173
                    {
1174
                        if ($mode == 'read'   && $user->rights->projet->lire)      $userAccess++;
1175
                        if ($mode == 'write'  && $user->rights->projet->creer)     $userAccess++;
1176
                        if ($mode == 'delete' && $user->rights->projet->supprimer) $userAccess++;
1177
                    }
1178
                    $nblinks++;
1179
                }
1180
            }
1181
            //if (empty($nblinks))	// If nobody has permission, we grant creator
1182
            //{
1183
            //	if ((!empty($this->user_author_id) && $this->user_author_id == $user->id))
1184
            //	{
1185
            //		$userAccess = 1;
1186
            //	}
1187
            //}
1188
        }
1189
1190
        return ($userAccess?$userAccess:-1);
1191
    }
1192
1193
    /**
1194
     * Return array of projects a user has permission on, is affected to, or all projects
1195
     *
1196
     * @param 	User	$user			User object
1197
     * @param 	int		$mode			0=All project I have permission on (assigned to me and public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts
1198
     * @param 	int		$list			0=Return array, 1=Return string list
1199
     * @param	int		$socid			0=No filter on third party, id of third party
1200
     * @param	string	$filter			additionnal filter on project (statut, ref, ...)
1201
     * @return 	array or string			Array of projects id, or string with projects id separated with "," if list is 1
1202
     */
1203
    function getProjectsAuthorizedForUser($user, $mode=0, $list=0, $socid=0, $filter='')
1204
    {
1205
        $projects = array();
1206
        $temp = array();
1207
1208
        $sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref";
1209
        $sql.= " FROM " . MAIN_DB_PREFIX . "projet as p";
1210
        if ($mode == 0 || $mode == 1)
1211
        {
1212
            $sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec";
1213
        }
1214
        $sql.= " WHERE p.entity IN (".getEntity('project').")";
1215
        // Internal users must see project he is contact to even if project linked to a third party he can't see.
1216
        //if ($socid || ! $user->rights->societe->client->voir)	$sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1217
        if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")";
1218
1219
        // Get id of types of contacts for projects (This list never contains a lot of elements)
1220
        $listofprojectcontacttype=array();
1221
        $sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
1222
        $sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'";
1223
        $sql2.= " AND ctc.source = 'internal'";
1224
        $resql = $this->db->query($sql2);
1225
        if ($resql)
1226
        {
1227
            while($obj = $this->db->fetch_object($resql))
1228
            {
1229
                $listofprojectcontacttype[$obj->rowid]=$obj->code;
1230
            }
1231
        }
1232
        else dol_print_error($this->db);
1233
        if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0';    // To avoid syntax error if not found
1234
1235
        if ($mode == 0)
1236
        {
1237
            $sql.= " AND ec.element_id = p.rowid";
1238
            $sql.= " AND ( p.public = 1";
1239
            $sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")";
1240
            $sql.= " AND ec.fk_socpeople = ".$user->id.")";
1241
            $sql.= " )";
1242
        }
1243
        if ($mode == 1)
1244
        {
1245
            $sql.= " AND ec.element_id = p.rowid";
1246
            $sql.= " AND (";
1247
            $sql.= "  ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")";
1248
            $sql.= " AND ec.fk_socpeople = ".$user->id.")";
1249
            $sql.= " )";
1250
        }
1251
        if ($mode == 2)
1252
        {
1253
            // No filter. Use this if user has permission to see all project
1254
        }
1255
1256
	$sql.= $filter;
1257
        //print $sql;
1258
1259
        $resql = $this->db->query($sql);
1260
        if ($resql)
1261
        {
1262
            $num = $this->db->num_rows($resql);
1263
            $i = 0;
1264
            while ($i < $num)
1265
            {
1266
                $row = $this->db->fetch_row($resql);
1267
                $projects[$row[0]] = $row[1];
1268
                $temp[] = $row[0];
1269
                $i++;
1270
            }
1271
1272
            $this->db->free($resql);
1273
1274
            if ($list)
1275
            {
1276
                if (empty($temp)) return '0';
1277
                $result = implode(',', $temp);
1278
                return $result;
1279
            }
1280
        }
1281
        else
1282
        {
1283
            dol_print_error($this->db);
1284
        }
1285
1286
        return $projects;
1287
    }
1288
1289
     /**
1290
      * Load an object from its id and create a new one in database
1291
	  *
1292
	  *	@param	int		$fromid     	Id of object to clone
1293
	  *	@param	bool	$clone_contact	Clone contact of project
1294
	  *	@param	bool	$clone_task		Clone task of project
1295
	  *	@param	bool	$clone_project_file		Clone file of project
1296
	  *	@param	bool	$clone_task_file		Clone file of task (if task are copied)
1297
      *	@param	bool	$clone_note		Clone note of project
1298
      * @param	bool	$move_date		Move task date on clone
1299
      *	@param	integer	$notrigger		No trigger flag
1300
      * @param  int     $newthirdpartyid  New thirdparty id
1301
	  * @return	int						New id of clone
1302
	  */
1303
	function createFromClone($fromid,$clone_contact=false,$clone_task=true,$clone_project_file=false,$clone_task_file=false,$clone_note=true,$move_date=true,$notrigger=0,$newthirdpartyid=0)
1304
	{
1305
		global $user,$langs,$conf;
1306
1307
		$error=0;
1308
1309
		dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date,LOG_DEBUG);
1310
1311
		$now = dol_mktime(0,0,0,idate('m',dol_now()),idate('d',dol_now()),idate('Y',dol_now()));
1312
1313
		$clone_project=new Project($this->db);
1314
1315
		$clone_project->context['createfromclone']='createfromclone';
1316
1317
		$this->db->begin();
1318
1319
		// Load source object
1320
		$clone_project->fetch($fromid);
1321
		$clone_project->fetch_optionals();
1322
		if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid;
1323
		$clone_project->fetch_thirdparty();
1324
1325
		$orign_dt_start=$clone_project->date_start;
1326
		$orign_project_ref=$clone_project->ref;
1327
1328
		$clone_project->id=0;
1329
		if ($move_date) {
1330
	        $clone_project->date_start = $now;
1331
	        if (!(empty($clone_project->date_end)))
1332
	        {
1333
	        	$clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start);
1334
	        }
1335
		}
1336
1337
        $clone_project->datec = $now;
0 ignored issues
show
Documentation Bug introduced by
It seems like $now can also be of type string. However, the property $datec is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1338
1339
        if (! $clone_note)
1340
        {
1341
        	    $clone_project->note_private='';
1342
    			$clone_project->note_public='';
1343
        }
1344
1345
		//Generate next ref
1346
		$defaultref='';
1347
    	$obj = empty($conf->global->PROJECT_ADDON)?'mod_project_simple':$conf->global->PROJECT_ADDON;
1348
    	// Search template files
1349
    	$file=''; $classname=''; $filefound=0;
1350
    	$dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']);
1351
    	foreach($dirmodels as $reldir)
1352
    	{
1353
    	    $file=dol_buildpath($reldir."core/modules/project/".$obj.'.php',0);
1354
    	    if (file_exists($file))
1355
    	    {
1356
    	        $filefound=1;
1357
    	        dol_include_once($reldir."core/modules/project/".$obj.'.php');
1358
            	$modProject = new $obj;
1359
            	$defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty)?$clone_project->thirdparty:null, $clone_project);
1360
            	break;
1361
    	    }
1362
    	}
1363
    	if (is_numeric($defaultref) && $defaultref <= 0) $defaultref='';
1364
1365
		$clone_project->ref=$defaultref;
1366
		$clone_project->title=$langs->trans("CopyOf").' '.$clone_project->title;
1367
1368
		// Create clone
1369
		$result=$clone_project->create($user,$notrigger);
1370
1371
		// Other options
1372
		if ($result < 0)
1373
		{
1374
			$this->error.=$clone_project->error;
1375
			$error++;
1376
		}
1377
1378
		if (! $error)
1379
		{
1380
			//Get the new project id
1381
			$clone_project_id=$clone_project->id;
1382
1383
			//Note Update
1384
			if (!$clone_note)
1385
       		{
1386
        	    $clone_project->note_private='';
1387
    			$clone_project->note_public='';
1388
        	}
1389
        	else
1390
        	{
1391
        		$this->db->begin();
1392
				$res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES),'_public');
1393
				if ($res < 0)
1394
				{
1395
					$this->error.=$clone_project->error;
1396
					$error++;
1397
					$this->db->rollback();
1398
				}
1399
				else
1400
				{
1401
					$this->db->commit();
1402
				}
1403
1404
				$this->db->begin();
1405
				$res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private');
1406
				if ($res < 0)
1407
				{
1408
					$this->error.=$clone_project->error;
1409
					$error++;
1410
					$this->db->rollback();
1411
				}
1412
				else
1413
				{
1414
					$this->db->commit();
1415
				}
1416
        	}
1417
1418
			//Duplicate contact
1419
			if ($clone_contact)
1420
			{
1421
				$origin_project = new Project($this->db);
1422
				$origin_project->fetch($fromid);
1423
1424
				foreach(array('internal','external') as $source)
1425
				{
1426
					$tab = $origin_project->liste_contact(-1,$source);
1427
1428
					foreach ($tab as $contacttoadd)
0 ignored issues
show
Bug introduced by
The expression $tab of type integer is not traversable.
Loading history...
1429
					{
1430
						$clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'],$notrigger);
1431
						if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS')
1432
						{
1433
							$langs->load("errors");
1434
							$this->error.=$langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1435
							$error++;
1436
						}
1437
						else
1438
						{
1439
							if ($clone_project->error!='')
1440
							{
1441
								$this->error.=$clone_project->error;
1442
								$error++;
1443
							}
1444
						}
1445
					}
1446
				}
1447
			}
1448
1449
			//Duplicate file
1450
			if ($clone_project_file)
1451
			{
1452
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1453
1454
				$clone_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($defaultref);
1455
				$ori_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($orign_project_ref);
1456
1457
				if (dol_mkdir($clone_project_dir) >= 0)
1458
				{
1459
					$filearray=dol_dir_list($ori_project_dir,"files",0,'','(\.meta|_preview.*\.png)$','',SORT_ASC,1);
1460
					foreach($filearray as $key => $file)
1461
					{
1462
						$rescopy = dol_copy($ori_project_dir . '/' . $file['name'], $clone_project_dir . '/' . $file['name'],0,1);
1463
						if (is_numeric($rescopy) && $rescopy < 0)
1464
						{
1465
							$this->error.=$langs->trans("ErrorFailToCopyFile",$ori_project_dir . '/' . $file['name'],$clone_project_dir . '/' . $file['name']);
1466
							$error++;
1467
						}
1468
					}
1469
				}
1470
				else
1471
				{
1472
					$this->error.=$langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1473
					$error++;
1474
				}
1475
			}
1476
1477
			//Duplicate task
1478
			if ($clone_task)
1479
			{
1480
				require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php';
1481
1482
				$taskstatic = new Task($this->db);
1483
1484
				// Security check
1485
				$socid=0;
1486
				if ($user->societe_id > 0) $socid = $user->societe_id;
1487
1488
				$tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0);
1489
1490
				$tab_conv_child_parent=array();
1491
1492
				// Loop on each task, to clone it
1493
			    foreach ($tasksarray as $tasktoclone)
1494
			    {
1495
					$result_clone = $taskstatic->createFromClone($tasktoclone->id,$clone_project_id,$tasktoclone->fk_parent,$move_date,true,false,$clone_task_file,true,false);
1496
					if ($result_clone <= 0)
1497
				    {
1498
				    	$this->error.=$result_clone->error;
0 ignored issues
show
Bug introduced by
The property error does not exist on integer.
Loading history...
1499
						$error++;
1500
				    }
1501
				    else
1502
				    {
1503
				    	$new_task_id=$result_clone;
1504
				    	$taskstatic->fetch($tasktoclone->id);
1505
1506
				    	//manage new parent clone task id
1507
				    	// if the current task has child we store the original task id and the equivalent clone task id
1508
						if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id,$tab_conv_child_parent))
1509
						{
1510
							$tab_conv_child_parent[$tasktoclone->id] =  $new_task_id;
1511
						}
1512
				    }
1513
			    }
1514
1515
			    //Parse all clone node to be sure to update new parent
1516
			    $tasksarray=$taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0);
1517
			    foreach ($tasksarray as $task_cloned)
1518
			    {
1519
			    	$taskstatic->fetch($task_cloned->id);
1520
			    	if ($taskstatic->fk_task_parent!=0)
1521
			    	{
1522
			    		$taskstatic->fk_task_parent=$tab_conv_child_parent[$taskstatic->fk_task_parent];
1523
			    	}
1524
			    	$res=$taskstatic->update($user,$notrigger);
1525
			    	if ($result_clone <= 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result_clone seems to be defined by a foreach iteration on line 1493. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1526
				    {
1527
				    	$this->error.=$taskstatic->error;
1528
						$error++;
1529
				    }
1530
			    }
1531
			}
1532
		}
1533
1534
		unset($clone_project->context['createfromclone']);
1535
1536
		if (! $error)
1537
		{
1538
			$this->db->commit();
1539
			return $clone_project_id;
1540
		}
1541
		else
1542
		{
1543
			$this->db->rollback();
1544
			dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : " . $this->error, LOG_ERR);
1545
			return -1;
1546
		}
1547
	}
1548
1549
1550
	 /**
1551
	  *    Shift project task date from current date to delta
1552
	  *
1553
	  *    @param	timestamp		$old_project_dt_start	old project start date
1554
	  *    @return	int				1 if OK or < 0 if KO
1555
	  */
1556
	function shiftTaskDate($old_project_dt_start)
1557
	{
1558
		global $user,$langs,$conf;
1559
1560
		$error=0;
1561
1562
		$taskstatic = new Task($this->db);
1563
1564
		// Security check
1565
		$socid=0;
1566
		if ($user->societe_id > 0) $socid = $user->societe_id;
1567
1568
		$tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1569
1570
	    foreach ($tasksarray as $tasktoshiftdate)
1571
	    {
1572
	    	$to_update=false;
1573
	    	// Fetch only if update of date will be made
1574
	    	if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end)))
1575
	    	{
1576
	    		//dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1577
	    		$to_update=true;
1578
		    	$task = new Task($this->db);
1579
		    	$result = $task->fetch($tasktoshiftdate->id);
1580
		    	if (!$result)
1581
		    	{
1582
		    		$error++;
1583
		    		$this->error.=$task->error;
1584
		    	}
1585
	    	}
1586
			//print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1587
1588
	    	//Calcultate new task start date with difference between old proj start date and origin task start date
1589
	    	if (!empty($tasktoshiftdate->date_start))
1590
	    	{
1591
				$task->date_start			= $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
1592
	    	}
1593
1594
	    	//Calcultate new task end date with difference between origin proj end date and origin task end date
1595
	    	if (!empty($tasktoshiftdate->date_end))
1596
	    	{
1597
				$task->date_end		    	= $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1598
	    	}
1599
1600
			if ($to_update)
1601
			{
1602
		    	$result = $task->update($user);
1603
		    	if (!$result)
1604
		    	{
1605
		    		$error++;
1606
		    		$this->error.=$task->error;
1607
		    	}
1608
			}
1609
	    }
1610
	    if ($error!=0)
1611
	    {
1612
	    	return -1;
1613
	    }
1614
	    return $result;
1615
	}
1616
1617
1618
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1619
	/**
1620
	 *    Associate element to a project
1621
	 *
1622
	 *    @param	string	$tableName			Table of the element to update
1623
	 *    @param	int		$elementSelectId	Key-rowid of the line of the element to update
1624
	 *    @return	int							1 if OK or < 0 if KO
1625
     */
1626
	function update_element($tableName, $elementSelectId)
1627
	{
1628
        // phpcs:enable
1629
		$sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1630
1631
		if ($tableName == "actioncomm")
1632
		{
1633
			$sql.= " SET fk_project=".$this->id;
1634
			$sql.= " WHERE id=".$elementSelectId;
1635
		}
1636
		else
1637
		{
1638
			$sql.= " SET fk_projet=".$this->id;
1639
			$sql.= " WHERE rowid=".$elementSelectId;
1640
		}
1641
1642
		dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
1643
		$resql=$this->db->query($sql);
1644
		if (!$resql) {
1645
			$this->error=$this->db->lasterror();
1646
			return -1;
1647
		}else {
1648
			return 1;
1649
		}
1650
	}
1651
1652
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1653
	/**
1654
	 *    Associate element to a project
1655
	 *
1656
	 *    @param	string	$tableName			Table of the element to update
1657
	 *    @param	int		$elementSelectId	Key-rowid of the line of the element to update
1658
	 *    @return	int							1 if OK or < 0 if KO
1659
	 */
1660
	function remove_element($tableName, $elementSelectId)
1661
	{
1662
        // phpcs:enable
1663
		$sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1664
1665
		if ($TableName=="actioncomm")
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $TableName does not exist. Did you maybe mean $tableName?
Loading history...
1666
		{
1667
			$sql.= " SET fk_project=NULL";
1668
			$sql.= " WHERE id=".$elementSelectId;
1669
		}
1670
		else
1671
		{
1672
			$sql.= " SET fk_projet=NULL";
1673
			$sql.= " WHERE rowid=".$elementSelectId;
1674
		}
1675
1676
		dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
1677
		$resql=$this->db->query($sql);
1678
		if (!$resql) {
1679
			$this->error=$this->db->lasterror();
1680
			return -1;
1681
		}else {
1682
			return 1;
1683
		}
1684
	}
1685
1686
	/**
1687
	 *  Create an intervention document on disk using template defined into PROJECT_ADDON_PDF
1688
	 *
1689
	 *  @param	string		$modele			Force template to use ('' by default)
1690
	 *  @param	Translate	$outputlangs	Objet lang to use for translation
1691
	 *  @param  int			$hidedetails    Hide details of lines
1692
	 *  @param  int			$hidedesc       Hide description
1693
	 *  @param  int			$hideref        Hide ref
1694
	 *  @return int         				0 if KO, 1 if OK
1695
	 */
1696
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
1697
	{
1698
		global $conf,$langs;
1699
1700
		$langs->load("projects");
1701
1702
		if (! dol_strlen($modele)) {
1703
1704
			$modele = 'baleine';
1705
1706
			if ($this->modelpdf) {
1707
				$modele = $this->modelpdf;
1708
			} elseif (! empty($conf->global->PROJECT_ADDON_PDF)) {
1709
				$modele = $conf->global->PROJECT_ADDON_PDF;
1710
			}
1711
		}
1712
1713
		$modelpath = "core/modules/project/doc/";
1714
1715
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1716
	}
1717
1718
1719
	/**
1720
	 * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project.
1721
	 * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call.
1722
	 *
1723
	 * @param 	int		$datestart		First day of week (use dol_get_first_day to find this date)
1724
	 * @param 	int		$taskid			Filter on a task id
1725
	 * @param 	int		$userid			Time spent by a particular user
1726
	 * @return 	int						<0 if OK, >0 if KO
1727
	 */
1728
	public function loadTimeSpent($datestart, $taskid=0, $userid=0)
1729
    {
1730
        $error=0;
1731
1732
        $this->weekWorkLoad=array();
1733
        $this->weekWorkLoadPerTask=array();
1734
1735
        if (empty($datestart)) dol_print_error('','Error datestart parameter is empty');
1736
1737
        $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task";
1738
        $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
1739
        $sql.= " WHERE ptt.fk_task = pt.rowid";
1740
        $sql.= " AND pt.fk_projet = ".$this->id;
1741
        $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' ";
1742
        $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
1743
        if ($task_id) $sql.= " AND ptt.fk_task=".$taskid;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $task_id does not exist. Did you maybe mean $taskid?
Loading history...
1744
        if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid;
1745
1746
        //print $sql;
1747
        $resql=$this->db->query($sql);
1748
        if ($resql)
1749
        {
1750
				$daylareadyfound=array();
1751
1752
                $num = $this->db->num_rows($resql);
1753
                $i = 0;
1754
                // Loop on each record found, so each couple (project id, task id)
1755
                while ($i < $num)
1756
                {
1757
                        $obj=$this->db->fetch_object($resql);
1758
                        $day=$this->db->jdate($obj->task_date);		// task_date is date without hours
1759
                        if (empty($daylareadyfound[$day]))
1760
                        {
1761
                        	$this->weekWorkLoad[$day] = $obj->task_duration;
1762
                        	$this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration;
1763
                        }
1764
                        else
1765
                        {
1766
                        	$this->weekWorkLoad[$day] += $obj->task_duration;
1767
                        	$this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration;
1768
                        }
1769
                        $daylareadyfound[$day]=1;
1770
                        $i++;
1771
                }
1772
                $this->db->free($resql);
1773
                return 1;
1774
         }
1775
        else
1776
        {
1777
                $this->error="Error ".$this->db->lasterror();
1778
                dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
1779
                return -1;
1780
        }
1781
    }
1782
1783
1784
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1785
    /**
1786
     * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
1787
     *
1788
     * @param	User	$user   Objet user
1789
     * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
1790
     */
1791
    function load_board($user)
1792
    {
1793
        // phpcs:enable
1794
        global $conf, $langs;
1795
1796
        // For external user, no check is done on company because readability is managed by public status of project and assignement.
1797
        //$socid=$user->societe_id;
1798
1799
        if (! $user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user,0,1,$socid);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $socid seems to be never defined.
Loading history...
1800
1801
        $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
1802
        $sql.= " FROM (".MAIN_DB_PREFIX."projet as p";
1803
        $sql.= ")";
1804
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
1805
        // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1806
        //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
1807
        $sql.= " WHERE p.fk_statut = 1";
1808
        $sql.= " AND p.entity IN (".getEntity('project').')';
1809
        if (! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
1810
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
1811
        //if ($socid || ! $user->rights->societe->client->voir)	$sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1812
        // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1813
        //if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))";
1814
1815
        //print $sql;
1816
        $resql=$this->db->query($sql);
1817
        if ($resql)
1818
        {
1819
            $project_static = new Project($this->db);
1820
1821
            $response = new WorkboardResponse();
1822
            $response->warning_delay = $conf->projet->warning_delay/60/60/24;
1823
            $response->label = $langs->trans("OpenedProjects");
1824
            if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
1825
            else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
1826
            $response->img = img_object('',"projectpub");
1827
1828
            // This assignment in condition is not a bug. It allows walking the results.
1829
            while ($obj=$this->db->fetch_object($resql))
1830
            {
1831
                $response->nbtodo++;
1832
1833
                $project_static->statut = $obj->status;
1834
                $project_static->opp_status = $obj->opp_status;
1835
                $project_static->datee = $this->db->jdate($obj->datee);
1836
1837
                if ($project_static->hasDelay()) {
1838
                    $response->nbtodolate++;
1839
                }
1840
            }
1841
1842
            return $response;
1843
        }
1844
        else
1845
        {
1846
            $this->error=$this->db->error();
1847
            return -1;
1848
        }
1849
    }
1850
1851
1852
	/**
1853
	 * Function used to replace a thirdparty id with another one.
1854
	 *
1855
	 * @param DoliDB $db Database handler
1856
	 * @param int $origin_id Old thirdparty id
1857
	 * @param int $dest_id New thirdparty id
1858
	 * @return bool
1859
	 */
1860
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1861
	{
1862
		$tables = array(
1863
			'projet'
1864
		);
1865
1866
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1867
	}
1868
1869
1870
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1871
	/**
1872
	 *      Charge indicateurs this->nb pour le tableau de bord
1873
	 *
1874
	 *      @return     int         <0 if KO, >0 if OK
1875
	 */
1876
	function load_state_board()
1877
	{
1878
        // phpcs:enable
1879
	    global $user;
1880
1881
	    $this->nb=array();
1882
1883
	    $sql = "SELECT count(p.rowid) as nb";
1884
	    $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
1885
	    $sql.= " WHERE";
1886
	    $sql.= " p.entity IN (".getEntity('project').")";
1887
		if (! $user->rights->projet->all->lire)
1888
		{
1889
			$projectsListId = $this->getProjectsAuthorizedForUser($user,0,1);
1890
			$sql .= "AND p.rowid IN (".$projectsListId.")";
1891
		}
1892
1893
	    $resql=$this->db->query($sql);
1894
	    if ($resql)
1895
	    {
1896
	        while ($obj=$this->db->fetch_object($resql))
1897
	        {
1898
	            $this->nb["projects"]=$obj->nb;
1899
	        }
1900
	        $this->db->free($resql);
1901
	        return 1;
1902
	    }
1903
	    else
1904
	    {
1905
	        dol_print_error($this->db);
1906
	        $this->error=$this->db->error();
1907
	        return -1;
1908
	    }
1909
	}
1910
1911
1912
	/**
1913
	 * Is the project delayed?
1914
	 *
1915
	 * @return bool
1916
	 */
1917
	public function hasDelay()
1918
	{
1919
	    global $conf;
1920
1921
        if (! ($this->statut == 1)) return false;
1922
        if (! $this->datee && ! $this->date_end) return false;
1923
1924
        $now = dol_now();
1925
1926
        return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay);
1927
	}
1928
1929
1930
	/**
1931
	 *	Charge les informations d'ordre info dans l'objet commande
1932
	 *
1933
	 *	@param  int		$id       Id of order
1934
	 *	@return	void
1935
	 */
1936
	function info($id)
1937
	{
1938
	    $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
1939
	    $sql.= ' date_close as datecloture,';
1940
	    $sql.= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture';
1941
	    $sql.= ' FROM '.MAIN_DB_PREFIX.'projet as c';
1942
	    $sql.= ' WHERE c.rowid = '.$id;
1943
	    $result=$this->db->query($sql);
1944
	    if ($result)
1945
	    {
1946
	        if ($this->db->num_rows($result))
1947
	        {
1948
	            $obj = $this->db->fetch_object($result);
1949
	            $this->id = $obj->rowid;
1950
	            if ($obj->fk_user_author)
1951
	            {
1952
	                $cuser = new User($this->db);
1953
	                $cuser->fetch($obj->fk_user_author);
1954
	                $this->user_creation   = $cuser;
1955
	            }
1956
1957
	            if ($obj->fk_user_cloture)
1958
	            {
1959
	                $cluser = new User($this->db);
1960
	                $cluser->fetch($obj->fk_user_cloture);
1961
	                $this->user_cloture   = $cluser;
1962
	            }
1963
1964
	            $this->date_creation     = $this->db->jdate($obj->datec);
1965
	            $this->date_modification = $this->db->jdate($obj->datem);
1966
	            $this->date_cloture      = $this->db->jdate($obj->datecloture);
1967
	        }
1968
1969
	        $this->db->free($result);
1970
	    }
1971
	    else
1972
	    {
1973
	        dol_print_error($this->db);
1974
	    }
1975
	}
1976
1977
	/**
1978
	 * Sets object to supplied categories.
1979
	 *
1980
	 * Deletes object from existing categories not supplied.
1981
	 * Adds it to non existing supplied categories.
1982
	 * Existing categories are left untouch.
1983
	 *
1984
	 * @param int[]|int $categories Category or categories IDs
1985
     * @return void
1986
	 */
1987
	public function setCategories($categories)
1988
	{
1989
		// Decode type
1990
		$type_id = Categorie::TYPE_PROJECT;
1991
		$type_text = 'project';
1992
1993
1994
		// Handle single category
1995
		if (!is_array($categories)) {
1996
			$categories = array($categories);
1997
		}
1998
1999
		// Get current categories
2000
		require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2001
		$c = new Categorie($this->db);
2002
		$existing = $c->containing($this->id, $type_id, 'id');
2003
2004
		// Diff
2005
		if (is_array($existing)) {
2006
			$to_del = array_diff($existing, $categories);
2007
			$to_add = array_diff($categories, $existing);
2008
		} else {
2009
			$to_del = array(); // Nothing to delete
2010
			$to_add = $categories;
2011
		}
2012
2013
		// Process
2014
		foreach ($to_del as $del) {
2015
			if ($c->fetch($del) > 0) {
2016
				$result=$c->del_type($this, $type_text);
2017
				if ($result<0) {
2018
					$this->errors=$c->errors;
2019
					$this->error=$c->error;
2020
					return -1;
2021
				}
2022
			}
2023
		}
2024
		foreach ($to_add as $add) {
2025
			if ($c->fetch($add) > 0) {
2026
				$result=$c->add_type($this, $type_text);
2027
				if ($result<0) {
2028
					$this->errors=$c->errors;
2029
					$this->error=$c->error;
2030
					return -1;
2031
				}
2032
			}
2033
		}
2034
2035
		return 1;
2036
	}
2037
2038
2039
	/**
2040
	 * 	Create an array of tasks of current project
2041
	 *
2042
	 *  @param  User   $user       Object user we want project allowed to
2043
	 * 	@return int		           >0 if OK, <0 if KO
2044
	 */
2045
	function getLinesArray($user)
2046
	{
2047
	    require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
2048
	    $taskstatic = new Task($this->db);
2049
2050
	    $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0);
2051
	}
2052
}
2053