Passed
Branch develop (a7390e)
by
unknown
25:38
created

Project::update()   F

Complexity

Conditions 35
Paths > 20000

Size

Total Lines 123
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 35
eloc 74
c 0
b 0
f 0
nc 7995400
nop 2
dl 0
loc 123
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1311
        /** @scrutinizer ignore-deprecated */ $clone_project->datec = $now;
Loading history...
Documentation Bug introduced by
It seems like $now can also be of type string. However, the property $datec is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1312
1313
        if (! $clone_note)
1314
        {
1315
        	    $clone_project->note_private='';
1316
    			$clone_project->note_public='';
1317
        }
1318
1319
		//Generate next ref
1320
		$defaultref='';
1321
    	$obj = empty($conf->global->PROJECT_ADDON)?'mod_project_simple':$conf->global->PROJECT_ADDON;
1322
    	// Search template files
1323
    	$file=''; $classname=''; $filefound=0;
1324
    	$dirmodels=array_merge(array('/'), (array) $conf->modules_parts['models']);
1325
    	foreach($dirmodels as $reldir)
1326
    	{
1327
    	    $file=dol_buildpath($reldir."core/modules/project/".$obj.'.php', 0);
1328
    	    if (file_exists($file))
1329
    	    {
1330
    	        $filefound=1;
1331
    	        dol_include_once($reldir."core/modules/project/".$obj.'.php');
1332
            	$modProject = new $obj;
1333
            	$defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty)?$clone_project->thirdparty:null, $clone_project);
1334
            	break;
1335
    	    }
1336
    	}
1337
    	if (is_numeric($defaultref) && $defaultref <= 0) $defaultref='';
1338
1339
		$clone_project->ref=$defaultref;
1340
		$clone_project->title=$langs->trans("CopyOf").' '.$clone_project->title;
1341
1342
		// Create clone
1343
		$result=$clone_project->create($user, $notrigger);
1344
1345
		// Other options
1346
		if ($result < 0)
1347
		{
1348
			$this->error.=$clone_project->error;
1349
			$error++;
1350
		}
1351
1352
		if (! $error)
1353
		{
1354
			//Get the new project id
1355
			$clone_project_id=$clone_project->id;
1356
1357
			//Note Update
1358
            if (!$clone_note)
1359
            {
1360
        	    $clone_project->note_private='';
1361
    			$clone_project->note_public='';
1362
            }
1363
        	else
1364
        	{
1365
        		$this->db->begin();
1366
				$res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES), '_public');
1367
				if ($res < 0)
1368
				{
1369
					$this->error.=$clone_project->error;
1370
					$error++;
1371
					$this->db->rollback();
1372
				}
1373
				else
1374
				{
1375
					$this->db->commit();
1376
				}
1377
1378
				$this->db->begin();
1379
				$res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private');
1380
				if ($res < 0)
1381
				{
1382
					$this->error.=$clone_project->error;
1383
					$error++;
1384
					$this->db->rollback();
1385
				}
1386
				else
1387
				{
1388
					$this->db->commit();
1389
				}
1390
        	}
1391
1392
			//Duplicate contact
1393
			if ($clone_contact)
1394
			{
1395
				$origin_project = new Project($this->db);
1396
				$origin_project->fetch($fromid);
1397
1398
				foreach(array('internal','external') as $source)
1399
				{
1400
					$tab = $origin_project->liste_contact(-1, $source);
1401
1402
					foreach ($tab as $contacttoadd)
0 ignored issues
show
Bug introduced by
The expression $tab of type integer is not traversable.
Loading history...
1403
					{
1404
						$clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'], $notrigger);
1405
						if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS')
1406
						{
1407
							$langs->load("errors");
1408
							$this->error.=$langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
1409
							$error++;
1410
						}
1411
						else
1412
						{
1413
							if ($clone_project->error!='')
1414
							{
1415
								$this->error.=$clone_project->error;
1416
								$error++;
1417
							}
1418
						}
1419
					}
1420
				}
1421
			}
1422
1423
			//Duplicate file
1424
			if ($clone_project_file)
