Completed
Branch develop (a896fb)
by
unknown
40:50
created

Project::load_state_board()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

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

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1480
				    {
1481
				    	$this->error.=$taskstatic->error;
1482
						$error++;
1483
				    }
1484
			    }
1485
			}
1486
		}
1487
1488
		unset($clone_project->context['createfromclone']);
1489
1490
		if (! $error)
1491
		{
1492
			$this->db->commit();
1493
			return $clone_project_id;
0 ignored issues
show
Bug introduced by
The variable $clone_project_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1494
		}
1495
		else
1496
		{
1497
			$this->db->rollback();
1498
			dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : " . $this->error, LOG_ERR);
1499
			return -1;
1500
		}
1501
	}
1502
1503
1504
	 /**
1505
	  *    Shift project task date from current date to delta
1506
	  *
1507
	  *    @param	timestamp		$old_project_dt_start	old project start date
1508
	  *    @return	int				1 if OK or < 0 if KO
1509
	  */
1510
	function shiftTaskDate($old_project_dt_start)
1511
	{
1512
		global $user,$langs,$conf;
1513
1514
		$error=0;
1515
1516
		$taskstatic = new Task($this->db);
1517
1518
		// Security check
1519
		$socid=0;
1520
		if ($user->societe_id > 0) $socid = $user->societe_id;
1521
1522
		$tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1523
1524
	    foreach ($tasksarray as $tasktoshiftdate)
1525
	    {
1526
	    	$to_update=false;
1527
	    	// Fetch only if update of date will be made
1528
	    	if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end)))
1529
	    	{
1530
	    		//dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1531
	    		$to_update=true;
1532
		    	$task = new Task($this->db);
1533
		    	$result = $task->fetch($tasktoshiftdate->id);
1534
		    	if (!$result)
1535
		    	{
1536
		    		$error++;
1537
		    		$this->error.=$task->error;
1538
		    	}
1539
	    	}
1540
			//print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1541
1542
	    	//Calcultate new task start date with difference between old proj start date and origin task start date
1543
	    	if (!empty($tasktoshiftdate->date_start))
1544
	    	{
1545
				$task->date_start			= $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
0 ignored issues
show
Bug introduced by
The variable $task does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1546
	    	}
1547
1548
	    	//Calcultate new task end date with difference between origin proj end date and origin task end date
1549
	    	if (!empty($tasktoshiftdate->date_end))
1550
	    	{
1551
				$task->date_end		    	= $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1552
	    	}
1553
1554
			if ($to_update)
1555
			{
1556
		    	$result = $task->update($user);
1557
		    	if (!$result)
1558
		    	{
1559
		    		$error++;
1560
		    		$this->error.=$task->error;
1561
		    	}
1562
			}
1563
	    }
1564
	    if ($error!=0)
1565
	    {
1566
	    	return -1;
1567
	    }
1568
	    return $result;
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1569
	}
1570
1571
1572
	 /**
1573
	  *    Associate element to a project
1574
	  *
1575
	  *    @param	string	$tableName			Table of the element to update
1576
	  *    @param	int		$elementSelectId	Key-rowid of the line of the element to update
1577
	  *    @return	int							1 if OK or < 0 if KO
1578
	  */
1579
	function update_element($tableName, $elementSelectId)
1580
	{
1581
		$sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1582
1583
		if ($tableName == "actioncomm")
1584
		{
1585
			$sql.= " SET fk_project=".$this->id;
1586
			$sql.= " WHERE id=".$elementSelectId;
1587
		}
1588
		else
1589
		{
1590
			$sql.= " SET fk_projet=".$this->id;
1591
			$sql.= " WHERE rowid=".$elementSelectId;
1592
		}
1593
1594
		dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
1595
		$resql=$this->db->query($sql);
1596
		if (!$resql) {
1597
			$this->error=$this->db->lasterror();
1598
			return -1;
1599
		}else {
1600
			return 1;
1601
		}
1602
1603
	}
1604
1605
	/**
1606
	 *    Associate element to a project
1607
	 *
1608
	 *    @param	string	$tableName			Table of the element to update
1609
	 *    @param	int		$elementSelectId	Key-rowid of the line of the element to update
1610
	 *    @return	int							1 if OK or < 0 if KO
1611
	 */
1612
	function remove_element($tableName, $elementSelectId)
