Passed
Branch develop (f6c1a1)
by
unknown
28:54
created

Project::loadTimeSpentMonth()   B

Complexity

Conditions 8
Paths 48

Size

Total Lines 51
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 36
nc 48
nop 3
dl 0
loc 51
rs 8.0995
c 0
b 0
f 0

How to fix   Long Method   

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

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