1425
			{
1426
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1427
1428
				$clone_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($defaultref);
1429
				$ori_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($orign_project_ref);
1430
1431
				if (dol_mkdir($clone_project_dir) >= 0)
1432
				{
1433
					$filearray=dol_dir_list($ori_project_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1434
					foreach($filearray as $key => $file)
1435
					{
1436
						$rescopy = dol_copy($ori_project_dir . '/' . $file['name'], $clone_project_dir . '/' . $file['name'], 0, 1);
1437
						if (is_numeric($rescopy) && $rescopy < 0)
1438
						{
1439
							$this->error.=$langs->trans("ErrorFailToCopyFile", $ori_project_dir . '/' . $file['name'], $clone_project_dir . '/' . $file['name']);
1440
							$error++;
1441
						}
1442
					}
1443
				}
1444
				else
1445
				{
1446
					$this->error.=$langs->trans('ErrorInternalErrorDetected').':dol_mkdir';
1447
					$error++;
1448
				}
1449
			}
1450
1451
			//Duplicate task
1452
			if ($clone_task)
1453
			{
1454
				require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php';
1455
1456
				$taskstatic = new Task($this->db);
1457
1458
				// Security check
1459
				$socid=0;
1460
				if ($user->societe_id > 0) $socid = $user->societe_id;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1460
				if (/** @scrutinizer ignore-deprecated */ $user->societe_id > 0) $socid = $user->societe_id;
Loading history...
1461
1462
				$tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0);
1463
1464
				$tab_conv_child_parent=array();
1465
1466
				// Loop on each task, to clone it
1467
			    foreach ($tasksarray as $tasktoclone)
1468
			    {
1469
					$result_clone = $taskstatic->createFromClone($user, $tasktoclone->id, $clone_project_id, $tasktoclone->fk_parent, $move_date, true, false, $clone_task_file, true, false);
1470
					if ($result_clone <= 0)
1471
				    {
1472
				    	$this->error.=$result_clone->error;
0 ignored issues
show
Bug introduced by
The property error does not exist on integer.
Loading history...
1473
						$error++;
1474
				    }
1475
				    else
1476
				    {
1477
				    	$new_task_id=$result_clone;
1478
				    	$taskstatic->fetch($tasktoclone->id);
1479
1480
				    	//manage new parent clone task id
1481
				    	// if the current task has child we store the original task id and the equivalent clone task id
1482
						if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id, $tab_conv_child_parent))
1483
						{
1484
							$tab_conv_child_parent[$tasktoclone->id] =  $new_task_id;
1485
						}
1486
				    }
1487
			    }
1488
1489
			    //Parse all clone node to be sure to update new parent
1490
			    $tasksarray=$taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0);
1491
			    foreach ($tasksarray as $task_cloned)
1492
			    {
1493
			    	$taskstatic->fetch($task_cloned->id);
1494
			    	if ($taskstatic->fk_task_parent!=0)
1495
			    	{
1496
			    		$taskstatic->fk_task_parent=$tab_conv_child_parent[$taskstatic->fk_task_parent];
1497
			    	}
1498
			    	$res=$taskstatic->update($user, $notrigger);
1499
			    	if ($result_clone <= 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result_clone seems to be defined by a foreach iteration on line 1467. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1500
				    {
1501
				    	$this->error.=$taskstatic->error;
1502
						$error++;
1503
				    }
1504
			    }
1505
			}
1506
		}
1507
1508
		unset($clone_project->context['createfromclone']);
1509
1510
		if (! $error)
1511
		{
1512
			$this->db->commit();
1513
			return $clone_project_id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $clone_project_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
1514
		}
1515
		else
1516
		{
1517
			$this->db->rollback();
1518
			dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : " . $this->error, LOG_ERR);
1519
			return -1;
1520
		}
1521
    }
1522
1523
1524
    /**
1525
	 *    Shift project task date from current date to delta
1526
	 *
1527
	 *    @param	integer		$old_project_dt_start	Old project start date
1528
	 *    @return	int				                    1 if OK or < 0 if KO
1529
	 */
1530
    public function shiftTaskDate($old_project_dt_start)
1531
    {
1532
		global $user, $langs, $conf;
1533
1534
		$error=0;
1535
1536
		$taskstatic = new Task($this->db);
1537
1538
		// Security check
1539
		$socid=0;
1540
		if ($user->societe_id > 0) $socid = $user->societe_id;
1541
1542
		$tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0);