1613
	{
1614
		$sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1615
1616
		if ($TableName=="actioncomm")
0 ignored issues
show
Bug introduced by
The variable $TableName does not exist. Did you mean $tableName?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
1617
		{
1618
			$sql.= " SET fk_project=NULL";
1619
			$sql.= " WHERE id=".$elementSelectId;
1620
		}
1621
		else
1622
		{
1623
			$sql.= " SET fk_projet=NULL";
1624
			$sql.= " WHERE rowid=".$elementSelectId;
1625
		}
1626
1627
		dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
1628
		$resql=$this->db->query($sql);
1629
		if (!$resql) {
1630
			$this->error=$this->db->lasterror();
1631
			return -1;
1632
		}else {
1633
			return 1;
1634
		}
1635
1636
	}
1637
1638
	/**
1639
	 *  Create an intervention document on disk using template defined into PROJECT_ADDON_PDF
1640
	 *
1641
	 *  @param	string		$modele			Force template to use ('' by default)
1642
	 *  @param	Translate	$outputlangs	Objet lang to use for translation
1643
	 *  @param  int			$hidedetails    Hide details of lines
1644
	 *  @param  int			$hidedesc       Hide description
1645
	 *  @param  int			$hideref        Hide ref
1646
	 *  @return int         				0 if KO, 1 if OK
1647
	 */
1648
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
1649
	{
1650
		global $conf,$langs;
1651
1652
		$langs->load("projects");
1653
1654
		if (! dol_strlen($modele)) {
1655
1656
			$modele = 'baleine';
1657
1658
			if ($this->modelpdf) {
1659
				$modele = $this->modelpdf;
1660
			} elseif (! empty($conf->global->PROJECT_ADDON_PDF)) {
1661
				$modele = $conf->global->PROJECT_ADDON_PDF;
1662
			}
1663
		}
1664
1665
		$modelpath = "core/modules/project/doc/";
1666
1667
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1668
	}
1669
1670
1671
	/**
1672
	 * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project.
1673
	 * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call.
1674
	 *
1675
	 * @param 	int		$datestart		First day of week (use dol_get_first_day to find this date)
1676
	 * @param 	int		$taskid			Filter on a task id
1677
	 * @param 	int		$userid			Time spent by a particular user
1678
	 * @return 	int						<0 if OK, >0 if KO
1679
	 */
1680
	public function loadTimeSpent($datestart, $taskid=0, $userid=0)
1681
    {
1682
        $error=0;
1683
1684
        $this->weekWorkLoad=array();
1685
        $this->weekWorkLoadPerTask=array();
1686
1687
        if (empty($datestart)) dol_print_error('','Error datestart parameter is empty');
1688
1689
        $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task";
1690
        $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
1691
        $sql.= " WHERE ptt.fk_task = pt.rowid";
1692
        $sql.= " AND pt.fk_projet = ".$this->id;
1693
        $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' ";
1694
        $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
1695
        if ($task_id) $sql.= " AND ptt.fk_task=".$taskid;
0 ignored issues
show
Bug introduced by
The variable $task_id does not exist. Did you mean $taskid?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
1696
        if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid;
1697
1698
        //print $sql;
1699
        $resql=$this->db->query($sql);
1700
        if ($resql)
1701
        {
1702
				$daylareadyfound=array();
1703
1704
                $num = $this->db->num_rows($resql);
1705
                $i = 0;
1706
                // Loop on each record found, so each couple (project id, task id)
1707
                while ($i < $num)
1708
                {
1709
                        $obj=$this->db->fetch_object($resql);
1710
                        $day=$this->db->jdate($obj->task_date);		// task_date is date without hours
1711
                        if (empty($daylareadyfound[$day]))
1712
                        {
1713
                        	$this->weekWorkLoad[$day] = $obj->task_duration;
1714
                        	$this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration;
1715
                        }
1716
                        else
1717
                        {
1718
                        	$this->weekWorkLoad[$day] += $obj->task_duration;
1719
                        	$this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration;
1720
                        }
1721
                        $daylareadyfound[$day]=1;
1722
                        $i++;
1723
                }
1724
                $this->db->free($resql);
1725
                return 1;
1726
         }
1727
        else
1728
        {
1729
                $this->error="Error ".$this->db->lasterror();
1730
                dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
1731
                return -1;
1732
        }
1733
    }
