|
1
|
|
|
<?php |
|
2
|
|
|
/* Copyright (C) 2002-2005 Rodolphe Quiedeville <[email protected]> |
|
3
|
|
|
* Copyright (C) 2005-2016 Laurent Destailleur <[email protected]> |
|
4
|
|
|
* Copyright (C) 2005-2010 Regis Houssin <[email protected]> |
|
5
|
|
|
* Copyright (C) 2013 Florian Henry <[email protected]> |
|
6
|
|
|
* Copyright (C) 2014-2017 Marcos García <[email protected]> |
|
7
|
|
|
* Copyright (C) 2017 Ferran Marcet <[email protected]> |
|
8
|
|
|
* |
|
9
|
|
|
* This program is free software; you can redistribute it and/or modify |
|
10
|
|
|
* it under the terms of the GNU General Public License as published by |
|
11
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
|
12
|
|
|
* (at your option) any later version. |
|
13
|
|
|
* |
|
14
|
|
|
* This program is distributed in the hope that it will be useful, |
|
15
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
17
|
|
|
* GNU General Public License for more details. |
|
18
|
|
|
* |
|
19
|
|
|
* You should have received a copy of the GNU General Public License |
|
20
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
21
|
|
|
*/ |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* \file htdocs/projet/class/project.class.php |
|
25
|
|
|
* \ingroup projet |
|
26
|
|
|
* \brief File of class to manage projects |
|
27
|
|
|
*/ |
|
28
|
|
|
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* Class to manage projects |
|
32
|
|
|
*/ |
|
33
|
|
|
class Project extends CommonObject |
|
34
|
|
|
{ |
|
35
|
|
|
|
|
36
|
|
|
public $element = 'project'; //!< Id that identify managed objects |
|
37
|
|
|
public $table_element = 'projet'; //!< Name of table without prefix where object is stored |
|
38
|
|
|
public $table_element_line = 'projet_task'; |
|
39
|
|
|
public $fk_element = 'fk_projet'; |
|
40
|
|
|
public $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe |
|
41
|
|
|
public $picto = 'projectpub'; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* {@inheritdoc} |
|
45
|
|
|
*/ |
|
46
|
|
|
protected $table_ref_field = 'ref'; |
|
47
|
|
|
|
|
48
|
|
|
var $description; |
|
49
|
|
|
/** |
|
50
|
|
|
* @var string |
|
51
|
|
|
* @deprecated |
|
52
|
|
|
* @see title |
|
53
|
|
|
*/ |
|
54
|
|
|
public $titre; |
|
55
|
|
|
var $title; |
|
56
|
|
|
var $date_start; |
|
57
|
|
|
var $date_end; |
|
58
|
|
|
var $date_close; |
|
59
|
|
|
|
|
60
|
|
|
var $socid; // To store id of thirdparty |
|
61
|
|
|
var $thirdparty_name; // To store name of thirdparty (defined only in some cases) |
|
62
|
|
|
|
|
63
|
|
|
var $user_author_id; //!< Id of project creator. Not defined if shared project. |
|
64
|
|
|
var $user_close_id; |
|
65
|
|
|
var $public; //!< Tell if this is a public or private project |
|
66
|
|
|
var $budget_amount; |
|
67
|
|
|
var $bill_time; // Is the time spent on project must be invoiced or not |
|
68
|
|
|
|
|
69
|
|
|
var $statuts_short; |
|
70
|
|
|
var $statuts_long; |
|
71
|
|
|
|
|
72
|
|
|
var $statut; // 0=draft, 1=opened, 2=closed |
|
73
|
|
|
var $opp_status; // opportunity status, into table llx_c_lead_status |
|
74
|
|
|
var $opp_percent; // opportunity probability |
|
75
|
|
|
|
|
76
|
|
|
var $oldcopy; |
|
77
|
|
|
|
|
78
|
|
|
var $weekWorkLoad; // Used to store workload details of a projet |
|
79
|
|
|
var $weekWorkLoadPerTask; // Used to store workload details of tasks of a projet |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* @var int Creation date |
|
83
|
|
|
* @deprecated |
|
84
|
|
|
* @see date_c |
|
85
|
|
|
*/ |
|
86
|
|
|
public $datec; |
|
87
|
|
|
/** |
|
88
|
|
|
* @var int Creation date |
|
89
|
|
|
*/ |
|
90
|
|
|
public $date_c; |
|
91
|
|
|
/** |
|
92
|
|
|
* @var int Modification date |
|
93
|
|
|
* @deprecated |
|
94
|
|
|
* @see date_m |
|
95
|
|
|
*/ |
|
96
|
|
|
public $datem; |
|
97
|
|
|
/** |
|
98
|
|
|
* @var int Modification date |
|
99
|
|
|
*/ |
|
100
|
|
|
public $date_m; |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* @var Task[] |
|
104
|
|
|
*/ |
|
105
|
|
|
public $lines; |
|
106
|
|
|
|
|
107
|
|
|
/** |
|
108
|
|
|
* Draft status |
|
109
|
|
|
*/ |
|
110
|
|
|
const STATUS_DRAFT = 0; |
|
111
|
|
|
/** |
|
112
|
|
|
* Open/Validated status |
|
113
|
|
|
*/ |
|
114
|
|
|
const STATUS_VALIDATED = 1; |
|
115
|
|
|
/** |
|
116
|
|
|
* Closed status |
|
117
|
|
|
*/ |
|
118
|
|
|
const STATUS_CLOSED = 2; |
|
119
|
|
|
|
|
120
|
|
|
|
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* Constructor |
|
124
|
|
|
* |
|
125
|
|
|
* @param DoliDB $db Database handler |
|
126
|
|
|
*/ |
|
127
|
|
|
function __construct($db) |
|
128
|
|
|
{ |
|
129
|
|
|
$this->db = $db; |
|
130
|
|
|
|
|
131
|
|
|
$this->statuts_short = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed'); |
|
132
|
|
|
$this->statuts_long = array(0 => 'Draft', 1 => 'Opened', 2 => 'Closed'); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Create a project into database |
|
137
|
|
|
* |
|
138
|
|
|
* @param User $user User making creation |
|
139
|
|
|
* @param int $notrigger Disable triggers |
|
140
|
|
|
* @return int <0 if KO, id of created project if OK |
|
141
|
|
|
*/ |
|
142
|
|
|
function create($user, $notrigger=0) |
|
143
|
|
|
{ |
|
144
|
|
|
global $conf, $langs; |
|
145
|
|
|
|
|
146
|
|
|
$error = 0; |
|
147
|
|
|
$ret = 0; |
|
148
|
|
|
|
|
149
|
|
|
$now=dol_now(); |
|
150
|
|
|
|
|
151
|
|
|
// Check parameters |
|
152
|
|
|
if (!trim($this->ref)) |
|
153
|
|
|
{ |
|
154
|
|
|
$this->error = 'ErrorFieldsRequired'; |
|
155
|
|
|
dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR); |
|
156
|
|
|
return -1; |
|
157
|
|
|
} |
|
158
|
|
|
if (! empty($conf->global->PROJECT_THIRDPARTY_REQUIRED) && ! $this->socid > 0) |
|
159
|
|
|
{ |
|
160
|
|
|
$this->error = 'ErrorFieldsRequired'; |
|
161
|
|
|
dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR); |
|
162
|
|
|
return -1; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
$this->db->begin(); |
|
166
|
|
|
|
|
167
|
|
|
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet ("; |
|
168
|
|
|
$sql.= "ref"; |
|
169
|
|
|
$sql.= ", title"; |
|
170
|
|
|
$sql.= ", description"; |
|
171
|
|
|
$sql.= ", fk_soc"; |
|
172
|
|
|
$sql.= ", fk_user_creat"; |
|
173
|
|
|
$sql.= ", fk_statut"; |
|
174
|
|
|
$sql.= ", fk_opp_status"; |
|
175
|
|
|
$sql.= ", opp_percent"; |
|
176
|
|
|
$sql.= ", public"; |
|
177
|
|
|
$sql.= ", datec"; |
|
178
|
|
|
$sql.= ", dateo"; |
|
179
|
|
|
$sql.= ", datee"; |
|
180
|
|
|
$sql.= ", opp_amount"; |
|
181
|
|
|
$sql.= ", budget_amount"; |
|
182
|
|
|
$sql.= ", bill_time"; |
|
183
|
|
|
$sql.= ", entity"; |
|
184
|
|
|
$sql.= ") VALUES ("; |
|
185
|
|
|
$sql.= "'" . $this->db->escape($this->ref) . "'"; |
|
186
|
|
|
$sql.= ", '" . $this->db->escape($this->title) . "'"; |
|
187
|
|
|
$sql.= ", '" . $this->db->escape($this->description) . "'"; |
|
188
|
|
|
$sql.= ", " . ($this->socid > 0 ? $this->socid : "null"); |
|
189
|
|
|
$sql.= ", " . $user->id; |
|
190
|
|
|
$sql.= ", ".(is_numeric($this->statut) ? $this->statut : '0'); |
|
191
|
|
|
$sql.= ", ".(is_numeric($this->opp_status) ? $this->opp_status : 'NULL'); |
|
192
|
|
|
$sql.= ", ".(is_numeric($this->opp_percent) ? $this->opp_percent : 'NULL'); |
|
193
|
|
|
$sql.= ", " . ($this->public ? 1 : 0); |
|
194
|
|
|
$sql.= ", '".$this->db->idate($now)."'"; |
|
195
|
|
|
$sql.= ", " . ($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null'); |
|
196
|
|
|
$sql.= ", " . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null'); |
|
197
|
|
|
$sql.= ", " . (strcmp($this->opp_amount,'') ? price2num($this->opp_amount) : 'null'); |
|
198
|
|
|
$sql.= ", " . (strcmp($this->budget_amount,'') ? price2num($this->budget_amount) : 'null'); |
|
199
|
|
|
$sql.= ", " . ($this->bill_time ? 1 : 0); |
|
200
|
|
|
$sql.= ", ".$conf->entity; |
|
201
|
|
|
$sql.= ")"; |
|
202
|
|
|
|
|
203
|
|
|
dol_syslog(get_class($this)."::create", LOG_DEBUG); |
|
204
|
|
|
$resql = $this->db->query($sql); |
|
205
|
|
|
if ($resql) |
|
206
|
|
|
{ |
|
207
|
|
|
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "projet"); |
|
208
|
|
|
$ret = $this->id; |
|
209
|
|
|
|
|
210
|
|
|
if (!$notrigger) |
|
211
|
|
|
{ |
|
212
|
|
|
// Call trigger |
|
213
|
|
|
$result=$this->call_trigger('PROJECT_CREATE',$user); |
|
214
|
|
|
if ($result < 0) { $error++; } |
|
215
|
|
|
// End call triggers |
|
216
|
|
|
} |
|
217
|
|
|
} |
|
218
|
|
|
else |
|
219
|
|
|
{ |
|
220
|
|
|
$this->error = $this->db->lasterror(); |
|
221
|
|
|
$this->errno = $this->db->lasterrno(); |
|
222
|
|
|
$error++; |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
// Update extrafield |
|
226
|
|
|
if (! $error) { |
|
227
|
|
|
if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used |
|
228
|
|
|
{ |
|
229
|
|
|
$result=$this->insertExtraFields(); |
|
230
|
|
|
if ($result < 0) |
|
231
|
|
|
{ |
|
232
|
|
|
$error++; |
|
233
|
|
|
} |
|
234
|
|
|
} |
|
235
|
|
|
} |
|
236
|
|
|
|
|
237
|
|
|
if (! $error && !empty($conf->global->MAIN_DISABLEDRAFTSTATUS)) |
|
238
|
|
|
{ |
|
239
|
|
|
$res = $this->setValid($user); |
|
240
|
|
|
if ($res < 0) $error++; |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
if (! $error) |
|
244
|
|
|
{ |
|
245
|
|
|
$this->db->commit(); |
|
246
|
|
|
return $ret; |
|
247
|
|
|
} |
|
248
|
|
|
else |
|
249
|
|
|
{ |
|
250
|
|
|
$this->db->rollback(); |
|
251
|
|
|
return -1; |
|
252
|
|
|
} |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
/** |
|
256
|
|
|
* Update a project |
|
257
|
|
|
* |
|
258
|
|
|
* @param User $user User object of making update |
|
259
|
|
|
* @param int $notrigger 1=Disable all triggers |
|
260
|
|
|
* @return int <=0 if KO, >0 if OK |
|
261
|
|
|
*/ |
|
262
|
|
|
function update($user, $notrigger=0) |
|
263
|
|
|
{ |
|
264
|
|
|
global $langs, $conf; |
|
265
|
|
|
|
|
266
|
|
|
$error=0; |
|
267
|
|
|
|
|
268
|
|
|
// Clean parameters |
|
269
|
|
|
$this->title = trim($this->title); |
|
270
|
|
|
$this->description = trim($this->description); |
|
271
|
|
|
if ($this->opp_amount < 0) $this->opp_amount=''; |
|
272
|
|
|
if ($this->opp_percent < 0) $this->opp_percent=''; |
|
273
|
|
|
if ($this->date_end && $this->date_end < $this->date_start) |
|
274
|
|
|
{ |
|
275
|
|
|
$this->error = $langs->trans("ErrorDateEndLowerThanDateStart"); |
|
276
|
|
|
$this->errors[] = $this->error; |
|
277
|
|
|
$this->db->rollback(); |
|
278
|
|
|
dol_syslog(get_class($this)."::update error -3 " . $this->error, LOG_ERR); |
|
279
|
|
|
return -3; |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
if (dol_strlen(trim($this->ref)) > 0) |
|
283
|
|
|
{ |
|
284
|
|
|
$this->db->begin(); |
|
285
|
|
|
|
|
286
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet SET"; |
|
287
|
|
|
$sql.= " ref='" . $this->db->escape($this->ref) . "'"; |
|
288
|
|
|
$sql.= ", title = '" . $this->db->escape($this->title) . "'"; |
|
289
|
|
|
$sql.= ", description = '" . $this->db->escape($this->description) . "'"; |
|
290
|
|
|
$sql.= ", fk_soc = " . ($this->socid > 0 ? $this->socid : "null"); |
|
291
|
|
|
$sql.= ", fk_statut = " . $this->statut; |
|
292
|
|
|
$sql.= ", fk_opp_status = " . ((is_numeric($this->opp_status) && $this->opp_status > 0) ? $this->opp_status : 'null'); |
|
293
|
|
|
$sql.= ", opp_percent = " . ((is_numeric($this->opp_percent) && $this->opp_percent != '') ? $this->opp_percent : 'null'); |
|
294
|
|
|
$sql.= ", public = " . ($this->public ? 1 : 0); |
|
295
|
|
|
$sql.= ", datec=" . ($this->date_c != '' ? "'".$this->db->idate($this->date_c)."'" : 'null'); |
|
296
|
|
|
$sql.= ", dateo=" . ($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : 'null'); |
|
297
|
|
|
$sql.= ", datee=" . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null'); |
|
298
|
|
|
$sql.= ", date_close=" . ($this->date_close != '' ? "'".$this->db->idate($this->date_close)."'" : 'null'); |
|
299
|
|
|
$sql.= ", fk_user_close=" . ($this->fk_user_close > 0 ? $this->fk_user_close : "null"); |
|
300
|
|
|
$sql.= ", opp_amount = " . (strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null"); |
|
301
|
|
|
$sql.= ", budget_amount = " . (strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null"); |
|
302
|
|
|
$sql.= ", fk_user_modif = " . $user->id; |
|
303
|
|
|
$sql.= ", bill_time = " . ($this->bill_time ? 1 : 0); |
|
304
|
|
|
$sql.= " WHERE rowid = " . $this->id; |
|
305
|
|
|
|
|
306
|
|
|
dol_syslog(get_class($this)."::update", LOG_DEBUG); |
|
307
|
|
|
$resql=$this->db->query($sql); |
|
308
|
|
|
if ($resql) |
|
309
|
|
|
{ |
|
310
|
|
|
// Update extrafield |
|
311
|
|
|
if (! $error) |
|
312
|
|
|
{ |
|
313
|
|
|
if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used |
|
314
|
|
|
{ |
|
315
|
|
|
$result=$this->insertExtraFields(); |
|
316
|
|
|
if ($result < 0) |
|
317
|
|
|
{ |
|
318
|
|
|
$error++; |
|
319
|
|
|
} |
|
320
|
|
|
} |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
if (! $error && ! $notrigger) |
|
324
|
|
|
{ |
|
325
|
|
|
// Call trigger |
|
326
|
|
|
$result=$this->call_trigger('PROJECT_MODIFY',$user); |
|
327
|
|
|
if ($result < 0) { $error++; } |
|
328
|
|
|
// End call triggers |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
if (! $error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) |
|
332
|
|
|
{ |
|
333
|
|
|
// We remove directory |
|
334
|
|
|
if ($conf->projet->dir_output) |
|
335
|
|
|
{ |
|
336
|
|
|
$olddir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($this->oldcopy->ref); |
|
337
|
|
|
$newdir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($this->ref); |
|
338
|
|
|
if (file_exists($olddir)) |
|
339
|
|
|
{ |
|
340
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; |
|
341
|
|
|
$res=dol_move($olddir, $newdir); |
|
342
|
|
|
if (! $res) |
|
343
|
|
|
{ |
|
344
|
|
|
$langs->load("errors"); |
|
345
|
|
|
$this->error=$langs->trans('ErrorFailToRenameDir',$olddir,$newdir); |
|
346
|
|
|
$error++; |
|
347
|
|
|
} |
|
348
|
|
|
} |
|
349
|
|
|
} |
|
350
|
|
|
} |
|
351
|
|
|
if (! $error ) |
|
352
|
|
|
{ |
|
353
|
|
|
$this->db->commit(); |
|
354
|
|
|
$result = 1; |
|
355
|
|
|
} |
|
356
|
|
|
else |
|
357
|
|
|
{ |
|
358
|
|
|
$this->db->rollback(); |
|
359
|
|
|
$result = -1; |
|
360
|
|
|
} |
|
361
|
|
|
} |
|
362
|
|
|
else |
|
363
|
|
|
{ |
|
364
|
|
|
$this->error = $this->db->lasterror(); |
|
365
|
|
|
$this->errors[] = $this->error; |
|
366
|
|
|
$this->db->rollback(); |
|
367
|
|
|
if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') |
|
368
|
|
|
{ |
|
369
|
|
|
$result = -4; |
|
370
|
|
|
} |
|
371
|
|
|
else |
|
372
|
|
|
{ |
|
373
|
|
|
$result = -2; |
|
374
|
|
|
} |
|
375
|
|
|
dol_syslog(get_class($this)."::update error " . $result . " " . $this->error, LOG_ERR); |
|
376
|
|
|
} |
|
377
|
|
|
} |
|
378
|
|
|
else |
|
379
|
|
|
{ |
|
380
|
|
|
dol_syslog(get_class($this)."::update ref null"); |
|
381
|
|
|
$result = -1; |
|
382
|
|
|
} |
|
383
|
|
|
|
|
384
|
|
|
return $result; |
|
385
|
|
|
} |
|
386
|
|
|
|
|
387
|
|
|
/** |
|
388
|
|
|
* Get object from database |
|
389
|
|
|
* |
|
390
|
|
|
* @param int $id Id of object to load |
|
391
|
|
|
* @param string $ref Ref of project |
|
392
|
|
|
* @return int >0 if OK, 0 if not found, <0 if KO |
|
393
|
|
|
*/ |
|
394
|
|
|
function fetch($id, $ref='') |
|
395
|
|
|
{ |
|
396
|
|
|
global $conf; |
|
397
|
|
|
|
|
398
|
|
|
if (empty($id) && empty($ref)) return -1; |
|
399
|
|
|
|
|
400
|
|
|
$sql = "SELECT rowid, ref, title, description, public, datec, opp_amount, budget_amount,"; |
|
401
|
|
|
$sql.= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent,"; |
|
402
|
|
|
$sql.= " note_private, note_public, model_pdf, bill_time"; |
|
403
|
|
|
$sql.= " FROM " . MAIN_DB_PREFIX . "projet"; |
|
404
|
|
|
if (! empty($id)) |
|
405
|
|
|
{ |
|
406
|
|
|
$sql.= " WHERE rowid=".$id; |
|
407
|
|
|
} |
|
408
|
|
|
else if (! empty($ref)) |
|
409
|
|
|
{ |
|
410
|
|
|
$sql.= " WHERE ref='".$this->db->escape($ref)."'"; |
|
411
|
|
|
$sql.= " AND entity IN (".getEntity('project').")"; |
|
412
|
|
|
} |
|
413
|
|
|
|
|
414
|
|
|
dol_syslog(get_class($this)."::fetch", LOG_DEBUG); |
|
415
|
|
|
$resql = $this->db->query($sql); |
|
416
|
|
|
if ($resql) |
|
417
|
|
|
{ |
|
418
|
|
|
$num_rows = $this->db->num_rows($resql); |
|
419
|
|
|
|
|
420
|
|
|
if ($num_rows) |
|
421
|
|
|
{ |
|
422
|
|
|
$obj = $this->db->fetch_object($resql); |
|
423
|
|
|
|
|
424
|
|
|
$this->id = $obj->rowid; |
|
425
|
|
|
$this->ref = $obj->ref; |
|
426
|
|
|
$this->title = $obj->title; |
|
427
|
|
|
$this->titre = $obj->title; // TODO deprecated |
|
428
|
|
|
$this->description = $obj->description; |
|
429
|
|
|
$this->date_c = $this->db->jdate($obj->datec); |
|
|
|
|
|
|
430
|
|
|
$this->datec = $this->db->jdate($obj->datec); // TODO deprecated |
|
|
|
|
|
|
431
|
|
|
$this->date_m = $this->db->jdate($obj->tms); |
|
|
|
|
|
|
432
|
|
|
$this->datem = $this->db->jdate($obj->tms); // TODO deprecated |
|
|
|
|
|
|
433
|
|
|
$this->date_start = $this->db->jdate($obj->dateo); |
|
434
|
|
|
$this->date_end = $this->db->jdate($obj->datee); |
|
435
|
|
|
$this->date_close = $this->db->jdate($obj->date_close); |
|
436
|
|
|
$this->note_private = $obj->note_private; |
|
437
|
|
|
$this->note_public = $obj->note_public; |
|
438
|
|
|
$this->socid = $obj->fk_soc; |
|
439
|
|
|
$this->user_author_id = $obj->fk_user_creat; |
|
440
|
|
|
$this->user_modification_id = $obj->fk_user_modif; |
|
441
|
|
|
$this->user_close_id = $obj->fk_user_close; |
|
442
|
|
|
$this->public = $obj->public; |
|
443
|
|
|
$this->statut = $obj->fk_statut; |
|
444
|
|
|
$this->opp_status = $obj->fk_opp_status; |
|
445
|
|
|
$this->opp_amount = $obj->opp_amount; |
|
446
|
|
|
$this->opp_percent = $obj->opp_percent; |
|
447
|
|
|
$this->budget_amount = $obj->budget_amount; |
|
448
|
|
|
$this->modelpdf = $obj->model_pdf; |
|
449
|
|
|
$this->bill_time = (int) $obj->bill_time; |
|
450
|
|
|
|
|
451
|
|
|
$this->db->free($resql); |
|
452
|
|
|
|
|
453
|
|
|
// Retreive all extrafield |
|
454
|
|
|
// fetch optionals attributes and labels |
|
455
|
|
|
$this->fetch_optionals(); |
|
456
|
|
|
|
|
457
|
|
|
return 1; |
|
458
|
|
|
} |
|
459
|
|
|
|
|
460
|
|
|
$this->db->free($resql); |
|
461
|
|
|
|
|
462
|
|
|
return 0; |
|
463
|
|
|
} |
|
464
|
|
|
else |
|
465
|
|
|
{ |
|
466
|
|
|
$this->error = $this->db->lasterror(); |
|
467
|
|
|
return -1; |
|
468
|
|
|
} |
|
469
|
|
|
} |
|
470
|
|
|
|
|
471
|
|
|
/** |
|
472
|
|
|
* Return list of projects |
|
473
|
|
|
* |
|
474
|
|
|
* @param int $socid To filter on a particular third party |
|
475
|
|
|
* @return array List of projects |
|
476
|
|
|
*/ |
|
477
|
|
|
function liste_array($socid='') |
|
478
|
|
|
{ |
|
479
|
|
|
global $conf; |
|
480
|
|
|
|
|
481
|
|
|
$projects = array(); |
|
482
|
|
|
|
|
483
|
|
|
$sql = "SELECT rowid, title"; |
|
484
|
|
|
$sql.= " FROM " . MAIN_DB_PREFIX . "projet"; |
|
485
|
|
|
$sql.= " WHERE entity = " . $conf->entity; |
|
486
|
|
|
if (! empty($socid)) $sql.= " AND fk_soc = " . $socid; |
|
487
|
|
|
|
|
488
|
|
|
$resql = $this->db->query($sql); |
|
489
|
|
|
if ($resql) |
|
490
|
|
|
{ |
|
491
|
|
|
$nump = $this->db->num_rows($resql); |
|
492
|
|
|
|
|
493
|
|
|
if ($nump) |
|
494
|
|
|
{ |
|
495
|
|
|
$i = 0; |
|
496
|
|
|
while ($i < $nump) |
|
497
|
|
|
{ |
|
498
|
|
|
$obj = $this->db->fetch_object($resql); |
|
499
|
|
|
|
|
500
|
|
|
$projects[$obj->rowid] = $obj->title; |
|
501
|
|
|
$i++; |
|
502
|
|
|
} |
|
503
|
|
|
} |
|
504
|
|
|
return $projects; |
|
505
|
|
|
} |
|
506
|
|
|
else |
|
507
|
|
|
{ |
|
508
|
|
|
print $this->db->lasterror(); |
|
509
|
|
|
} |
|
510
|
|
|
} |
|
511
|
|
|
|
|
512
|
|
|
/** |
|
513
|
|
|
* Return list of elements for type, linked to a project |
|
514
|
|
|
* |
|
515
|
|
|
* @param string $type 'propal','order','invoice','order_supplier','invoice_supplier',... |
|
516
|
|
|
* @param string $tablename name of table associated of the type |
|
517
|
|
|
* @param string $datefieldname name of date field for filter |
|
518
|
|
|
* @param int $dates Start date |
|
519
|
|
|
* @param int $datee End date |
|
520
|
|
|
* @param string $projectkey Equivalent key to fk_projet for actual type |
|
521
|
|
|
* @return mixed Array list of object ids linked to project, < 0 or string if error |
|
522
|
|
|
*/ |
|
523
|
|
|
function get_element_list($type, $tablename, $datefieldname='', $dates='', $datee='', $projectkey='fk_projet') |
|
524
|
|
|
{ |
|
525
|
|
|
$elements = array(); |
|
526
|
|
|
|
|
527
|
|
|
if ($this->id <= 0) return $elements; |
|
528
|
|
|
|
|
529
|
|
|
$ids = $this->id; |
|
530
|
|
|
|
|
531
|
|
|
if ($type == 'agenda') |
|
532
|
|
|
{ |
|
533
|
|
|
$sql = "SELECT id as rowid FROM " . MAIN_DB_PREFIX . "actioncomm WHERE fk_project IN (". $ids .")"; |
|
534
|
|
|
} |
|
535
|
|
|
elseif ($type == 'expensereport') |
|
536
|
|
|
{ |
|
537
|
|
|
$sql = "SELECT ed.rowid FROM " . MAIN_DB_PREFIX . "expensereport as e, " . MAIN_DB_PREFIX . "expensereport_det as ed WHERE e.rowid = ed.fk_expensereport AND ed.fk_projet IN (". $ids .")"; |
|
538
|
|
|
} |
|
539
|
|
|
elseif ($type == 'project_task') |
|
540
|
|
|
{ |
|
541
|
|
|
$sql = "SELECT DISTINCT pt.rowid FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (". $ids .")"; |
|
542
|
|
|
} |
|
543
|
|
|
elseif ($type == 'project_task_time') // Case we want to duplicate line foreach user |
|
544
|
|
|
{ |
|
545
|
|
|
$sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet IN (". $ids .")"; |
|
546
|
|
|
} |
|
547
|
|
|
elseif ($type == 'stock_mouvement') |
|
548
|
|
|
{ |
|
549
|
|
|
$sql = 'SELECT ms.rowid, ms.fk_user_author as fk_user FROM ' . MAIN_DB_PREFIX . "stock_mouvement as ms WHERE ms.origintype = 'project' AND ms.fk_origin IN (". $ids .") AND ms.type_mouvement = 1"; |
|
550
|
|
|
} |
|
551
|
|
|
else |
|
552
|
|
|
{ |
|
553
|
|
|
$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $tablename." WHERE ".$projectkey." IN (". $ids .")"; |
|
554
|
|
|
} |
|
555
|
|
|
|
|
556
|
|
|
if ($dates > 0) |
|
557
|
|
|
{ |
|
558
|
|
|
if (empty($datefieldname) && ! empty($this->table_element_date)) $datefieldname=$this->table_element_date; |
|
559
|
|
|
if (empty($datefieldname)) return 'Error this object has no date field defined'; |
|
560
|
|
|
$sql.=" AND (".$datefieldname." >= '".$this->db->idate($dates)."' OR ".$datefieldname." IS NULL)"; |
|
561
|
|
|
} |
|
562
|
|
|
if ($datee > 0) |
|
563
|
|
|
{ |
|
564
|
|
|
if (empty($datefieldname) && ! empty($this->table_element_date)) $datefieldname=$this->table_element_date; |
|
565
|
|
|
if (empty($datefieldname)) return 'Error this object has no date field defined'; |
|
566
|
|
|
$sql.=" AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)"; |
|
567
|
|
|
} |
|
568
|
|
|
if (! $sql) return -1; |
|
569
|
|
|
|
|
570
|
|
|
//print $sql; |
|
571
|
|
|
dol_syslog(get_class($this)."::get_element_list", LOG_DEBUG); |
|
572
|
|
|
$result = $this->db->query($sql); |
|
573
|
|
|
if ($result) |
|
574
|
|
|
{ |
|
575
|
|
|
$nump = $this->db->num_rows($result); |
|
576
|
|
|
if ($nump) |
|
577
|
|
|
{ |
|
578
|
|
|
$i = 0; |
|
579
|
|
|
while ($i < $nump) |
|
580
|
|
|
{ |
|
581
|
|
|
$obj = $this->db->fetch_object($result); |
|
582
|
|
|
|
|
583
|
|
|
$elements[$i] = $obj->rowid.(empty($obj->fk_user)?'':'_'.$obj->fk_user); |
|
584
|
|
|
|
|
585
|
|
|
$i++; |
|
586
|
|
|
} |
|
587
|
|
|
$this->db->free($result); |
|
588
|
|
|
} |
|
589
|
|
|
|
|
590
|
|
|
/* Return array even if empty*/ |
|
591
|
|
|
return $elements; |
|
592
|
|
|
} |
|
593
|
|
|
else |
|
594
|
|
|
{ |
|
595
|
|
|
dol_print_error($this->db); |
|
596
|
|
|
} |
|
597
|
|
|
} |
|
598
|
|
|
|
|
599
|
|
|
/** |
|
600
|
|
|
* Delete a project from database |
|
601
|
|
|
* |
|
602
|
|
|
* @param User $user User |
|
603
|
|
|
* @param int $notrigger Disable triggers |
|
604
|
|
|
* @return int <0 if KO, 0 if not possible, >0 if OK |
|
605
|
|
|
*/ |
|
606
|
|
|
function delete($user, $notrigger=0) |
|
607
|
|
|
{ |
|
608
|
|
|
global $langs, $conf; |
|
609
|
|
|
require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; |
|
610
|
|
|
|
|
611
|
|
|
$error = 0; |
|
612
|
|
|
|
|
613
|
|
|
$this->db->begin(); |
|
614
|
|
|
|
|
615
|
|
|
if (!$error) |
|
616
|
|
|
{ |
|
617
|
|
|
// Delete linked contacts |
|
618
|
|
|
$res = $this->delete_linked_contact(); |
|
619
|
|
|
if ($res < 0) |
|
620
|
|
|
{ |
|
621
|
|
|
$this->error = 'ErrorFailToDeleteLinkedContact'; |
|
622
|
|
|
//$error++; |
|
623
|
|
|
$this->db->rollback(); |
|
624
|
|
|
return 0; |
|
625
|
|
|
} |
|
626
|
|
|
} |
|
627
|
|
|
|
|
628
|
|
|
// Set fk_projet into elements to null |
|
629
|
|
|
$listoftables=array( |
|
630
|
|
|
'facture'=>'fk_projet','propal'=>'fk_projet','commande'=>'fk_projet', |
|
631
|
|
|
'facture_fourn'=>'fk_projet','commande_fournisseur'=>'fk_projet','supplier_proposal'=>'fk_projet', |
|
632
|
|
|
'expensereport_det'=>'fk_projet','contrat'=>'fk_projet','fichinter'=>'fk_projet','don'=>'fk_projet' |
|
633
|
|
|
); |
|
634
|
|
|
foreach($listoftables as $key => $value) |
|
635
|
|
|
{ |
|
636
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . $key . " SET ".$value." = NULL where ".$value." = ". $this->id; |
|
637
|
|
|
$resql = $this->db->query($sql); |
|
638
|
|
|
if (!$resql) |
|
639
|
|
|
{ |
|
640
|
|
|
$this->errors[] = $this->db->lasterror(); |
|
641
|
|
|
$error++; |
|
642
|
|
|
break; |
|
643
|
|
|
} |
|
644
|
|
|
} |
|
645
|
|
|
|
|
646
|
|
|
// Fetch tasks |
|
647
|
|
|
$this->getLinesArray($user); |
|
648
|
|
|
|
|
649
|
|
|
// Delete tasks |
|
650
|
|
|
$ret = $this->deleteTasks($user); |
|
651
|
|
|
if ($ret < 0) $error++; |
|
652
|
|
|
|
|
653
|
|
|
// Delete project |
|
654
|
|
|
if (! $error) |
|
655
|
|
|
{ |
|
656
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet"; |
|
657
|
|
|
$sql.= " WHERE rowid=" . $this->id; |
|
658
|
|
|
|
|
659
|
|
|
$resql = $this->db->query($sql); |
|
660
|
|
|
if (!$resql) |
|
661
|
|
|
{ |
|
662
|
|
|
$this->errors[] = $langs->trans("CantRemoveProject"); |
|
663
|
|
|
$error++; |
|
664
|
|
|
} |
|
665
|
|
|
} |
|
666
|
|
|
|
|
667
|
|
|
if (! $error) |
|
668
|
|
|
{ |
|
669
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_extrafields"; |
|
670
|
|
|
$sql.= " WHERE fk_object=" . $this->id; |
|
671
|
|
|
|
|
672
|
|
|
$resql = $this->db->query($sql); |
|
673
|
|
|
if (! $resql) |
|
674
|
|
|
{ |
|
675
|
|
|
$this->errors[] = $this->db->lasterror(); |
|
676
|
|
|
$error++; |
|
677
|
|
|
} |
|
678
|
|
|
} |
|
679
|
|
|
|
|
680
|
|
|
if (empty($error)) |
|
681
|
|
|
{ |
|
682
|
|
|
// We remove directory |
|
683
|
|
|
$projectref = dol_sanitizeFileName($this->ref); |
|
684
|
|
|
if ($conf->projet->dir_output) |
|
685
|
|
|
{ |
|
686
|
|
|
$dir = $conf->projet->dir_output . "/" . $projectref; |
|
687
|
|
|
if (file_exists($dir)) |
|
688
|
|
|
{ |
|
689
|
|
|
$res = @dol_delete_dir_recursive($dir); |
|
690
|
|
|
if (!$res) |
|
691
|
|
|
{ |
|
692
|
|
|
$this->errors[] = 'ErrorFailToDeleteDir'; |
|
693
|
|
|
$error++; |
|
694
|
|
|
} |
|
695
|
|
|
} |
|
696
|
|
|
} |
|
697
|
|
|
|
|
698
|
|
|
if (!$notrigger) |
|
699
|
|
|
{ |
|
700
|
|
|
// Call trigger |
|
701
|
|
|
$result=$this->call_trigger('PROJECT_DELETE',$user); |
|
702
|
|
|
|
|
703
|
|
|
if ($result < 0) { |
|
704
|
|
|
$error++; |
|
705
|
|
|
} |
|
706
|
|
|
// End call triggers |
|
707
|
|
|
} |
|
708
|
|
|
} |
|
709
|
|
|
|
|
710
|
|
|
if (empty($error)) |
|
711
|
|
|
{ |
|
712
|
|
|
$this->db->commit(); |
|
713
|
|
|
return 1; |
|
714
|
|
|
} |
|
715
|
|
|
else |
|
716
|
|
|
{ |
|
717
|
|
|
foreach ( $this->errors as $errmsg ) |
|
718
|
|
|
{ |
|
719
|
|
|
dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR); |
|
720
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
|
721
|
|
|
} |
|
722
|
|
|
dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR); |
|
723
|
|
|
$this->db->rollback(); |
|
724
|
|
|
return -1; |
|
725
|
|
|
} |
|
726
|
|
|
} |
|
727
|
|
|
|
|
728
|
|
|
/** |
|
729
|
|
|
* Delete tasks with no children first, then task with children recursively |
|
730
|
|
|
* |
|
731
|
|
|
* @param User $user User |
|
732
|
|
|
* @return int <0 if KO, 1 if OK |
|
733
|
|
|
*/ |
|
734
|
|
|
function deleteTasks($user) |
|
735
|
|
|
{ |
|
736
|
|
|
$countTasks = count($this->lines); |
|
737
|
|
|
$deleted = false; |
|
738
|
|
|
if ($countTasks) |
|
739
|
|
|
{ |
|
740
|
|
|
foreach($this->lines as $task) |
|
741
|
|
|
{ |
|
742
|
|
|
if ($task->hasChildren() <= 0) { // If there is no children (or error to detect them) |
|
743
|
|
|
$deleted = true; |
|
744
|
|
|
$ret = $task->delete($user); |
|
745
|
|
|
if ($ret <= 0) |
|
746
|
|
|
{ |
|
747
|
|
|
$this->errors[] = $this->db->lasterror(); |
|
748
|
|
|
return -1; |
|
749
|
|
|
} |
|
750
|
|
|
} |
|
751
|
|
|
} |
|
752
|
|
|
} |
|
753
|
|
|
$this->getLinesArray($user); |
|
754
|
|
|
if ($deleted && count($this->lines) < $countTasks) |
|
755
|
|
|
{ |
|
756
|
|
|
if (count($this->lines)) $this->deleteTasks($this->lines); |
|
757
|
|
|
} |
|
758
|
|
|
|
|
759
|
|
|
return 1; |
|
760
|
|
|
} |
|
761
|
|
|
|
|
762
|
|
|
/** |
|
763
|
|
|
* Validate a project |
|
764
|
|
|
* |
|
765
|
|
|
* @param User $user User that validate |
|
766
|
|
|
* @param int $notrigger 1=Disable triggers |
|
767
|
|
|
* @return int <0 if KO, >0 if OK |
|
768
|
|
|
*/ |
|
769
|
|
|
function setValid($user, $notrigger=0) |
|
770
|
|
|
{ |
|
771
|
|
|
global $langs, $conf; |
|
772
|
|
|
|
|
773
|
|
|
$error=0; |
|
774
|
|
|
|
|
775
|
|
|
if ($this->statut != 1) |
|
776
|
|
|
{ |
|
777
|
|
|
// Check parameters |
|
778
|
|
|
if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->title)) |
|
779
|
|
|
{ |
|
780
|
|
|
$this->error=$langs->trans("ErrorFieldFormat",$langs->transnoentities("Label")).'. '.$langs->trans('RemoveString',$langs->transnoentitiesnoconv("CopyOf")); |
|
781
|
|
|
return -1; |
|
782
|
|
|
} |
|
783
|
|
|
|
|
784
|
|
|
$this->db->begin(); |
|
785
|
|
|
|
|
786
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet"; |
|
787
|
|
|
$sql.= " SET fk_statut = 1"; |
|
788
|
|
|
$sql.= " WHERE rowid = " . $this->id; |
|
789
|
|
|
$sql.= " AND entity = " . $conf->entity; |
|
790
|
|
|
|
|
791
|
|
|
dol_syslog(get_class($this)."::setValid", LOG_DEBUG); |
|
792
|
|
|
$resql = $this->db->query($sql); |
|
793
|
|
|
if ($resql) |
|
794
|
|
|
{ |
|
795
|
|
|
// Call trigger |
|
796
|
|
|
if (empty($notrigger)) |
|
797
|
|
|
{ |
|
798
|
|
|
$result=$this->call_trigger('PROJECT_VALIDATE',$user); |
|
799
|
|
|
if ($result < 0) { $error++; } |
|
800
|
|
|
// End call triggers |
|
801
|
|
|
} |
|
802
|
|
|
|
|
803
|
|
|
if (!$error) |
|
804
|
|
|
{ |
|
805
|
|
|
$this->statut=1; |
|
806
|
|
|
$this->db->commit(); |
|
807
|
|
|
return 1; |
|
808
|
|
|
} |
|
809
|
|
|
else |
|
810
|
|
|
{ |
|
811
|
|
|
$this->db->rollback(); |
|
812
|
|
|
$this->error = join(',', $this->errors); |
|
813
|
|
|
dol_syslog(get_class($this)."::setValid " . $this->error, LOG_ERR); |
|
814
|
|
|
return -1; |
|
815
|
|
|
} |
|
816
|
|
|
} |
|
817
|
|
|
else |
|
818
|
|
|
{ |
|
819
|
|
|
$this->db->rollback(); |
|
820
|
|
|
$this->error = $this->db->lasterror(); |
|
821
|
|
|
return -1; |
|
822
|
|
|
} |
|
823
|
|
|
} |
|
824
|
|
|
} |
|
825
|
|
|
|
|
826
|
|
|
/** |
|
827
|
|
|
* Close a project |
|
828
|
|
|
* |
|
829
|
|
|
* @param User $user User that close project |
|
830
|
|
|
* @return int <0 if KO, 0 if already closed, >0 if OK |
|
831
|
|
|
*/ |
|
832
|
|
|
function setClose($user) |
|
833
|
|
|
{ |
|
834
|
|
|
global $langs, $conf; |
|
835
|
|
|
|
|
836
|
|
|
$now = dol_now(); |
|
837
|
|
|
|
|
838
|
|
|
$error=0; |
|
839
|
|
|
|
|
840
|
|
|
if ($this->statut != 2) |
|
841
|
|
|
{ |
|
842
|
|
|
$this->db->begin(); |
|
843
|
|
|
|
|
844
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet"; |
|
845
|
|
|
$sql.= " SET fk_statut = 2, fk_user_close = ".$user->id.", date_close = '".$this->db->idate($now)."'"; |
|
846
|
|
|
$sql.= " WHERE rowid = " . $this->id; |
|
847
|
|
|
$sql.= " AND entity = " . $conf->entity; |
|
848
|
|
|
$sql.= " AND fk_statut = 1"; |
|
849
|
|
|
|
|
850
|
|
|
if (! empty($conf->global->PROJECT_USE_OPPORTUNITIES)) |
|
851
|
|
|
{ |
|
852
|
|
|
// TODO What to do if fk_opp_status is not code 'WON' or 'LOST' |
|
853
|
|
|
} |
|
854
|
|
|
|
|
855
|
|
|
dol_syslog(get_class($this)."::setClose", LOG_DEBUG); |
|
856
|
|
|
$resql = $this->db->query($sql); |
|
857
|
|
|
if ($resql) |
|
858
|
|
|
{ |
|
859
|
|
|
// Call trigger |
|
860
|
|
|
$result=$this->call_trigger('PROJECT_CLOSE',$user); |
|
861
|
|
|
if ($result < 0) { $error++; } |
|
862
|
|
|
// End call triggers |
|
863
|
|
|
|
|
864
|
|
|
if (!$error) |
|
865
|
|
|
{ |
|
866
|
|
|
$this->statut = 2; |
|
867
|
|
|
$this->db->commit(); |
|
868
|
|
|
return 1; |
|
869
|
|
|
} |
|
870
|
|
|
else |
|
871
|
|
|
{ |
|
872
|
|
|
$this->db->rollback(); |
|
873
|
|
|
$this->error = join(',', $this->errors); |
|
874
|
|
|
dol_syslog(get_class($this)."::setClose " . $this->error, LOG_ERR); |
|
875
|
|
|
return -1; |
|
876
|
|
|
} |
|
877
|
|
|
} |
|
878
|
|
|
else |
|
879
|
|
|
{ |
|
880
|
|
|
$this->db->rollback(); |
|
881
|
|
|
$this->error = $this->db->lasterror(); |
|
882
|
|
|
return -1; |
|
883
|
|
|
} |
|
884
|
|
|
} |
|
885
|
|
|
|
|
886
|
|
|
return 0; |
|
887
|
|
|
} |
|
888
|
|
|
|
|
889
|
|
|
/** |
|
890
|
|
|
* Return status label of object |
|
891
|
|
|
* |
|
892
|
|
|
* @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto |
|
893
|
|
|
* @return string Label |
|
894
|
|
|
*/ |
|
895
|
|
|
function getLibStatut($mode=0) |
|
896
|
|
|
{ |
|
897
|
|
|
return $this->LibStatut($this->statut, $mode); |
|
898
|
|
|
} |
|
899
|
|
|
|
|
900
|
|
|
/** |
|
901
|
|
|
* Renvoi status label for a status |
|
902
|
|
|
* |
|
903
|
|
|
* @param int $statut id statut |
|
904
|
|
|
* @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto |
|
905
|
|
|
* @return string Label |
|
906
|
|
|
*/ |
|
907
|
|
|
function LibStatut($statut, $mode=0) |
|
908
|
|
|
{ |
|
909
|
|
|
global $langs; |
|
910
|
|
|
|
|
911
|
|
|
if ($mode == 0) |
|
912
|
|
|
{ |
|
913
|
|
|
return $langs->trans($this->statuts_long[$statut]); |
|
914
|
|
|
} |
|
915
|
|
|
if ($mode == 1) |
|
916
|
|
|
{ |
|
917
|
|
|
return $langs->trans($this->statuts_short[$statut]); |
|
918
|
|
|
} |
|
919
|
|
|
if ($mode == 2) |
|
920
|
|
|
{ |
|
921
|
|
|
if ($statut == 0) |
|
922
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0') . ' ' . $langs->trans($this->statuts_short[$statut]); |
|
923
|
|
|
if ($statut == 1) |
|
924
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4') . ' ' . $langs->trans($this->statuts_short[$statut]); |
|
925
|
|
|
if ($statut == 2) |
|
926
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6') . ' ' . $langs->trans($this->statuts_short[$statut]); |
|
927
|
|
|
} |
|
928
|
|
|
if ($mode == 3) |
|
929
|
|
|
{ |
|
930
|
|
|
if ($statut == 0) |
|
931
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0'); |
|
932
|
|
|
if ($statut == 1) |
|
933
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4'); |
|
934
|
|
|
if ($statut == 2) |
|
935
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6'); |
|
936
|
|
|
} |
|
937
|
|
|
if ($mode == 4) |
|
938
|
|
|
{ |
|
939
|
|
|
if ($statut == 0) |
|
940
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut0') . ' ' . $langs->trans($this->statuts_long[$statut]); |
|
941
|
|
|
if ($statut == 1) |
|
942
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut4') . ' ' . $langs->trans($this->statuts_long[$statut]); |
|
943
|
|
|
if ($statut == 2) |
|
944
|
|
|
return img_picto($langs->trans($this->statuts_long[$statut]), 'statut6') . ' ' . $langs->trans($this->statuts_long[$statut]); |
|
945
|
|
|
} |
|
946
|
|
|
if ($mode == 5) |
|
947
|
|
|
{ |
|
948
|
|
|
if ($statut == 0) |
|
949
|
|
|
return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut0'); |
|
950
|
|
|
if ($statut == 1) |
|
951
|
|
|
return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut4'); |
|
952
|
|
|
if ($statut == 2) |
|
953
|
|
|
return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_long[$statut]), 'statut6'); |
|
954
|
|
|
} |
|
955
|
|
|
} |
|
956
|
|
|
|
|
957
|
|
|
/** |
|
958
|
|
|
* Return clicable name (with picto eventually) |
|
959
|
|
|
* |
|
960
|
|
|
* @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto |
|
961
|
|
|
* @param string $option Variant ('', 'nolink') |
|
962
|
|
|
* @param int $addlabel 0=Default, 1=Add label into string, >1=Add first chars into string |
|
963
|
|
|
* @param string $moreinpopup Text to add into popup |
|
964
|
|
|
* @param string $sep Separator between ref and label if option addlabel is set |
|
965
|
|
|
* @param int $notooltip 1=Disable tooltip |
|
966
|
|
|
* @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking |
|
967
|
|
|
* @return string String with URL |
|
968
|
|
|
*/ |
|
969
|
|
|
function getNomUrl($withpicto=0, $option='', $addlabel=0, $moreinpopup='', $sep=' - ', $notooltip=0, $save_lastsearch_value=-1) |
|
970
|
|
|
{ |
|
971
|
|
|
global $conf, $langs, $user, $hookmanager; |
|
972
|
|
|
|
|
973
|
|
|
if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips |
|
974
|
|
|
|
|
975
|
|
|
$result = ''; |
|
976
|
|
|
|
|
977
|
|
|
$label=''; |
|
978
|
|
|
if ($option != 'nolink') $label = '<u>' . $langs->trans("ShowProject") . '</u>'; |
|
979
|
|
|
$label .= ($label?'<br>':'').'<b>' . $langs->trans('Ref') . ': </b>' . $this->ref; // The space must be after the : to not being explode when showing the title in img_picto |
|
980
|
|
|
$label .= ($label?'<br>':'').'<b>' . $langs->trans('Label') . ': </b>' . $this->title; // The space must be after the : to not being explode when showing the title in img_picto |
|
981
|
|
|
if (! empty($this->thirdparty_name)) |
|
982
|
|
|
$label .= ($label?'<br>':'').'<b>' . $langs->trans('ThirdParty') . ': </b>' . $this->thirdparty_name; // The space must be after the : to not being explode when showing the title in img_picto |
|
983
|
|
|
if (! empty($this->dateo)) |
|
984
|
|
|
$label .= ($label?'<br>':'').'<b>' . $langs->trans('DateStart') . ': </b>' . dol_print_date($this->dateo, 'day'); // The space must be after the : to not being explode when showing the title in img_picto |
|
985
|
|
|
if (! empty($this->datee)) |
|
986
|
|
|
$label .= ($label?'<br>':'').'<b>' . $langs->trans('DateEnd') . ': </b>' . dol_print_date($this->datee, 'day'); // The space must be after the : to not being explode when showing the title in img_picto |
|
987
|
|
|
if ($moreinpopup) $label.='<br>'.$moreinpopup; |
|
988
|
|
|
|
|
989
|
|
|
$url=''; |
|
990
|
|
|
if ($option != 'nolink') |
|
991
|
|
|
{ |
|
992
|
|
|
if (preg_match('/\.php$/',$option)) { |
|
993
|
|
|
$url = dol_buildpath($option,1) . '?id=' . $this->id; |
|
994
|
|
|
} |
|
995
|
|
|
else if ($option == 'task') |
|
996
|
|
|
{ |
|
997
|
|
|
$url = DOL_URL_ROOT . '/projet/tasks.php?id=' . $this->id; |
|
998
|
|
|
} |
|
999
|
|
|
else |
|
1000
|
|
|
{ |
|
1001
|
|
|
$url = DOL_URL_ROOT . '/projet/card.php?id=' . $this->id; |
|
1002
|
|
|
} |
|
1003
|
|
|
// Add param to save lastsearch_values or not |
|
1004
|
|
|
$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); |
|
1005
|
|
|
if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; |
|
1006
|
|
|
if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; |
|
1007
|
|
|
} |
|
1008
|
|
|
|
|
1009
|
|
|
$linkclose=''; |
|
1010
|
|
|
if (empty($notooltip) && $user->rights->projet->lire) |
|
1011
|
|
|
{ |
|
1012
|
|
|
if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) |
|
1013
|
|
|
{ |
|
1014
|
|
|
$label=$langs->trans("ShowProject"); |
|
1015
|
|
|
$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; |
|
1016
|
|
|
} |
|
1017
|
|
|
$linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; |
|
1018
|
|
|
$linkclose.=' class="classfortooltip"'; |
|
1019
|
|
|
|
|
1020
|
|
|
/* |
|
1021
|
|
|
$hookmanager->initHooks(array('projectdao')); |
|
1022
|
|
|
$parameters=array('id'=>$this->id); |
|
1023
|
|
|
// Note that $action and $object may have been modified by some hooks |
|
1024
|
|
|
$reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); |
|
1025
|
|
|
if ($reshook > 0) |
|
1026
|
|
|
$linkclose = $hookmanager->resPrint; |
|
1027
|
|
|
*/ |
|
1028
|
|
|
} |
|
1029
|
|
|
|
|
1030
|
|
|
$picto = 'projectpub'; |
|
1031
|
|
|
if (! $this->public) $picto = 'project'; |
|
1032
|
|
|
|
|
1033
|
|
|
$linkstart = '<a href="'.$url.'"'; |
|
1034
|
|
|
$linkstart.=$linkclose.'>'; |
|
1035
|
|
|
$linkend='</a>'; |
|
1036
|
|
|
|
|
1037
|
|
|
$result .= $linkstart; |
|
1038
|
|
|
if ($withpicto) $result.=img_object(($notooltip?'':$label), $picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); |
|
1039
|
|
|
if ($withpicto != 2) $result.= $this->ref; |
|
1040
|
|
|
$result .= $linkend; |
|
1041
|
|
|
if ($withpicto != 2) $result.=(($addlabel && $this->title) ? $sep . dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); |
|
1042
|
|
|
|
|
1043
|
|
|
global $action; |
|
1044
|
|
|
$hookmanager->initHooks(array('projectdao')); |
|
1045
|
|
|
$parameters=array('id'=>$this->id, 'getnomurl'=>$result); |
|
1046
|
|
|
$reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks |
|
1047
|
|
|
if ($reshook > 0) $result = $hookmanager->resPrint; |
|
1048
|
|
|
else $result .= $hookmanager->resPrint; |
|
1049
|
|
|
|
|
1050
|
|
|
return $result; |
|
1051
|
|
|
} |
|
1052
|
|
|
|
|
1053
|
|
|
/** |
|
1054
|
|
|
* Initialise an instance with random values. |
|
1055
|
|
|
* Used to build previews or test instances. |
|
1056
|
|
|
* id must be 0 if object instance is a specimen. |
|
1057
|
|
|
* |
|
1058
|
|
|
* @return void |
|
1059
|
|
|
*/ |
|
1060
|
|
|
function initAsSpecimen() |
|
1061
|
|
|
{ |
|
1062
|
|
|
global $user, $langs, $conf; |
|
1063
|
|
|
|
|
1064
|
|
|
$now=dol_now(); |
|
1065
|
|
|
|
|
1066
|
|
|
// Initialise parameters |
|
1067
|
|
|
$this->id = 0; |
|
1068
|
|
|
$this->ref = 'SPECIMEN'; |
|
1069
|
|
|
$this->specimen = 1; |
|
1070
|
|
|
$this->socid = 1; |
|
1071
|
|
|
$this->date_c = $now; |
|
1072
|
|
|
$this->date_m = $now; |
|
1073
|
|
|
$this->date_start = $now; |
|
1074
|
|
|
$this->date_end = $now + (3600 * 24 * 365); |
|
1075
|
|
|
$this->note_public = 'SPECIMEN'; |
|
1076
|
|
|
$this->fk_ele = 20000; |
|
1077
|
|
|
$this->opp_amount = 20000; |
|
1078
|
|
|
$this->budget_amount = 10000; |
|
1079
|
|
|
|
|
1080
|
|
|
/* |
|
1081
|
|
|
$nbp = mt_rand(1, 9); |
|
1082
|
|
|
$xnbp = 0; |
|
1083
|
|
|
while ($xnbp < $nbp) |
|
1084
|
|
|
{ |
|
1085
|
|
|
$line = new Task($this->db); |
|
1086
|
|
|
$line->fk_project = 0; |
|
1087
|
|
|
$line->label = $langs->trans("Label") . " " . $xnbp; |
|
1088
|
|
|
$line->description = $langs->trans("Description") . " " . $xnbp; |
|
1089
|
|
|
|
|
1090
|
|
|
$this->lines[]=$line; |
|
1091
|
|
|
$xnbp++; |
|
1092
|
|
|
} |
|
1093
|
|
|
*/ |
|
1094
|
|
|
} |
|
1095
|
|
|
|
|
1096
|
|
|
/** |
|
1097
|
|
|
* Check if user has permission on current project |
|
1098
|
|
|
* |
|
1099
|
|
|
* @param User $user Object user to evaluate |
|
1100
|
|
|
* @param string $mode Type of permission we want to know: 'read', 'write' |
|
1101
|
|
|
* @return int >0 if user has permission, <0 if user has no permission |
|
1102
|
|
|
*/ |
|
1103
|
|
|
function restrictedProjectArea($user, $mode='read') |
|
1104
|
|
|
{ |
|
1105
|
|
|
// To verify role of users |
|
1106
|
|
|
$userAccess = 0; |
|
1107
|
|
|
if (($mode == 'read' && ! empty($user->rights->projet->all->lire)) || ($mode == 'write' && ! empty($user->rights->projet->all->creer)) || ($mode == 'delete' && ! empty($user->rights->projet->all->supprimer))) |
|
1108
|
|
|
{ |
|
1109
|
|
|
$userAccess = 1; |
|
1110
|
|
|
} |
|
1111
|
|
|
else if ($this->public && (($mode == 'read' && ! empty($user->rights->projet->lire)) || ($mode == 'write' && ! empty($user->rights->projet->creer)) || ($mode == 'delete' && ! empty($user->rights->projet->supprimer)))) |
|
1112
|
|
|
{ |
|
1113
|
|
|
$userAccess = 1; |
|
1114
|
|
|
} |
|
1115
|
|
|
else |
|
1116
|
|
|
{ |
|
1117
|
|
|
foreach (array('internal', 'external') as $source) |
|
1118
|
|
|
{ |
|
1119
|
|
|
$userRole = $this->liste_contact(4, $source); |
|
1120
|
|
|
$num = count($userRole); |
|
1121
|
|
|
|
|
1122
|
|
|
$nblinks = 0; |
|
1123
|
|
|
while ($nblinks < $num) |
|
1124
|
|
|
{ |
|
1125
|
|
|
if ($source == 'internal' && preg_match('/^PROJECT/', $userRole[$nblinks]['code']) && $user->id == $userRole[$nblinks]['id']) |
|
1126
|
|
|
{ |
|
1127
|
|
|
if ($mode == 'read' && $user->rights->projet->lire) $userAccess++; |
|
1128
|
|
|
if ($mode == 'write' && $user->rights->projet->creer) $userAccess++; |
|
1129
|
|
|
if ($mode == 'delete' && $user->rights->projet->supprimer) $userAccess++; |
|
1130
|
|
|
} |
|
1131
|
|
|
$nblinks++; |
|
1132
|
|
|
} |
|
1133
|
|
|
} |
|
1134
|
|
|
//if (empty($nblinks)) // If nobody has permission, we grant creator |
|
1135
|
|
|
//{ |
|
1136
|
|
|
// if ((!empty($this->user_author_id) && $this->user_author_id == $user->id)) |
|
1137
|
|
|
// { |
|
1138
|
|
|
// $userAccess = 1; |
|
1139
|
|
|
// } |
|
1140
|
|
|
//} |
|
1141
|
|
|
} |
|
1142
|
|
|
|
|
1143
|
|
|
return ($userAccess?$userAccess:-1); |
|
1144
|
|
|
} |
|
1145
|
|
|
|
|
1146
|
|
|
/** |
|
1147
|
|
|
* Return array of projects a user has permission on, is affected to, or all projects |
|
1148
|
|
|
* |
|
1149
|
|
|
* @param User $user User object |
|
1150
|
|
|
* @param int $mode 0=All project I have permission on (assigned to me and public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts |
|
1151
|
|
|
* @param int $list 0=Return array, 1=Return string list |
|
1152
|
|
|
* @param int $socid 0=No filter on third party, id of third party |
|
1153
|
|
|
* @param string $filter additionnal filter on project (statut, ref, ...) |
|
1154
|
|
|
* @return array or string Array of projects id, or string with projects id separated with "," if list is 1 |
|
1155
|
|
|
*/ |
|
1156
|
|
|
function getProjectsAuthorizedForUser($user, $mode=0, $list=0, $socid=0, $filter='') |
|
1157
|
|
|
{ |
|
1158
|
|
|
$projects = array(); |
|
1159
|
|
|
$temp = array(); |
|
1160
|
|
|
|
|
1161
|
|
|
$sql = "SELECT ".(($mode == 0 || $mode == 1) ? "DISTINCT " : "")."p.rowid, p.ref"; |
|
1162
|
|
|
$sql.= " FROM " . MAIN_DB_PREFIX . "projet as p"; |
|
1163
|
|
|
if ($mode == 0 || $mode == 1) |
|
1164
|
|
|
{ |
|
1165
|
|
|
$sql.= ", " . MAIN_DB_PREFIX . "element_contact as ec"; |
|
1166
|
|
|
} |
|
1167
|
|
|
$sql.= " WHERE p.entity IN (".getEntity('project').")"; |
|
1168
|
|
|
// Internal users must see project he is contact to even if project linked to a third party he can't see. |
|
1169
|
|
|
//if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; |
|
1170
|
|
|
if ($socid > 0) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . $socid . ")"; |
|
1171
|
|
|
|
|
1172
|
|
|
// Get id of types of contacts for projects (This list never contains a lot of elements) |
|
1173
|
|
|
$listofprojectcontacttype=array(); |
|
1174
|
|
|
$sql2 = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc"; |
|
1175
|
|
|
$sql2.= " WHERE ctc.element = '" . $this->db->escape($this->element) . "'"; |
|
1176
|
|
|
$sql2.= " AND ctc.source = 'internal'"; |
|
1177
|
|
|
$resql = $this->db->query($sql2); |
|
1178
|
|
|
if ($resql) |
|
1179
|
|
|
{ |
|
1180
|
|
|
while($obj = $this->db->fetch_object($resql)) |
|
1181
|
|
|
{ |
|
1182
|
|
|
$listofprojectcontacttype[$obj->rowid]=$obj->code; |
|
1183
|
|
|
} |
|
1184
|
|
|
} |
|
1185
|
|
|
else dol_print_error($this->db); |
|
1186
|
|
|
if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0'; // To avoid syntax error if not found |
|
1187
|
|
|
|
|
1188
|
|
|
if ($mode == 0) |
|
1189
|
|
|
{ |
|
1190
|
|
|
$sql.= " AND ec.element_id = p.rowid"; |
|
1191
|
|
|
$sql.= " AND ( p.public = 1"; |
|
1192
|
|
|
$sql.= " OR ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; |
|
1193
|
|
|
$sql.= " AND ec.fk_socpeople = ".$user->id.")"; |
|
1194
|
|
|
$sql.= " )"; |
|
1195
|
|
|
} |
|
1196
|
|
|
if ($mode == 1) |
|
1197
|
|
|
{ |
|
1198
|
|
|
$sql.= " AND ec.element_id = p.rowid"; |
|
1199
|
|
|
$sql.= " AND ("; |
|
1200
|
|
|
$sql.= " ( ec.fk_c_type_contact IN (".join(',', array_keys($listofprojectcontacttype)).")"; |
|
1201
|
|
|
$sql.= " AND ec.fk_socpeople = ".$user->id.")"; |
|
1202
|
|
|
$sql.= " )"; |
|
1203
|
|
|
} |
|
1204
|
|
|
if ($mode == 2) |
|
1205
|
|
|
{ |
|
1206
|
|
|
// No filter. Use this if user has permission to see all project |
|
1207
|
|
|
} |
|
1208
|
|
|
|
|
1209
|
|
|
$sql.= $filter; |
|
1210
|
|
|
//print $sql; |
|
1211
|
|
|
|
|
1212
|
|
|
$resql = $this->db->query($sql); |
|
1213
|
|
|
if ($resql) |
|
1214
|
|
|
{ |
|
1215
|
|
|
$num = $this->db->num_rows($resql); |
|
1216
|
|
|
$i = 0; |
|
1217
|
|
|
while ($i < $num) |
|
1218
|
|
|
{ |
|
1219
|
|
|
$row = $this->db->fetch_row($resql); |
|
1220
|
|
|
$projects[$row[0]] = $row[1]; |
|
1221
|
|
|
$temp[] = $row[0]; |
|
1222
|
|
|
$i++; |
|
1223
|
|
|
} |
|
1224
|
|
|
|
|
1225
|
|
|
$this->db->free($resql); |
|
1226
|
|
|
|
|
1227
|
|
|
if ($list) |
|
1228
|
|
|
{ |
|
1229
|
|
|
if (empty($temp)) return '0'; |
|
1230
|
|
|
$result = implode(',', $temp); |
|
1231
|
|
|
return $result; |
|
1232
|
|
|
} |
|
1233
|
|
|
} |
|
1234
|
|
|
else |
|
1235
|
|
|
{ |
|
1236
|
|
|
dol_print_error($this->db); |
|
1237
|
|
|
} |
|
1238
|
|
|
|
|
1239
|
|
|
return $projects; |
|
1240
|
|
|
} |
|
1241
|
|
|
|
|
1242
|
|
|
/** |
|
1243
|
|
|
* Load an object from its id and create a new one in database |
|
1244
|
|
|
* |
|
1245
|
|
|
* @param int $fromid Id of object to clone |
|
1246
|
|
|
* @param bool $clone_contact Clone contact of project |
|
1247
|
|
|
* @param bool $clone_task Clone task of project |
|
1248
|
|
|
* @param bool $clone_project_file Clone file of project |
|
1249
|
|
|
* @param bool $clone_task_file Clone file of task (if task are copied) |
|
1250
|
|
|
* @param bool $clone_note Clone note of project |
|
1251
|
|
|
* @param bool $move_date Move task date on clone |
|
1252
|
|
|
* @param integer $notrigger No trigger flag |
|
1253
|
|
|
* @param int $newthirdpartyid New thirdparty id |
|
1254
|
|
|
* @return int New id of clone |
|
1255
|
|
|
*/ |
|
1256
|
|
|
function createFromClone($fromid,$clone_contact=false,$clone_task=true,$clone_project_file=false,$clone_task_file=false,$clone_note=true,$move_date=true,$notrigger=0,$newthirdpartyid=0) |
|
1257
|
|
|
{ |
|
1258
|
|
|
global $user,$langs,$conf; |
|
1259
|
|
|
|
|
1260
|
|
|
$error=0; |
|
1261
|
|
|
|
|
1262
|
|
|
dol_syslog("createFromClone clone_contact=".$clone_contact." clone_task=".$clone_task." clone_project_file=".$clone_project_file." clone_note=".$clone_note." move_date=".$move_date,LOG_DEBUG); |
|
1263
|
|
|
|
|
1264
|
|
|
$now = dol_mktime(0,0,0,idate('m',dol_now()),idate('d',dol_now()),idate('Y',dol_now())); |
|
1265
|
|
|
|
|
1266
|
|
|
$clone_project=new Project($this->db); |
|
1267
|
|
|
|
|
1268
|
|
|
$clone_project->context['createfromclone']='createfromclone'; |
|
1269
|
|
|
|
|
1270
|
|
|
$this->db->begin(); |
|
1271
|
|
|
|
|
1272
|
|
|
// Load source object |
|
1273
|
|
|
$clone_project->fetch($fromid); |
|
1274
|
|
|
$clone_project->fetch_optionals(); |
|
1275
|
|
|
if ($newthirdpartyid > 0) $clone_project->socid = $newthirdpartyid; |
|
1276
|
|
|
$clone_project->fetch_thirdparty(); |
|
1277
|
|
|
|
|
1278
|
|
|
$orign_dt_start=$clone_project->date_start; |
|
1279
|
|
|
$orign_project_ref=$clone_project->ref; |
|
1280
|
|
|
|
|
1281
|
|
|
$clone_project->id=0; |
|
1282
|
|
|
if ($move_date) { |
|
1283
|
|
|
$clone_project->date_start = $now; |
|
1284
|
|
|
if (!(empty($clone_project->date_end))) |
|
1285
|
|
|
{ |
|
1286
|
|
|
$clone_project->date_end = $clone_project->date_end + ($now - $orign_dt_start); |
|
1287
|
|
|
} |
|
1288
|
|
|
} |
|
1289
|
|
|
|
|
1290
|
|
|
$clone_project->datec = $now; |
|
|
|
|
|
|
1291
|
|
|
|
|
1292
|
|
|
if (! $clone_note) |
|
1293
|
|
|
{ |
|
1294
|
|
|
$clone_project->note_private=''; |
|
1295
|
|
|
$clone_project->note_public=''; |
|
1296
|
|
|
} |
|
1297
|
|
|
|
|
1298
|
|
|
//Generate next ref |
|
1299
|
|
|
$defaultref=''; |
|
1300
|
|
|
$obj = empty($conf->global->PROJECT_ADDON)?'mod_project_simple':$conf->global->PROJECT_ADDON; |
|
1301
|
|
|
// Search template files |
|
1302
|
|
|
$file=''; $classname=''; $filefound=0; |
|
1303
|
|
|
$dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); |
|
1304
|
|
|
foreach($dirmodels as $reldir) |
|
1305
|
|
|
{ |
|
1306
|
|
|
$file=dol_buildpath($reldir."core/modules/project/".$obj.'.php',0); |
|
1307
|
|
|
if (file_exists($file)) |
|
1308
|
|
|
{ |
|
1309
|
|
|
$filefound=1; |
|
1310
|
|
|
dol_include_once($reldir."core/modules/project/".$obj.'.php'); |
|
1311
|
|
|
$modProject = new $obj; |
|
1312
|
|
|
$defaultref = $modProject->getNextValue(is_object($clone_project->thirdparty)?$clone_project->thirdparty:null, $clone_project); |
|
1313
|
|
|
break; |
|
1314
|
|
|
} |
|
1315
|
|
|
} |
|
1316
|
|
|
if (is_numeric($defaultref) && $defaultref <= 0) $defaultref=''; |
|
1317
|
|
|
|
|
1318
|
|
|
$clone_project->ref=$defaultref; |
|
1319
|
|
|
$clone_project->title=$langs->trans("CopyOf").' '.$clone_project->title; |
|
1320
|
|
|
|
|
1321
|
|
|
// Create clone |
|
1322
|
|
|
$result=$clone_project->create($user,$notrigger); |
|
1323
|
|
|
|
|
1324
|
|
|
// Other options |
|
1325
|
|
|
if ($result < 0) |
|
1326
|
|
|
{ |
|
1327
|
|
|
$this->error.=$clone_project->error; |
|
1328
|
|
|
$error++; |
|
1329
|
|
|
} |
|
1330
|
|
|
|
|
1331
|
|
|
if (! $error) |
|
1332
|
|
|
{ |
|
1333
|
|
|
//Get the new project id |
|
1334
|
|
|
$clone_project_id=$clone_project->id; |
|
1335
|
|
|
|
|
1336
|
|
|
//Note Update |
|
1337
|
|
|
if (!$clone_note) |
|
1338
|
|
|
{ |
|
1339
|
|
|
$clone_project->note_private=''; |
|
1340
|
|
|
$clone_project->note_public=''; |
|
1341
|
|
|
} |
|
1342
|
|
|
else |
|
1343
|
|
|
{ |
|
1344
|
|
|
$this->db->begin(); |
|
1345
|
|
|
$res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_public, ENT_QUOTES),'_public'); |
|
1346
|
|
|
if ($res < 0) |
|
1347
|
|
|
{ |
|
1348
|
|
|
$this->error.=$clone_project->error; |
|
1349
|
|
|
$error++; |
|
1350
|
|
|
$this->db->rollback(); |
|
1351
|
|
|
} |
|
1352
|
|
|
else |
|
1353
|
|
|
{ |
|
1354
|
|
|
$this->db->commit(); |
|
1355
|
|
|
} |
|
1356
|
|
|
|
|
1357
|
|
|
$this->db->begin(); |
|
1358
|
|
|
$res=$clone_project->update_note(dol_html_entity_decode($clone_project->note_private, ENT_QUOTES), '_private'); |
|
1359
|
|
|
if ($res < 0) |
|
1360
|
|
|
{ |
|
1361
|
|
|
$this->error.=$clone_project->error; |
|
1362
|
|
|
$error++; |
|
1363
|
|
|
$this->db->rollback(); |
|
1364
|
|
|
} |
|
1365
|
|
|
else |
|
1366
|
|
|
{ |
|
1367
|
|
|
$this->db->commit(); |
|
1368
|
|
|
} |
|
1369
|
|
|
} |
|
1370
|
|
|
|
|
1371
|
|
|
//Duplicate contact |
|
1372
|
|
|
if ($clone_contact) |
|
1373
|
|
|
{ |
|
1374
|
|
|
$origin_project = new Project($this->db); |
|
1375
|
|
|
$origin_project->fetch($fromid); |
|
1376
|
|
|
|
|
1377
|
|
|
foreach(array('internal','external') as $source) |
|
1378
|
|
|
{ |
|
1379
|
|
|
$tab = $origin_project->liste_contact(-1,$source); |
|
1380
|
|
|
|
|
1381
|
|
|
foreach ($tab as $contacttoadd) |
|
1382
|
|
|
{ |
|
1383
|
|
|
$clone_project->add_contact($contacttoadd['id'], $contacttoadd['code'], $contacttoadd['source'],$notrigger); |
|
1384
|
|
|
if ($clone_project->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') |
|
1385
|
|
|
{ |
|
1386
|
|
|
$langs->load("errors"); |
|
1387
|
|
|
$this->error.=$langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); |
|
1388
|
|
|
$error++; |
|
1389
|
|
|
} |
|
1390
|
|
|
else |
|
1391
|
|
|
{ |
|
1392
|
|
|
if ($clone_project->error!='') |
|
1393
|
|
|
{ |
|
1394
|
|
|
$this->error.=$clone_project->error; |
|
1395
|
|
|
$error++; |
|
1396
|
|
|
} |
|
1397
|
|
|
} |
|
1398
|
|
|
} |
|
1399
|
|
|
} |
|
1400
|
|
|
} |
|
1401
|
|
|
|
|
1402
|
|
|
//Duplicate file |
|
1403
|
|
|
if ($clone_project_file) |
|
1404
|
|
|
{ |
|
1405
|
|
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; |
|
1406
|
|
|
|
|
1407
|
|
|
$clone_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($defaultref); |
|
1408
|
|
|
$ori_project_dir = $conf->projet->dir_output . "/" . dol_sanitizeFileName($orign_project_ref); |
|
1409
|
|
|
|
|
1410
|
|
|
if (dol_mkdir($clone_project_dir) >= 0) |
|
1411
|
|
|
{ |
|
1412
|
|
|
$filearray=dol_dir_list($ori_project_dir,"files",0,'','(\.meta|_preview.*\.png)$','',SORT_ASC,1); |
|
1413
|
|
|
foreach($filearray as $key => $file) |
|
1414
|
|
|
{ |
|
1415
|
|
|
$rescopy = dol_copy($ori_project_dir . '/' . $file['name'], $clone_project_dir . '/' . $file['name'],0,1); |
|
1416
|
|
|
if (is_numeric($rescopy) && $rescopy < 0) |
|
1417
|
|
|
{ |
|
1418
|
|
|
$this->error.=$langs->trans("ErrorFailToCopyFile",$ori_project_dir . '/' . $file['name'],$clone_project_dir . '/' . $file['name']); |
|
1419
|
|
|
$error++; |
|
1420
|
|
|
} |
|
1421
|
|
|
} |
|
1422
|
|
|
} |
|
1423
|
|
|
else |
|
1424
|
|
|
{ |
|
1425
|
|
|
$this->error.=$langs->trans('ErrorInternalErrorDetected').':dol_mkdir'; |
|
1426
|
|
|
$error++; |
|
1427
|
|
|
} |
|
1428
|
|
|
} |
|
1429
|
|
|
|
|
1430
|
|
|
//Duplicate task |
|
1431
|
|
|
if ($clone_task) |
|
1432
|
|
|
{ |
|
1433
|
|
|
require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; |
|
1434
|
|
|
|
|
1435
|
|
|
$taskstatic = new Task($this->db); |
|
1436
|
|
|
|
|
1437
|
|
|
// Security check |
|
1438
|
|
|
$socid=0; |
|
1439
|
|
|
if ($user->societe_id > 0) $socid = $user->societe_id; |
|
1440
|
|
|
|
|
1441
|
|
|
$tasksarray=$taskstatic->getTasksArray(0, 0, $fromid, $socid, 0); |
|
1442
|
|
|
|
|
1443
|
|
|
$tab_conv_child_parent=array(); |
|
1444
|
|
|
|
|
1445
|
|
|
// Loop on each task, to clone it |
|
1446
|
|
|
foreach ($tasksarray as $tasktoclone) |
|
1447
|
|
|
{ |
|
1448
|
|
|
$result_clone = $taskstatic->createFromClone($tasktoclone->id,$clone_project_id,$tasktoclone->fk_parent,$move_date,true,false,$clone_task_file,true,false); |
|
1449
|
|
|
if ($result_clone <= 0) |
|
1450
|
|
|
{ |
|
1451
|
|
|
$this->error.=$result_clone->error; |
|
1452
|
|
|
$error++; |
|
1453
|
|
|
} |
|
1454
|
|
|
else |
|
1455
|
|
|
{ |
|
1456
|
|
|
$new_task_id=$result_clone; |
|
1457
|
|
|
$taskstatic->fetch($tasktoclone->id); |
|
1458
|
|
|
|
|
1459
|
|
|
//manage new parent clone task id |
|
1460
|
|
|
// if the current task has child we store the original task id and the equivalent clone task id |
|
1461
|
|
|
if (($taskstatic->hasChildren()) && !array_key_exists($tasktoclone->id,$tab_conv_child_parent)) |
|
1462
|
|
|
{ |
|
1463
|
|
|
$tab_conv_child_parent[$tasktoclone->id] = $new_task_id; |
|
1464
|
|
|
} |
|
1465
|
|
|
} |
|
1466
|
|
|
|
|
1467
|
|
|
} |
|
1468
|
|
|
|
|
1469
|
|
|
//Parse all clone node to be sure to update new parent |
|
1470
|
|
|
$tasksarray=$taskstatic->getTasksArray(0, 0, $clone_project_id, $socid, 0); |
|
1471
|
|
|
foreach ($tasksarray as $task_cloned) |
|
1472
|
|
|
{ |
|
1473
|
|
|
$taskstatic->fetch($task_cloned->id); |
|
1474
|
|
|
if ($taskstatic->fk_task_parent!=0) |
|
1475
|
|
|
{ |
|
1476
|
|
|
$taskstatic->fk_task_parent=$tab_conv_child_parent[$taskstatic->fk_task_parent]; |
|
1477
|
|
|
} |
|
1478
|
|
|
$res=$taskstatic->update($user,$notrigger); |
|
1479
|
|
|
if ($result_clone <= 0) |
|
|
|
|
|
|
1480
|
|
|
{ |
|
1481
|
|
|
$this->error.=$taskstatic->error; |
|
1482
|
|
|
$error++; |
|
1483
|
|
|
} |
|
1484
|
|
|
} |
|
1485
|
|
|
} |
|
1486
|
|
|
} |
|
1487
|
|
|
|
|
1488
|
|
|
unset($clone_project->context['createfromclone']); |
|
1489
|
|
|
|
|
1490
|
|
|
if (! $error) |
|
1491
|
|
|
{ |
|
1492
|
|
|
$this->db->commit(); |
|
1493
|
|
|
return $clone_project_id; |
|
|
|
|
|
|
1494
|
|
|
} |
|
1495
|
|
|
else |
|
1496
|
|
|
{ |
|
1497
|
|
|
$this->db->rollback(); |
|
1498
|
|
|
dol_syslog(get_class($this)."::createFromClone nbError: ".$error." error : " . $this->error, LOG_ERR); |
|
1499
|
|
|
return -1; |
|
1500
|
|
|
} |
|
1501
|
|
|
} |
|
1502
|
|
|
|
|
1503
|
|
|
|
|
1504
|
|
|
/** |
|
1505
|
|
|
* Shift project task date from current date to delta |
|
1506
|
|
|
* |
|
1507
|
|
|
* @param timestamp $old_project_dt_start old project start date |
|
1508
|
|
|
* @return int 1 if OK or < 0 if KO |
|
1509
|
|
|
*/ |
|
1510
|
|
|
function shiftTaskDate($old_project_dt_start) |
|
1511
|
|
|
{ |
|
1512
|
|
|
global $user,$langs,$conf; |
|
1513
|
|
|
|
|
1514
|
|
|
$error=0; |
|
1515
|
|
|
|
|
1516
|
|
|
$taskstatic = new Task($this->db); |
|
1517
|
|
|
|
|
1518
|
|
|
// Security check |
|
1519
|
|
|
$socid=0; |
|
1520
|
|
|
if ($user->societe_id > 0) $socid = $user->societe_id; |
|
1521
|
|
|
|
|
1522
|
|
|
$tasksarray=$taskstatic->getTasksArray(0, 0, $this->id, $socid, 0); |
|
1523
|
|
|
|
|
1524
|
|
|
foreach ($tasksarray as $tasktoshiftdate) |
|
1525
|
|
|
{ |
|
1526
|
|
|
$to_update=false; |
|
1527
|
|
|
// Fetch only if update of date will be made |
|
1528
|
|
|
if ((!empty($tasktoshiftdate->date_start)) || (!empty($tasktoshiftdate->date_end))) |
|
1529
|
|
|
{ |
|
1530
|
|
|
//dol_syslog(get_class($this)."::shiftTaskDate to_update", LOG_DEBUG); |
|
1531
|
|
|
$to_update=true; |
|
1532
|
|
|
$task = new Task($this->db); |
|
1533
|
|
|
$result = $task->fetch($tasktoshiftdate->id); |
|
1534
|
|
|
if (!$result) |
|
1535
|
|
|
{ |
|
1536
|
|
|
$error++; |
|
1537
|
|
|
$this->error.=$task->error; |
|
1538
|
|
|
} |
|
1539
|
|
|
} |
|
1540
|
|
|
//print "$this->date_start + $tasktoshiftdate->date_start - $old_project_dt_start";exit; |
|
1541
|
|
|
|
|
1542
|
|
|
//Calcultate new task start date with difference between old proj start date and origin task start date |
|
1543
|
|
|
if (!empty($tasktoshiftdate->date_start)) |
|
1544
|
|
|
{ |
|
1545
|
|
|
$task->date_start = $this->date_start + ($tasktoshiftdate->date_start - $old_project_dt_start); |
|
|
|
|
|
|
1546
|
|
|
} |
|
1547
|
|
|
|
|
1548
|
|
|
//Calcultate new task end date with difference between origin proj end date and origin task end date |
|
1549
|
|
|
if (!empty($tasktoshiftdate->date_end)) |
|
1550
|
|
|
{ |
|
1551
|
|
|
$task->date_end = $this->date_start + ($tasktoshiftdate->date_end - $old_project_dt_start); |
|
1552
|
|
|
} |
|
1553
|
|
|
|
|
1554
|
|
|
if ($to_update) |
|
1555
|
|
|
{ |
|
1556
|
|
|
$result = $task->update($user); |
|
1557
|
|
|
if (!$result) |
|
1558
|
|
|
{ |
|
1559
|
|
|
$error++; |
|
1560
|
|
|
$this->error.=$task->error; |
|
1561
|
|
|
} |
|
1562
|
|
|
} |
|
1563
|
|
|
} |
|
1564
|
|
|
if ($error!=0) |
|
1565
|
|
|
{ |
|
1566
|
|
|
return -1; |
|
1567
|
|
|
} |
|
1568
|
|
|
return $result; |
|
|
|
|
|
|
1569
|
|
|
} |
|
1570
|
|
|
|
|
1571
|
|
|
|
|
1572
|
|
|
/** |
|
1573
|
|
|
* Associate element to a project |
|
1574
|
|
|
* |
|
1575
|
|
|
* @param string $tableName Table of the element to update |
|
1576
|
|
|
* @param int $elementSelectId Key-rowid of the line of the element to update |
|
1577
|
|
|
* @return int 1 if OK or < 0 if KO |
|
1578
|
|
|
*/ |
|
1579
|
|
|
function update_element($tableName, $elementSelectId) |
|
1580
|
|
|
{ |
|
1581
|
|
|
$sql="UPDATE ".MAIN_DB_PREFIX.$tableName; |
|
1582
|
|
|
|
|
1583
|
|
|
if ($tableName == "actioncomm") |
|
1584
|
|
|
{ |
|
1585
|
|
|
$sql.= " SET fk_project=".$this->id; |
|
1586
|
|
|
$sql.= " WHERE id=".$elementSelectId; |
|
1587
|
|
|
} |
|
1588
|
|
|
else |
|
1589
|
|
|
{ |
|
1590
|
|
|
$sql.= " SET fk_projet=".$this->id; |
|
1591
|
|
|
$sql.= " WHERE rowid=".$elementSelectId; |
|
1592
|
|
|
} |
|
1593
|
|
|
|
|
1594
|
|
|
dol_syslog(get_class($this)."::update_element", LOG_DEBUG); |
|
1595
|
|
|
$resql=$this->db->query($sql); |
|
1596
|
|
|
if (!$resql) { |
|
1597
|
|
|
$this->error=$this->db->lasterror(); |
|
1598
|
|
|
return -1; |
|
1599
|
|
|
}else { |
|
1600
|
|
|
return 1; |
|
1601
|
|
|
} |
|
1602
|
|
|
|
|
1603
|
|
|
} |
|
1604
|
|
|
|
|
1605
|
|
|
/** |
|
1606
|
|
|
* Associate element to a project |
|
1607
|
|
|
* |
|
1608
|
|
|
* @param string $tableName Table of the element to update |
|
1609
|
|
|
* @param int $elementSelectId Key-rowid of the line of the element to update |
|
1610
|
|
|
* @return int 1 if OK or < 0 if KO |
|
1611
|
|
|
*/ |
|
1612
|
|
|
function remove_element($tableName, $elementSelectId) |
|
1613
|
|
|
{ |
|
1614
|
|
|
$sql="UPDATE ".MAIN_DB_PREFIX.$tableName; |
|
1615
|
|
|
|
|
1616
|
|
|
if ($TableName=="actioncomm") |
|
|
|
|
|
|
1617
|
|
|
{ |
|
1618
|
|
|
$sql.= " SET fk_project=NULL"; |
|
1619
|
|
|
$sql.= " WHERE id=".$elementSelectId; |
|
1620
|
|
|
} |
|
1621
|
|
|
else |
|
1622
|
|
|
{ |
|
1623
|
|
|
$sql.= " SET fk_projet=NULL"; |
|
1624
|
|
|
$sql.= " WHERE rowid=".$elementSelectId; |
|
1625
|
|
|
} |
|
1626
|
|
|
|
|
1627
|
|
|
dol_syslog(get_class($this)."::remove_element", LOG_DEBUG); |
|
1628
|
|
|
$resql=$this->db->query($sql); |
|
1629
|
|
|
if (!$resql) { |
|
1630
|
|
|
$this->error=$this->db->lasterror(); |
|
1631
|
|
|
return -1; |
|
1632
|
|
|
}else { |
|
1633
|
|
|
return 1; |
|
1634
|
|
|
} |
|
1635
|
|
|
|
|
1636
|
|
|
} |
|
1637
|
|
|
|
|
1638
|
|
|
/** |
|
1639
|
|
|
* Create an intervention document on disk using template defined into PROJECT_ADDON_PDF |
|
1640
|
|
|
* |
|
1641
|
|
|
* @param string $modele Force template to use ('' by default) |
|
1642
|
|
|
* @param Translate $outputlangs Objet lang to use for translation |
|
1643
|
|
|
* @param int $hidedetails Hide details of lines |
|
1644
|
|
|
* @param int $hidedesc Hide description |
|
1645
|
|
|
* @param int $hideref Hide ref |
|
1646
|
|
|
* @return int 0 if KO, 1 if OK |
|
1647
|
|
|
*/ |
|
1648
|
|
|
public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0) |
|
1649
|
|
|
{ |
|
1650
|
|
|
global $conf,$langs; |
|
1651
|
|
|
|
|
1652
|
|
|
$langs->load("projects"); |
|
1653
|
|
|
|
|
1654
|
|
|
if (! dol_strlen($modele)) { |
|
1655
|
|
|
|
|
1656
|
|
|
$modele = 'baleine'; |
|
1657
|
|
|
|
|
1658
|
|
|
if ($this->modelpdf) { |
|
1659
|
|
|
$modele = $this->modelpdf; |
|
1660
|
|
|
} elseif (! empty($conf->global->PROJECT_ADDON_PDF)) { |
|
1661
|
|
|
$modele = $conf->global->PROJECT_ADDON_PDF; |
|
1662
|
|
|
} |
|
1663
|
|
|
} |
|
1664
|
|
|
|
|
1665
|
|
|
$modelpath = "core/modules/project/doc/"; |
|
1666
|
|
|
|
|
1667
|
|
|
return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); |
|
1668
|
|
|
} |
|
1669
|
|
|
|
|
1670
|
|
|
|
|
1671
|
|
|
/** |
|
1672
|
|
|
* Load time spent into this->weekWorkLoad and this->weekWorkLoadPerTask for all day of a week of project. |
|
1673
|
|
|
* Note: array weekWorkLoad and weekWorkLoadPerTask are reset and filled at each call. |
|
1674
|
|
|
* |
|
1675
|
|
|
* @param int $datestart First day of week (use dol_get_first_day to find this date) |
|
1676
|
|
|
* @param int $taskid Filter on a task id |
|
1677
|
|
|
* @param int $userid Time spent by a particular user |
|
1678
|
|
|
* @return int <0 if OK, >0 if KO |
|
1679
|
|
|
*/ |
|
1680
|
|
|
public function loadTimeSpent($datestart, $taskid=0, $userid=0) |
|
1681
|
|
|
{ |
|
1682
|
|
|
$error=0; |
|
1683
|
|
|
|
|
1684
|
|
|
$this->weekWorkLoad=array(); |
|
1685
|
|
|
$this->weekWorkLoadPerTask=array(); |
|
1686
|
|
|
|
|
1687
|
|
|
if (empty($datestart)) dol_print_error('','Error datestart parameter is empty'); |
|
1688
|
|
|
|
|
1689
|
|
|
$sql = "SELECT ptt.rowid as taskid, ptt.task_duration, ptt.task_date, ptt.task_datehour, ptt.fk_task"; |
|
1690
|
|
|
$sql.= " FROM ".MAIN_DB_PREFIX."projet_task_time AS ptt, ".MAIN_DB_PREFIX."projet_task as pt"; |
|
1691
|
|
|
$sql.= " WHERE ptt.fk_task = pt.rowid"; |
|
1692
|
|
|
$sql.= " AND pt.fk_projet = ".$this->id; |
|
1693
|
|
|
$sql.= " AND (ptt.task_date >= '".$this->db->idate($datestart)."' "; |
|
1694
|
|
|
$sql.= " AND ptt.task_date <= '".$this->db->idate(dol_time_plus_duree($datestart, 1, 'w') - 1)."')"; |
|
1695
|
|
|
if ($task_id) $sql.= " AND ptt.fk_task=".$taskid; |
|
|
|
|
|
|
1696
|
|
|
if (is_numeric($userid)) $sql.= " AND ptt.fk_user=".$userid; |
|
1697
|
|
|
|
|
1698
|
|
|
//print $sql; |
|
1699
|
|
|
$resql=$this->db->query($sql); |
|
1700
|
|
|
if ($resql) |
|
1701
|
|
|
{ |
|
1702
|
|
|
$daylareadyfound=array(); |
|
1703
|
|
|
|
|
1704
|
|
|
$num = $this->db->num_rows($resql); |
|
1705
|
|
|
$i = 0; |
|
1706
|
|
|
// Loop on each record found, so each couple (project id, task id) |
|
1707
|
|
|
while ($i < $num) |
|
1708
|
|
|
{ |
|
1709
|
|
|
$obj=$this->db->fetch_object($resql); |
|
1710
|
|
|
$day=$this->db->jdate($obj->task_date); // task_date is date without hours |
|
1711
|
|
|
if (empty($daylareadyfound[$day])) |
|
1712
|
|
|
{ |
|
1713
|
|
|
$this->weekWorkLoad[$day] = $obj->task_duration; |
|
1714
|
|
|
$this->weekWorkLoadPerTask[$day][$obj->fk_task] = $obj->task_duration; |
|
1715
|
|
|
} |
|
1716
|
|
|
else |
|
1717
|
|
|
{ |
|
1718
|
|
|
$this->weekWorkLoad[$day] += $obj->task_duration; |
|
1719
|
|
|
$this->weekWorkLoadPerTask[$day][$obj->fk_task] += $obj->task_duration; |
|
1720
|
|
|
} |
|
1721
|
|
|
$daylareadyfound[$day]=1; |
|
1722
|
|
|
$i++; |
|
1723
|
|
|
} |
|
1724
|
|
|
$this->db->free($resql); |
|
1725
|
|
|
return 1; |
|
1726
|
|
|
} |
|
1727
|
|
|
else |
|
1728
|
|
|
{ |
|
1729
|
|
|
$this->error="Error ".$this->db->lasterror(); |
|
1730
|
|
|
dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); |
|
1731
|
|
|
return -1; |
|
1732
|
|
|
} |
|
1733
|
|
|
} |
|
1734
|
|
|
|
|
1735
|
|
|
|
|
1736
|
|
|
/** |
|
1737
|
|
|
* Load indicators for dashboard (this->nbtodo and this->nbtodolate) |
|
1738
|
|
|
* |
|
1739
|
|
|
* @param User $user Objet user |
|
1740
|
|
|
* @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK |
|
1741
|
|
|
*/ |
|
1742
|
|
|
function load_board($user) |
|
1743
|
|
|
{ |
|
1744
|
|
|
global $conf, $langs; |
|
1745
|
|
|
|
|
1746
|
|
|
// For external user, no check is done on company because readability is managed by public status of project and assignement. |
|
1747
|
|
|
//$socid=$user->societe_id; |
|
1748
|
|
|
|
|
1749
|
|
|
if (! $user->rights->projet->all->lire) $projectsListId = $this->getProjectsAuthorizedForUser($user,0,1,$socid); |
|
|
|
|
|
|
1750
|
|
|
|
|
1751
|
|
|
$sql = "SELECT p.rowid, p.fk_statut as status, p.fk_opp_status, p.datee as datee"; |
|
1752
|
|
|
$sql.= " FROM (".MAIN_DB_PREFIX."projet as p"; |
|
1753
|
|
|
$sql.= ")"; |
|
1754
|
|
|
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; |
|
1755
|
|
|
// For external user, no check is done on company permission because readability is managed by public status of project and assignement. |
|
1756
|
|
|
//if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; |
|
1757
|
|
|
$sql.= " WHERE p.fk_statut = 1"; |
|
1758
|
|
|
$sql.= " AND p.entity IN (".getEntity('project').')'; |
|
1759
|
|
|
if (! $user->rights->projet->all->lire) $sql.= " AND p.rowid IN (".$projectsListId.")"; |
|
|
|
|
|
|
1760
|
|
|
// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser |
|
1761
|
|
|
//if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".$socid.")"; |
|
1762
|
|
|
// For external user, no check is done on company permission because readability is managed by public status of project and assignement. |
|
1763
|
|
|
//if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id.") OR (s.rowid IS NULL))"; |
|
1764
|
|
|
|
|
1765
|
|
|
//print $sql; |
|
1766
|
|
|
$resql=$this->db->query($sql); |
|
1767
|
|
|
if ($resql) |
|
1768
|
|
|
{ |
|
1769
|
|
|
$project_static = new Project($this->db); |
|
1770
|
|
|
|
|
1771
|
|
|
$response = new WorkboardResponse(); |
|
1772
|
|
|
$response->warning_delay = $conf->projet->warning_delay/60/60/24; |
|
1773
|
|
|
$response->label = $langs->trans("OpenedProjects"); |
|
1774
|
|
|
if ($user->rights->projet->all->lire) $response->url = DOL_URL_ROOT.'/projet/list.php?search_status=1&mainmenu=project'; |
|
1775
|
|
|
else $response->url = DOL_URL_ROOT.'/projet/list.php?search_project_user=-1&search_status=1&mainmenu=project'; |
|
1776
|
|
|
$response->img = img_object('',"projectpub"); |
|
1777
|
|
|
|
|
1778
|
|
|
// This assignment in condition is not a bug. It allows walking the results. |
|
1779
|
|
|
while ($obj=$this->db->fetch_object($resql)) |
|
1780
|
|
|
{ |
|
1781
|
|
|
$response->nbtodo++; |
|
1782
|
|
|
|
|
1783
|
|
|
$project_static->statut = $obj->status; |
|
1784
|
|
|
$project_static->opp_status = $obj->opp_status; |
|
1785
|
|
|
$project_static->datee = $this->db->jdate($obj->datee); |
|
1786
|
|
|
|
|
1787
|
|
|
if ($project_static->hasDelay()) { |
|
1788
|
|
|
$response->nbtodolate++; |
|
1789
|
|
|
} |
|
1790
|
|
|
} |
|
1791
|
|
|
|
|
1792
|
|
|
return $response; |
|
1793
|
|
|
} |
|
1794
|
|
|
else |
|
1795
|
|
|
{ |
|
1796
|
|
|
$this->error=$this->db->error(); |
|
1797
|
|
|
return -1; |
|
1798
|
|
|
} |
|
1799
|
|
|
} |
|
1800
|
|
|
|
|
1801
|
|
|
|
|
1802
|
|
|
/** |
|
1803
|
|
|
* Function used to replace a thirdparty id with another one. |
|
1804
|
|
|
* |
|
1805
|
|
|
* @param DoliDB $db Database handler |
|
1806
|
|
|
* @param int $origin_id Old thirdparty id |
|
1807
|
|
|
* @param int $dest_id New thirdparty id |
|
1808
|
|
|
* @return bool |
|
1809
|
|
|
*/ |
|
1810
|
|
|
public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) |
|
1811
|
|
|
{ |
|
1812
|
|
|
$tables = array( |
|
1813
|
|
|
'projet' |
|
1814
|
|
|
); |
|
1815
|
|
|
|
|
1816
|
|
|
return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); |
|
1817
|
|
|
} |
|
1818
|
|
|
|
|
1819
|
|
|
|
|
1820
|
|
|
/** |
|
1821
|
|
|
* Charge indicateurs this->nb pour le tableau de bord |
|
1822
|
|
|
* |
|
1823
|
|
|
* @return int <0 if KO, >0 if OK |
|
1824
|
|
|
*/ |
|
1825
|
|
|
function load_state_board() |
|
1826
|
|
|
{ |
|
1827
|
|
|
global $user; |
|
1828
|
|
|
|
|
1829
|
|
|
$this->nb=array(); |
|
1830
|
|
|
|
|
1831
|
|
|
$sql = "SELECT count(p.rowid) as nb"; |
|
1832
|
|
|
$sql.= " FROM ".MAIN_DB_PREFIX."projet as p"; |
|
1833
|
|
|
$sql.= " WHERE"; |
|
1834
|
|
|
$sql.= " p.entity IN (".getEntity('project').")"; |
|
1835
|
|
|
if (! $user->rights->projet->all->lire) |
|
1836
|
|
|
{ |
|
1837
|
|
|
$projectsListId = $this->getProjectsAuthorizedForUser($user,0,1); |
|
1838
|
|
|
$sql .= "AND p.rowid IN (".$projectsListId.")"; |
|
1839
|
|
|
} |
|
1840
|
|
|
|
|
1841
|
|
|
$resql=$this->db->query($sql); |
|
1842
|
|
|
if ($resql) |
|
1843
|
|
|
{ |
|
1844
|
|
|
while ($obj=$this->db->fetch_object($resql)) |
|
1845
|
|
|
{ |
|
1846
|
|
|
$this->nb["projects"]=$obj->nb; |
|
1847
|
|
|
} |
|
1848
|
|
|
$this->db->free($resql); |
|
1849
|
|
|
return 1; |
|
1850
|
|
|
} |
|
1851
|
|
|
else |
|
1852
|
|
|
{ |
|
1853
|
|
|
dol_print_error($this->db); |
|
1854
|
|
|
$this->error=$this->db->error(); |
|
1855
|
|
|
return -1; |
|
1856
|
|
|
} |
|
1857
|
|
|
} |
|
1858
|
|
|
|
|
1859
|
|
|
|
|
1860
|
|
|
/** |
|
1861
|
|
|
* Is the project delayed? |
|
1862
|
|
|
* |
|
1863
|
|
|
* @return bool |
|
1864
|
|
|
*/ |
|
1865
|
|
|
public function hasDelay() |
|
1866
|
|
|
{ |
|
1867
|
|
|
global $conf; |
|
1868
|
|
|
|
|
1869
|
|
|
if (! ($this->statut == 1)) return false; |
|
1870
|
|
|
if (! $this->datee && ! $this->date_end) return false; |
|
1871
|
|
|
|
|
1872
|
|
|
$now = dol_now(); |
|
1873
|
|
|
|
|
1874
|
|
|
return ($this->datee ? $this->datee : $this->date_end) < ($now - $conf->projet->warning_delay); |
|
1875
|
|
|
} |
|
1876
|
|
|
|
|
1877
|
|
|
|
|
1878
|
|
|
/** |
|
1879
|
|
|
* Charge les informations d'ordre info dans l'objet commande |
|
1880
|
|
|
* |
|
1881
|
|
|
* @param int $id Id of order |
|
1882
|
|
|
* @return void |
|
1883
|
|
|
*/ |
|
1884
|
|
|
function info($id) |
|
1885
|
|
|
{ |
|
1886
|
|
|
$sql = 'SELECT c.rowid, datec as datec, tms as datem,'; |
|
1887
|
|
|
$sql.= ' date_close as datecloture,'; |
|
1888
|
|
|
$sql.= ' fk_user_creat as fk_user_author, fk_user_close as fk_use_cloture'; |
|
1889
|
|
|
$sql.= ' FROM '.MAIN_DB_PREFIX.'projet as c'; |
|
1890
|
|
|
$sql.= ' WHERE c.rowid = '.$id; |
|
1891
|
|
|
$result=$this->db->query($sql); |
|
1892
|
|
|
if ($result) |
|
1893
|
|
|
{ |
|
1894
|
|
|
if ($this->db->num_rows($result)) |
|
1895
|
|
|
{ |
|
1896
|
|
|
$obj = $this->db->fetch_object($result); |
|
1897
|
|
|
$this->id = $obj->rowid; |
|
1898
|
|
|
if ($obj->fk_user_author) |
|
1899
|
|
|
{ |
|
1900
|
|
|
$cuser = new User($this->db); |
|
1901
|
|
|
$cuser->fetch($obj->fk_user_author); |
|
1902
|
|
|
$this->user_creation = $cuser; |
|
1903
|
|
|
} |
|
1904
|
|
|
|
|
1905
|
|
|
if ($obj->fk_user_cloture) |
|
1906
|
|
|
{ |
|
1907
|
|
|
$cluser = new User($this->db); |
|
1908
|
|
|
$cluser->fetch($obj->fk_user_cloture); |
|
1909
|
|
|
$this->user_cloture = $cluser; |
|
1910
|
|
|
} |
|
1911
|
|
|
|
|
1912
|
|
|
$this->date_creation = $this->db->jdate($obj->datec); |
|
1913
|
|
|
$this->date_modification = $this->db->jdate($obj->datem); |
|
1914
|
|
|
$this->date_cloture = $this->db->jdate($obj->datecloture); |
|
1915
|
|
|
} |
|
1916
|
|
|
|
|
1917
|
|
|
$this->db->free($result); |
|
1918
|
|
|
|
|
1919
|
|
|
} |
|
1920
|
|
|
else |
|
1921
|
|
|
{ |
|
1922
|
|
|
dol_print_error($this->db); |
|
1923
|
|
|
} |
|
1924
|
|
|
} |
|
1925
|
|
|
|
|
1926
|
|
|
/** |
|
1927
|
|
|
* Sets object to supplied categories. |
|
1928
|
|
|
* |
|
1929
|
|
|
* Deletes object from existing categories not supplied. |
|
1930
|
|
|
* Adds it to non existing supplied categories. |
|
1931
|
|
|
* Existing categories are left untouch. |
|
1932
|
|
|
* |
|
1933
|
|
|
* @param int[]|int $categories Category or categories IDs |
|
1934
|
|
|
* @return void |
|
1935
|
|
|
*/ |
|
1936
|
|
|
public function setCategories($categories) |
|
1937
|
|
|
{ |
|
1938
|
|
|
// Decode type |
|
1939
|
|
|
$type_id = Categorie::TYPE_PROJECT; |
|
1940
|
|
|
$type_text = 'project'; |
|
1941
|
|
|
|
|
1942
|
|
|
|
|
1943
|
|
|
// Handle single category |
|
1944
|
|
|
if (!is_array($categories)) { |
|
1945
|
|
|
$categories = array($categories); |
|
1946
|
|
|
} |
|
1947
|
|
|
|
|
1948
|
|
|
// Get current categories |
|
1949
|
|
|
require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; |
|
1950
|
|
|
$c = new Categorie($this->db); |
|
1951
|
|
|
$existing = $c->containing($this->id, $type_id, 'id'); |
|
1952
|
|
|
|
|
1953
|
|
|
// Diff |
|
1954
|
|
|
if (is_array($existing)) { |
|
1955
|
|
|
$to_del = array_diff($existing, $categories); |
|
1956
|
|
|
$to_add = array_diff($categories, $existing); |
|
1957
|
|
|
} else { |
|
1958
|
|
|
$to_del = array(); // Nothing to delete |
|
1959
|
|
|
$to_add = $categories; |
|
1960
|
|
|
} |
|
1961
|
|
|
|
|
1962
|
|
|
// Process |
|
1963
|
|
|
foreach ($to_del as $del) { |
|
1964
|
|
|
if ($c->fetch($del) > 0) { |
|
1965
|
|
|
$result=$c->del_type($this, $type_text); |
|
1966
|
|
|
if ($result<0) { |
|
1967
|
|
|
$this->errors=$c->errors; |
|
1968
|
|
|
$this->error=$c->error; |
|
1969
|
|
|
return -1; |
|
1970
|
|
|
} |
|
1971
|
|
|
} |
|
1972
|
|
|
} |
|
1973
|
|
|
foreach ($to_add as $add) { |
|
1974
|
|
|
if ($c->fetch($add) > 0) { |
|
1975
|
|
|
$result=$c->add_type($this, $type_text); |
|
1976
|
|
|
if ($result<0) { |
|
1977
|
|
|
$this->errors=$c->errors; |
|
1978
|
|
|
$this->error=$c->error; |
|
1979
|
|
|
return -1; |
|
1980
|
|
|
} |
|
1981
|
|
|
} |
|
1982
|
|
|
} |
|
1983
|
|
|
|
|
1984
|
|
|
return 1; |
|
1985
|
|
|
} |
|
1986
|
|
|
|
|
1987
|
|
|
|
|
1988
|
|
|
/** |
|
1989
|
|
|
* Create an array of tasks of current project |
|
1990
|
|
|
* |
|
1991
|
|
|
* @param User $user Object user we want project allowed to |
|
1992
|
|
|
* @return int >0 if OK, <0 if KO |
|
1993
|
|
|
*/ |
|
1994
|
|
|
function getLinesArray($user) |
|
1995
|
|
|
{ |
|
1996
|
|
|
require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; |
|
1997
|
|
|
$taskstatic = new Task($this->db); |
|
1998
|
|
|
|
|
1999
|
|
|
$this->lines = $taskstatic->getTasksArray(0, $user, $this->id, 0, 0); |
|
|
|
|
|
|
2000
|
|
|
} |
|
2001
|
|
|
|
|
2002
|
|
|
} |
|
2003
|
|
|
|
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
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. 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.