1543
1544
	    foreach ($tasksarray as $tasktoshiftdate)
1545
	    {
1546
	    	$to_update=false;
1547
	    	// Fetch only if update of date will be made
1548
	    	if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end)))
1549
	    	{
1550
	    		//dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG);
1551
	    		$to_update=true;
1552
		    	$task = new Task($this->db);
1553
		    	$result = $task->fetch($tasktoshiftdate->id);
1554
		    	if (!$result)
1555
		    	{
1556
		    		$error++;
1557
		    		$this->error.=$task->error;
1558
		    	}
1559
	    	}
1560
			//print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit;
1561
1562
	    	//Calcultate new task start date with difference between old proj start date and origin task start date
1563
	    	if (!empty($tasktoshiftdate->date_start))
1564
	    	{
1565
				$task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $task does not seem to be defined for all execution paths leading up to this point.
Loading history...
1566
	    	}
1567
1568
	    	//Calcultate new task end date with difference between origin proj end date and origin task end date
1569
	    	if (!empty($tasktoshiftdate->date_end))
1570
	    	{
1571
				$task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start);
1572
	    	}
1573
1574
			if ($to_update)
1575
			{
1576
		    	$result = $task->update($user);
1577
		    	if (!$result)
1578
		    	{
1579
		    		$error++;
1580
		    		$this->error.=$task->error;
1581
		    	}
1582
			}
1583
	    }
1584
	    if ($error!=0)
1585
	    {
1586
	    	return -1;
1587
	    }
1588
	    return $result;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
1589
	}
1590
1591
1592
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1593
	/**
1594
	 *    Associate element to a project
1595
	 *
1596
	 *    @param	string	$tableName			Table of the element to update
1597
	 *    @param	int		$elementSelectId	Key-rowid of the line of the element to update
1598
	 *    @return	int							1 if OK or < 0 if KO
1599
     */
1600
	public function update_element($tableName, $elementSelectId)
1601
	{
1602
        // phpcs:enable
1603
		$sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1604
1605
		if ($tableName == "actioncomm")
1606
		{
1607
			$sql.= " SET fk_project=".$this->id;
1608
			$sql.= " WHERE id=".$elementSelectId;
1609
		}
1610
		else
1611
		{
1612
			$sql.= " SET fk_projet=".$this->id;
1613
			$sql.= " WHERE rowid=".$elementSelectId;
1614
		}
1615
1616
		dol_syslog(get_class($this)."::update_element", LOG_DEBUG);
1617
		$resql=$this->db->query($sql);
1618
		if (!$resql) {
1619
			$this->error=$this->db->lasterror();
1620
			return -1;
1621
		} else {
1622
			return 1;
1623
		}
1624
	}
1625
1626
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1627
	/**
1628
	 *    Associate element to a project
1629
	 *
1630
	 *    @param	string	$tableName			Table of the element to update
1631
	 *    @param	int		$elementSelectId	Key-rowid of the line of the element to update
1632
	 *    @return	int							1 if OK or < 0 if KO
1633
	 */
1634
	public function remove_element($tableName, $elementSelectId)
1635
	{
1636
        // phpcs:enable
1637
		$sql="UPDATE ".MAIN_DB_PREFIX.$tableName;
1638
1639
		if ($tableName=="actioncomm")
1640
		{
1641
			$sql.= " SET fk_project=NULL";
1642
			$sql.= " WHERE id=".$elementSelectId;
1643
		}
1644
		else
1645
		{
1646
			$sql.= " SET fk_projet=NULL";
1647
			$sql.= " WHERE rowid=".$elementSelectId;
1648
		}
1649
1650
		dol_syslog(get_class($this)."::remove_element", LOG_DEBUG);
1651
		$resql=$this->db->query($sql);
1652
		if (!$resql) {
1653
			$this->error=$this->db->lasterror();
1654
			return -1;
1655
		}else {
1656
			return 1;
1657
		}
1658
	}