1734
1735
1736
    /**
1737
     * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
1738
     *
1739
     * @param	User	$user   Objet user
1740
     * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
1741
     */
1742
    function load_board($user)
1743
    {
1744
        global $conf, $langs;
1745
1746
        // For external user, no check is done on company because readability is managed by public status of project and assignement.
1747
        //$socid=$user->societe_id;
1748
1749
        if (! $user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user,0,1,$socid);
0 ignored issues
show
Bug introduced by
The variable $socid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1750
1751
        $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
1752
        $sql.= " FROM (".MAIN_DB_PREFIX."projet as p";
1753
        $sql.= ")";
1754
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
1755
        // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1756
        //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
1757
        $sql.= " WHERE p.fk_statut = 1";
1758
        $sql.= " AND p.entity IN (".getEntity('project').')';
1759
        if (! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")";
0 ignored issues
show
Bug introduced by
The variable $projectsListId does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1760
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
1761
        //if ($socid || ! $user->rights->societe->client->voir)	$sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1762
        // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1763
        //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))";
1764
1765
        //print $sql;
1766
        $resql=$this->db->query($sql);
1767
        if ($resql)
1768
        {
1769
            $project_static = new Project($this->db);
1770
1771
            $response = new WorkboardResponse();
1772
            $response->warning_delay = $conf->projet->warning_delay/60/60/24;
1773
            $response->label = $langs->trans("OpenedProjects");
1774
            if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
1775
            else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
1776
            $response->img = img_object('',"projectpub");
1777
1778
            // This assignment in condition is not a bug. It allows walking the results.
1779
            while ($obj=$this->db->fetch_object($resql))
1780
            {
1781
                $response->nbtodo++;
1782
1783
                $project_static->statut = $obj->status;
1784
                $project_static->opp_status = $obj->opp_status;
1785
                $project_static->datee = $this->db->jdate($obj->datee);
1786
1787
                if ($project_static->hasDelay()) {
1788
                    $response->nbtodolate++;
1789
                }
1790
            }
1791
1792
            return $response;
1793
        }
1794
        else
1795
        {
1796
            $this->error=$this->db->error();
1797
            return -1;
1798
        }
1799
    }
1800
1801
1802
	/**
1803
	 * Function used to replace a thirdparty id with another one.
1804
	 *
1805
	 * @param DoliDB $db Database handler
1806
	 * @param int $origin_id Old thirdparty id
1807
	 * @param int $dest_id New thirdparty id
1808
	 * @return bool
1809
	 */
1810
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1811
	{
1812
		$tables = array(
1813
			'projet'
1814
		);
1815
1816
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1817
	}
1818
1819
1820
	/**
1821
	 *      Charge indicateurs this->nb pour le tableau de bord
1822
	 *
1823
	 *      @return     int         <0 if KO, >0 if OK
1824
	 */
1825
	function load_state_board()
1826
	{
1827
	    global $user;
1828
1829
	    $this->nb=array();
1830
1831
	    $sql = "SELECT count(p.rowid) as nb";
1832
	    $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
1833
	    $sql.= " WHERE";
1834
	    $sql.= " p.entity IN (".getEntity('project').")";
1835
		if (! $user->rights->projet->all->lire)
1836
		{
1837
			$projectsListId = $this->getProjectsAuthorizedForUser($user,0,1);
1838
			$sql .= "AND p.rowid IN (".$projectsListId.")";
1839
		}
1840
1841
	    $resql=$this->db->query($sql);
1842
	    if ($resql)
1843
	    {
1844
	        while ($obj=$this->db->fetch_object($resql))
1845
	        {
1846
	            $this->nb["projects"]=$obj->nb;
1847
	        }
1848
	        $this->db->free($resql);
1849
	        return 1;
1850
	    }
1851
	    else
1852
	    {
1853
	        dol_print_error($this->db);
1854
	        $this->error=$this->db->error();
1855
	        return -1;
1856
	    }
1857
	}
1858
1859
1860
	/**
1861
	 * Is the project delayed?
1862
	 *
1863
	 * @return bool
1864
	 */
1865
	public function hasDelay()
1866
	{
1867
	    global $conf;
1868
1869
        if (! ($this->statut == 1)) return false;
1870
        if (! $this->datee && ! $this->date_end) return false;
1871
1872
        $now = dol_now();
1873
1874
        return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay);
1875
	}