1659
1660
	/**
1661
	 *  Create an intervention document on disk using template defined into PROJECT_ADDON_PDF
1662
	 *
1663
	 *  @param	string		$modele			Force template to use ('' by default)
1664
	 *  @param	Translate	$outputlangs	Objet lang to use for translation
1665
	 *  @param  int			$hidedetails    Hide details of lines
1666
	 *  @param  int			$hidedesc       Hide description
1667
	 *  @param  int			$hideref        Hide ref
1668
	 *  @return int         				0 if KO, 1 if OK
1669
	 */
1670
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
1671
	{
1672
		global $conf,$langs;
1673
1674
		$langs->load("projects");
1675
1676
		if (! dol_strlen($modele)) {
1677
1678
			$modele = 'baleine';
1679
1680
			if ($this->modelpdf) {
1681
				$modele = $this->modelpdf;
1682
			} elseif (! empty($conf->global->PROJECT_ADDON_PDF)) {
1683
				$modele = $conf->global->PROJECT_ADDON_PDF;
1684
			}
1685
		}
1686
1687
		$modelpath = "core/modules/project/doc/";
1688
1689
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
1690
	}
1691
1692
1693
	/**
1694
	 * Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project.
1695
	 * Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call.
1696
	 *
1697
	 * @param 	int		$datestart		First day of week (use dol_get_first_day to find this date)
1698
	 * @param 	int		$taskid			Filter on a task id
1699
	 * @param 	int		$userid			Time spent by a particular user
1700
	 * @return 	int						<0 if OK, >0 if KO
1701
	 */
1702
    public function loadTimeSpent($datestart, $taskid = 0, $userid = 0)
1703
    {
1704
        $error=0;
1705
1706
        $this->weekWorkLoad=array();
1707
        $this->weekWorkLoadPerTask=array();
1708
1709
        if (empty($datestart)) dol_print_error('', 'Error datestart parameter is empty');
1710
1711
        $sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task";
1712
        $sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt";
1713
        $sql.= " WHERE ptt.fk_task = pt.rowid";
1714
        $sql.= " AND pt.fk_projet = ".$this->id;
1715
        $sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' ";
1716
        $sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')";
1717
        if ($taskid) $sql.= " AND ptt.fk_task=".$taskid;
1718
        if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid;
1 ignored issue
show
introduced by
The condition is_numeric($userid) is always true.
Loading history...
1719
1720
        //print $sql;
1721
        $resql=$this->db->query($sql);
1722
        if ($resql)
1723
        {
1724
				$daylareadyfound=array();
1725
1726
                $num = $this->db->num_rows($resql);
1727
                $i = 0;
1728
                // Loop on each record found, so each couple (project id, task id)
1729
                while ($i < $num)
1730
                {
1731
                        $obj=$this->db->fetch_object($resql);
1732
                        $day=$this->db->jdate($obj->task_date);		// task_date is date without hours
1733
                        if (empty($daylareadyfound[$day]))
1734
                        {
1735
                        	$this->weekWorkLoad[$day] = $obj->task_duration;
1736
                        	$this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration;
1737
                        }
1738
                        else
1739
                        {
1740
                        	$this->weekWorkLoad[$day] += $obj->task_duration;
1741
                        	$this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration;
1742
                        }
1743
                        $daylareadyfound[$day]=1;
1744
                        $i++;
1745
                }
1746
                $this->db->free($resql);
1747
                return 1;
1748
        }
1749
        else
1750
        {
1751
                $this->error="Error ".$this->db->lasterror();
1752
                dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
1753
                return -1;
1754
        }
1755
    }
1756
1757
1758
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1759
    /**
1760
     * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
1761
     *
1762
     * @param	User	$user   Objet user
1763
     * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
1764
     */
1765
    public function load_board($user)
1766
    {
1767
        // phpcs:enable
1768
        global $conf, $langs;
1769
1770
        // For external user, no check is done on company because readability is managed by public status of project and assignement.
1771
        //$socid=$user->societe_id;
1772
1773
		$projectsListId = null;
1774
        if (! $user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
1775
1776
        $sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee";
1777
        $sql.= " FROM (".MAIN_DB_PREFIX."projet as p";
1778
        $sql.= ")";
1779
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
1780
        // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1781
        //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
1782
        $sql.= " WHERE p.fk_statut = 1";
1783
        $sql.= " AND p.entity IN (".getEntity('project').')';
1784
        if (! empty($projectsListId)) $sql.= " AND p.rowid IN (".$projectsListId.")";
1 ignored issue
show
Bug introduced by
Are you sure $projectsListId of type array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1784
        if (! empty($projectsListId)) $sql.= " AND p.rowid IN ("./** @scrutinizer ignore-type */ $projectsListId.")";
Loading history...
1785
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
1786
        //if ($socid || ! $user->rights->societe->client->voir)	$sql.= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")";
1787
        // For external user, no check is done on company permission because readability is managed by public status of project and assignement.
1788
        //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))";
1789
1790
        //print $sql;
1791
        $resql=$this->db->query($sql);
1792
        if ($resql)
1793
        {
1794
            $project_static = new Project($this->db);
1795
1796
            $response = new WorkboardResponse();
1797
            $response->warning_delay = $conf->projet->warning_delay/60/60/24;
1798
            $response->label = $langs->trans("OpenedProjects");
1799
            if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project';
1800
            else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project';
1801
            $response->img = img_object('', "projectpub");
1802
1803
            // This assignment in condition is not a bug. It allows walking the results.
1804
            while ($obj=$this->db->fetch_object($resql))
1805
            {
1806
                $response->nbtodo++;
1807
1808
                $project_static->statut = $obj->status;
1809
                $project_static->opp_status = $obj->opp_status;
1810
                $project_static->datee = $this->db->jdate($obj->datee);
1811
1812
                if ($project_static->hasDelay()) {
1813
                    $response->nbtodolate++;
1814
                }
1815
            }
1816
1817
            return $response;
1818
        }
1819
        else
1820
        {
1821
            $this->error=$this->db->error();
1822
            return -1;
1823
        }
1824
    }
1825
1826
1827
	/**
1828
	 * Function used to replace a thirdparty id with another one.
1829
	 *
1830
	 * @param DoliDB $db Database handler
1831
	 * @param int $origin_id Old thirdparty id
1832
	 * @param int $dest_id New thirdparty id
1833
	 * @return bool
1834
	 */
1835
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1836
	{
1837
		$tables = array(
1838
			'projet'
1839
		);
1840
1841
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1842
	}
1843
1844
1845
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1846
	/**
1847
	 *      Charge indicateurs this->nb pour le tableau de bord
1848
	 *
1849
	 *      @return     int         <0 if KO, >0 if OK
1850
	 */
1851
	public function load_state_board()
1852
	{
1853
        // phpcs:enable
1854
	    global $user;
1855
1856
	    $this->nb=array();
1857
1858
	    $sql = "SELECT count(p.rowid) as nb";
1859
	    $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
1860
	    $sql.= " WHERE";
1861
	    $sql.= " p.entity IN (".getEntity('project').")";
1862
		if (! $user->rights->projet->all->lire)
1863
		{
1864
			$projectsListId = $this->getProjectsAuthorizedForUser($user, 0, 1);
1865
			$sql .= "AND p.rowid IN (".$projectsListId.")";
1 ignored issue
show
Bug introduced by
Are you sure $projectsListId of type array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1865
			$sql .= "AND p.rowid IN ("./** @scrutinizer ignore-type */ $projectsListId.")";
Loading history...
1866
		}
1867
1868
	    $resql=$this->db->query($sql);
1869
	    if ($resql)
1870
	    {
1871
	        while ($obj=$this->db->fetch_object($resql))
1872
	        {
1873
	            $this->nb["projects"]=$obj->nb;
1874
	        }
1875
	        $this->db->free($resql);
1876
	        return 1;
1877
	    }
1878
	    else
1879
	    {
1880
	        dol_print_error($this->db);
1881
	        $this->error=$this->db->error();
1882
	        return -1;
1883
	    }
1884
	}
1885
1886
1887
	/**
1888
	 * Is the project delayed?
1889
	 *
1890
	 * @return bool
1891
	 */
1892
	public function hasDelay()
1893
	{
1894
	    global $conf;
1895
1896
        if (! ($this->statut == 1)) return false;
1897
        if (! $this->datee && ! $this->date_end) return false;
1898
1899
        $now = dol_now();
1900
1901
        return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay);
1902
	}