1876
1877
1878
	/**
1879
	 *	Charge les informations d'ordre info dans l'objet commande
1880
	 *
1881
	 *	@param  int		$id       Id of order
1882
	 *	@return	void
1883
	 */
1884
	function info($id)
1885
	{
1886
	    $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
1887
	    $sql.= ' date_close as datecloture,';
1888
	    $sql.= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture';
1889
	    $sql.= ' FROM '.MAIN_DB_PREFIX.'projet as c';
1890
	    $sql.= ' WHERE c.rowid = '.$id;
1891
	    $result=$this->db->query($sql);
1892
	    if ($result)
1893
	    {
1894
	        if ($this->db->num_rows($result))
1895
	        {
1896
	            $obj = $this->db->fetch_object($result);
1897
	            $this->id = $obj->rowid;
1898
	            if ($obj->fk_user_author)
1899
	            {
1900
	                $cuser = new User($this->db);
1901
	                $cuser->fetch($obj->fk_user_author);
1902
	                $this->user_creation   = $cuser;
1903
	            }
1904
1905
	            if ($obj->fk_user_cloture)
1906
	            {
1907
	                $cluser = new User($this->db);
1908
	                $cluser->fetch($obj->fk_user_cloture);
1909
	                $this->user_cloture   = $cluser;
1910
	            }
1911
1912
	            $this->date_creation     = $this->db->jdate($obj->datec);
1913
	            $this->date_modification = $this->db->jdate($obj->datem);
1914
	            $this->date_cloture      = $this->db->jdate($obj->datecloture);
1915
	        }
1916
1917
	        $this->db->free($result);
1918
1919
	    }
1920
	    else
1921
	    {
1922
	        dol_print_error($this->db);
1923
	    }
1924
	}
1925
1926
	/**
1927
	 * Sets object to supplied categories.
1928
	 *
1929
	 * Deletes object from existing categories not supplied.
1930
	 * Adds it to non existing supplied categories.
1931
	 * Existing categories are left untouch.
1932
	 *
1933
	 * @param int[]|int $categories Category or categories IDs
1934
     * @return void
1935
	 */
1936
	public function setCategories($categories)
1937
	{
1938
		// Decode type
1939
		$type_id = Categorie::TYPE_PROJECT;
1940
		$type_text = 'project';
1941
1942
1943
		// Handle single category
1944
		if (!is_array($categories)) {
1945
			$categories = array($categories);
1946
		}
1947
1948
		// Get current categories
1949
		require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1950
		$c = new Categorie($this->db);
1951
		$existing = $c->containing($this->id, $type_id, 'id');
1952
1953
		// Diff
1954
		if (is_array($existing)) {
1955
			$to_del = array_diff($existing, $categories);
1956
			$to_add = array_diff($categories, $existing);
1957
		} else {
1958
			$to_del = array(); // Nothing to delete
1959
			$to_add = $categories;
1960
		}
1961
1962
		// Process
1963
		foreach ($to_del as $del) {
1964
			if ($c->fetch($del) > 0) {
1965
				$result=$c->del_type($this, $type_text);
1966
				if ($result<0) {
1967
					$this->errors=$c->errors;
1968
					$this->error=$c->error;
1969
					return -1;
1970
				}
1971
			}
1972
		}
1973
		foreach ($to_add as $add) {
1974
			if ($c->fetch($add) > 0) {
1975
				$result=$c->add_type($this, $type_text);
1976
				if ($result<0) {
1977
					$this->errors=$c->errors;
1978
					$this->error=$c->error;
1979
					return -1;
1980
				}
1981
			}
1982
		}
1983
1984
		return 1;
1985
	}
1986
1987
1988
	/**
1989
	 * 	Create an array of tasks of current project
1990
	 *
1991
	 *  @param  User   $user       Object user we want project allowed to
1992
	 * 	@return int		           >0 if OK, <0 if KO
1993
	 */
1994
	function getLinesArray($user)
1995
	{
1996
	    require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
1997
	    $taskstatic = new Task($this->db);
1998
1999
	    $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0);
0 ignored issues
show
Documentation Bug introduced by
It seems like $taskstatic->getTasksArr...$user, $this->id, 0, 0) can also be of type string. However, the property $lines is declared as type array<integer,object<Task>>. 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...
2000
	}
2001
2002
}
2003