1903
1904
1905
	/**
1906
	 *	Charge les informations d'ordre info dans l'objet commande
1907
	 *
1908
	 *	@param  int		$id       Id of order
1909
	 *	@return	void
1910
	 */
1911
	public function info($id)
1912
	{
1913
	    $sql = 'SELECT c.rowid, datec as datec, tms as datem,';
1914
	    $sql.= ' date_close as datecloture,';
1915
	    $sql.= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture';
1916
	    $sql.= ' FROM '.MAIN_DB_PREFIX.'projet as c';
1917
	    $sql.= ' WHERE c.rowid = '.$id;
1918
	    $result=$this->db->query($sql);
1919
	    if ($result)
1920
	    {
1921
	        if ($this->db->num_rows($result))
1922
	        {
1923
	            $obj = $this->db->fetch_object($result);
1924
	            $this->id = $obj->rowid;
1925
	            if ($obj->fk_user_author)
1926
	            {
1927
	                $cuser = new User($this->db);
1928
	                $cuser->fetch($obj->fk_user_author);
1929
	                $this->user_creation   = $cuser;
1930
	            }
1931
1932
	            if ($obj->fk_user_cloture)
1933
	            {
1934
	                $cluser = new User($this->db);
1935
	                $cluser->fetch($obj->fk_user_cloture);
1936
	                $this->user_cloture   = $cluser;
1937
	            }
1938
1939
	            $this->date_creation     = $this->db->jdate($obj->datec);
1940
	            $this->date_modification = $this->db->jdate($obj->datem);
1941
	            $this->date_cloture      = $this->db->jdate($obj->datecloture);
1942
	        }
1943
1944
	        $this->db->free($result);
1945
	    }
1946
	    else
1947
	    {
1948
	        dol_print_error($this->db);
1949
	    }
1950
	}
1951
1952
	/**
1953
	 * Sets object to supplied categories.
1954
	 *
1955
	 * Deletes object from existing categories not supplied.
1956
	 * Adds it to non existing supplied categories.
1957
	 * Existing categories are left untouch.
1958
	 *
1959
	 * @param int[]|int $categories Category or categories IDs
1960
     * @return void
1961
	 */
1962
	public function setCategories($categories)
1963
	{
1964
		// Decode type
1965
		$type_id = Categorie::TYPE_PROJECT;
1966
		$type_text = 'project';
1967
1968
1969
		// Handle single category
1970
		if (!is_array($categories)) {
1971
			$categories = array($categories);
1972
		}
1973
1974
		// Get current categories
1975
		require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1976
		$c = new Categorie($this->db);
1977
		$existing = $c->containing($this->id, $type_id, 'id');
1978
1979
		// Diff
1980
		if (is_array($existing)) {
0 ignored issues
show
introduced by
The condition is_array($existing) is always false.
Loading history...
1981
			$to_del = array_diff($existing, $categories);
1982
			$to_add = array_diff($categories, $existing);
1983
		} else {
1984
			$to_del = array(); // Nothing to delete
1985
			$to_add = $categories;
1986
		}
1987
1988
		// Process
1989
		foreach ($to_del as $del) {
1990
			if ($c->fetch($del) > 0) {
1991
				$result=$c->del_type($this, $type_text);
1992
				if ($result<0) {
1993
					$this->errors=$c->errors;
1994
					$this->error=$c->error;
1995
					return -1;
1996
				}
1997
			}
1998
		}
1999
		foreach ($to_add as $add) {
2000
			if ($c->fetch($add) > 0) {
2001
				$result=$c->add_type($this, $type_text);
2002
				if ($result<0) {
2003
					$this->errors=$c->errors;
2004
					$this->error=$c->error;
2005
					return -1;
2006
				}
2007
			}
2008
		}
2009
2010
		return 1;
2011
	}
2012
2013
2014
	/**
2015
	 * 	Create an array of tasks of current project
2016
	 *
2017
	 *  @param  User   $user       Object user we want project allowed to
2018
	 * 	@return int		           >0 if OK, <0 if KO
2019
	 */
2020
	public function getLinesArray($user)
2021
	{
2022
	    require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
2023
	    $taskstatic = new Task($this->db);
2024
2025
	    $this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0);
2026
	}
2027
}
2028