1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* Copyright (C) 2008-2014 Laurent Destailleur <[email protected]> |
4
|
|
|
* Copyright (C) 2010-2012 Regis Houssin <[email protected]> |
5
|
|
|
* Copyright (C) 2014 Marcos García <[email protected]> |
6
|
|
|
* Copyright (C) 2018-2024 Frédéric France <[email protected]> |
7
|
|
|
* Copyright (C) 2020 Juanjo Menent <[email protected]> |
8
|
|
|
* Copyright (C) 2022 Charlene Benke <[email protected]> |
9
|
|
|
* Copyright (C) 2023 Gauthier VERDOL <[email protected]> |
10
|
|
|
* Copyright (C) 2024 MDW <[email protected]> |
11
|
|
|
* Copyright (C) 2024 Rafael San José <[email protected]> |
12
|
|
|
* |
13
|
|
|
* This program is free software; you can redistribute it and/or modify |
14
|
|
|
* it under the terms of the GNU General Public License as published by |
15
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
16
|
|
|
* (at your option) any later version. |
17
|
|
|
* |
18
|
|
|
* This program is distributed in the hope that it will be useful, |
19
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
20
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21
|
|
|
* GNU General Public License for more details. |
22
|
|
|
* |
23
|
|
|
* You should have received a copy of the GNU General Public License |
24
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. |
25
|
|
|
*/ |
26
|
|
|
|
27
|
|
|
namespace Dolibarr\Code\Projet\Classes; |
28
|
|
|
|
29
|
|
|
use Dolibarr\Code\Core\Classes\ExtraFields; |
30
|
|
|
use Dolibarr\Code\Core\Classes\TimeSpent; |
31
|
|
|
use Dolibarr\Code\Core\Classes\Translate; |
32
|
|
|
use Dolibarr\Code\Core\Classes\WorkboardResponse; |
33
|
|
|
use Dolibarr\Code\User\Classes\User; |
34
|
|
|
use Dolibarr\Core\Base\CommonObjectLine; |
35
|
|
|
use DoliDB; |
36
|
|
|
use stdClass; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* \file htdocs/projet/class/task.class.php |
40
|
|
|
* \ingroup project |
41
|
|
|
* \brief This file is a CRUD class file for Task (Create/Read/Update/Delete) |
42
|
|
|
*/ |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Class to manage tasks |
46
|
|
|
*/ |
47
|
|
|
class Task extends CommonObjectLine |
48
|
|
|
{ |
49
|
|
|
/** |
50
|
|
|
* @var string ID to identify managed object |
51
|
|
|
*/ |
52
|
|
|
public $element = 'project_task'; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @var string Name of table without prefix where object is stored |
56
|
|
|
*/ |
57
|
|
|
public $table_element = 'projet_task'; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var string Field with ID of parent key if this field has a parent |
61
|
|
|
*/ |
62
|
|
|
public $fk_element = 'fk_element'; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @var string String with name of icon for myobject. |
66
|
|
|
*/ |
67
|
|
|
public $picto = 'projecttask'; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @var array<string, array<string>> List of child tables. To test if we can delete object. |
71
|
|
|
*/ |
72
|
|
|
protected $childtables = array( |
73
|
|
|
'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task') |
74
|
|
|
); |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var int ID parent task |
78
|
|
|
*/ |
79
|
|
|
public $fk_task_parent = 0; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var string Label of task |
83
|
|
|
*/ |
84
|
|
|
public $label; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @var string description |
88
|
|
|
*/ |
89
|
|
|
public $description; |
90
|
|
|
|
91
|
|
|
public $duration_effective; // total of time spent on this task |
92
|
|
|
public $planned_workload; |
93
|
|
|
public $date_c; |
94
|
|
|
public $progress; |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @deprecated Use date_start instead |
98
|
|
|
*/ |
99
|
|
|
public $dateo; |
100
|
|
|
|
101
|
|
|
public $date_start; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @deprecated Use date_end instead |
105
|
|
|
*/ |
106
|
|
|
public $datee; |
107
|
|
|
|
108
|
|
|
public $date_end; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @var int ID |
112
|
|
|
* @deprecated use status instead |
113
|
|
|
*/ |
114
|
|
|
public $fk_statut; |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @var int ID |
118
|
|
|
*/ |
119
|
|
|
public $status; |
120
|
|
|
|
121
|
|
|
public $priority; |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @var int ID |
125
|
|
|
*/ |
126
|
|
|
public $fk_user_creat; |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @var int ID |
130
|
|
|
*/ |
131
|
|
|
public $fk_user_valid; |
132
|
|
|
|
133
|
|
|
public $rang; |
134
|
|
|
|
135
|
|
|
public $timespent_min_date; |
136
|
|
|
public $timespent_max_date; |
137
|
|
|
public $timespent_total_duration; |
138
|
|
|
public $timespent_total_amount; |
139
|
|
|
public $timespent_nblinesnull; |
140
|
|
|
public $timespent_nblines; |
141
|
|
|
// For detail of lines of timespent record, there is the property ->lines in common |
142
|
|
|
|
143
|
|
|
// Var used to call method addTimeSpent(). Bad practice. |
144
|
|
|
public $timespent_id; |
145
|
|
|
public $timespent_duration; |
146
|
|
|
public $timespent_old_duration; |
147
|
|
|
public $timespent_date; |
148
|
|
|
public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds) |
149
|
|
|
public $timespent_withhour; // 1 = we entered also start hours for timesheet line |
150
|
|
|
public $timespent_fk_user; |
151
|
|
|
public $timespent_thm; |
152
|
|
|
public $timespent_note; |
153
|
|
|
public $timespent_fk_product; |
154
|
|
|
public $timespent_invoiceid; |
155
|
|
|
public $timespent_invoicelineid; |
156
|
|
|
|
157
|
|
|
public $comments = array(); |
158
|
|
|
|
159
|
|
|
// Properties calculated from sum of llx_element_time linked to task |
160
|
|
|
public $tobill; |
161
|
|
|
public $billed; |
162
|
|
|
|
163
|
|
|
// Properties to store project information |
164
|
|
|
public $projectref; |
165
|
|
|
public $projectstatus; |
166
|
|
|
public $projectlabel; |
167
|
|
|
public $opp_amount; |
168
|
|
|
public $opp_percent; |
169
|
|
|
public $fk_opp_status; |
170
|
|
|
public $usage_bill_time; |
171
|
|
|
public $public; |
172
|
|
|
public $array_options_project; |
173
|
|
|
|
174
|
|
|
// Properties to store thirdparty of project information |
175
|
|
|
public $socid; |
176
|
|
|
public $thirdparty_id; |
177
|
|
|
public $thirdparty_name; |
178
|
|
|
public $thirdparty_email; |
179
|
|
|
|
180
|
|
|
// store parent ref and position |
181
|
|
|
public $task_parent_ref; |
182
|
|
|
public $task_parent_position; |
183
|
|
|
|
184
|
|
|
|
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @var float budget_amount |
188
|
|
|
*/ |
189
|
|
|
public $budget_amount; |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @var float project_budget_amount |
193
|
|
|
*/ |
194
|
|
|
public $project_budget_amount; |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Draft status |
198
|
|
|
*/ |
199
|
|
|
const STATUS_DRAFT = 0; |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Validated status (To do). Note: We also have the field progress to know the progression from 0 to 100%. |
203
|
|
|
*/ |
204
|
|
|
const STATUS_VALIDATED = 1; |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Finished status |
208
|
|
|
*/ |
209
|
|
|
const STATUS_CLOSED = 3; |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Transferred status |
213
|
|
|
*/ |
214
|
|
|
const STATUS_TRANSFERRED = 4; |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* status canceled |
218
|
|
|
*/ |
219
|
|
|
const STATUS_CANCELED = 9; |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Constructor |
223
|
|
|
* |
224
|
|
|
* @param DoliDB $db Database handler |
225
|
|
|
*/ |
226
|
|
|
public function __construct($db) |
227
|
|
|
{ |
228
|
|
|
$this->db = $db; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Create into database |
233
|
|
|
* |
234
|
|
|
* @param User $user User that create |
235
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
236
|
|
|
* @return int Return integer <0 if KO, Id of created object if OK |
237
|
|
|
*/ |
238
|
|
|
public function create($user, $notrigger = 0) |
239
|
|
|
{ |
240
|
|
|
global $conf, $langs; |
241
|
|
|
|
242
|
|
|
//For the date |
243
|
|
|
$now = dol_now(); |
244
|
|
|
|
245
|
|
|
$error = 0; |
246
|
|
|
|
247
|
|
|
// Clean parameters |
248
|
|
|
$this->label = trim($this->label); |
249
|
|
|
$this->description = trim($this->description); |
250
|
|
|
$this->note_public = trim($this->note_public); |
251
|
|
|
$this->note_private = trim($this->note_private); |
252
|
|
|
|
253
|
|
|
if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) { |
254
|
|
|
$this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate'); |
255
|
|
|
return -1; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
// Insert request |
259
|
|
|
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet_task ("; |
260
|
|
|
$sql .= "entity"; |
261
|
|
|
$sql .= ", fk_projet"; |
262
|
|
|
$sql .= ", ref"; |
263
|
|
|
$sql .= ", fk_task_parent"; |
264
|
|
|
$sql .= ", label"; |
265
|
|
|
$sql .= ", description"; |
266
|
|
|
$sql .= ", note_public"; |
267
|
|
|
$sql .= ", note_private"; |
268
|
|
|
$sql .= ", datec"; |
269
|
|
|
$sql .= ", fk_user_creat"; |
270
|
|
|
$sql .= ", dateo"; |
271
|
|
|
$sql .= ", datee"; |
272
|
|
|
$sql .= ", planned_workload"; |
273
|
|
|
$sql .= ", progress"; |
274
|
|
|
$sql .= ", budget_amount"; |
275
|
|
|
$sql .= ", priority"; |
276
|
|
|
$sql .= ") VALUES ("; |
277
|
|
|
$sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity); |
278
|
|
|
$sql .= ", " . ((int) $this->fk_project); |
279
|
|
|
$sql .= ", " . (!empty($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : 'null'); |
280
|
|
|
$sql .= ", " . ((int) $this->fk_task_parent); |
281
|
|
|
$sql .= ", '" . $this->db->escape($this->label) . "'"; |
282
|
|
|
$sql .= ", '" . $this->db->escape($this->description) . "'"; |
283
|
|
|
$sql .= ", '" . $this->db->escape($this->note_public) . "'"; |
284
|
|
|
$sql .= ", '" . $this->db->escape($this->note_private) . "'"; |
285
|
|
|
$sql .= ", '" . $this->db->idate($now) . "'"; |
286
|
|
|
$sql .= ", " . ((int) $user->id); |
287
|
|
|
$sql .= ", " . ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : 'null'); |
288
|
|
|
$sql .= ", " . ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : 'null'); |
289
|
|
|
$sql .= ", " . (($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null'); |
290
|
|
|
$sql .= ", " . (($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null'); |
291
|
|
|
$sql .= ", " . (($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null'); |
292
|
|
|
$sql .= ", " . (($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null'); |
293
|
|
|
$sql .= ")"; |
294
|
|
|
|
295
|
|
|
$this->db->begin(); |
296
|
|
|
|
297
|
|
|
dol_syslog(get_only_class($this) . "::create", LOG_DEBUG); |
298
|
|
|
$resql = $this->db->query($sql); |
299
|
|
|
if (!$resql) { |
300
|
|
|
$error++; |
301
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
if (!$error) { |
305
|
|
|
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "projet_task"); |
306
|
|
|
// Update extrafield |
307
|
|
|
$result = $this->insertExtraFields(); |
308
|
|
|
if ($result < 0) { |
309
|
|
|
$error++; |
310
|
|
|
} |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
if (!$error) { |
314
|
|
|
if (!$notrigger) { |
315
|
|
|
// Call trigger |
316
|
|
|
$result = $this->call_trigger('TASK_CREATE', $user); |
317
|
|
|
if ($result < 0) { |
318
|
|
|
$error++; |
319
|
|
|
} |
320
|
|
|
// End call triggers |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
// Commit or rollback |
325
|
|
|
if ($error) { |
326
|
|
|
foreach ($this->errors as $errmsg) { |
327
|
|
|
dol_syslog(get_only_class($this) . "::create " . $errmsg, LOG_ERR); |
328
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
329
|
|
|
} |
330
|
|
|
$this->db->rollback(); |
331
|
|
|
return -1 * $error; |
332
|
|
|
} else { |
333
|
|
|
$this->db->commit(); |
334
|
|
|
return $this->id; |
335
|
|
|
} |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Load object in memory from database |
341
|
|
|
* |
342
|
|
|
* @param int $id Id object |
343
|
|
|
* @param string $ref ref object |
344
|
|
|
* @param int $loadparentdata Also load parent data |
345
|
|
|
* @return int Return integer <0 if KO, 0 if not found, >0 if OK |
346
|
|
|
*/ |
347
|
|
|
public function fetch($id, $ref = '', $loadparentdata = 0) |
348
|
|
|
{ |
349
|
|
|
$sql = "SELECT"; |
350
|
|
|
$sql .= " t.rowid,"; |
351
|
|
|
$sql .= " t.ref,"; |
352
|
|
|
$sql .= " t.entity,"; |
353
|
|
|
$sql .= " t.fk_projet as fk_project,"; |
354
|
|
|
$sql .= " t.fk_task_parent,"; |
355
|
|
|
$sql .= " t.label,"; |
356
|
|
|
$sql .= " t.description,"; |
357
|
|
|
$sql .= " t.duration_effective,"; |
358
|
|
|
$sql .= " t.planned_workload,"; |
359
|
|
|
$sql .= " t.datec,"; |
360
|
|
|
$sql .= " t.dateo as date_start,"; |
361
|
|
|
$sql .= " t.datee as date_end,"; |
362
|
|
|
$sql .= " t.fk_user_creat,"; |
363
|
|
|
$sql .= " t.fk_user_valid,"; |
364
|
|
|
$sql .= " t.fk_statut as status,"; |
365
|
|
|
$sql .= " t.progress,"; |
366
|
|
|
$sql .= " t.budget_amount,"; |
367
|
|
|
$sql .= " t.priority,"; |
368
|
|
|
$sql .= " t.note_private,"; |
369
|
|
|
$sql .= " t.note_public,"; |
370
|
|
|
$sql .= " t.rang"; |
371
|
|
|
if (!empty($loadparentdata)) { |
372
|
|
|
$sql .= ", t2.ref as task_parent_ref"; |
373
|
|
|
$sql .= ", t2.rang as task_parent_position"; |
374
|
|
|
} |
375
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet_task as t"; |
376
|
|
|
if (!empty($loadparentdata)) { |
377
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task as t2 ON t.fk_task_parent = t2.rowid"; |
378
|
|
|
} |
379
|
|
|
$sql .= " WHERE "; |
380
|
|
|
if (!empty($ref)) { |
381
|
|
|
$sql .= "entity IN (" . getEntity('project') . ")"; |
382
|
|
|
$sql .= " AND t.ref = '" . $this->db->escape($ref) . "'"; |
383
|
|
|
} else { |
384
|
|
|
$sql .= "t.rowid = " . ((int) $id); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
dol_syslog(get_only_class($this) . "::fetch", LOG_DEBUG); |
388
|
|
|
$resql = $this->db->query($sql); |
389
|
|
|
if ($resql) { |
390
|
|
|
$num_rows = $this->db->num_rows($resql); |
391
|
|
|
|
392
|
|
|
if ($num_rows) { |
393
|
|
|
$obj = $this->db->fetch_object($resql); |
394
|
|
|
|
395
|
|
|
$this->id = $obj->rowid; |
396
|
|
|
$this->ref = $obj->ref; |
397
|
|
|
$this->entity = $obj->entity; |
398
|
|
|
$this->fk_project = $obj->fk_project; |
399
|
|
|
$this->fk_task_parent = $obj->fk_task_parent; |
400
|
|
|
$this->label = $obj->label; |
401
|
|
|
$this->description = $obj->description; |
402
|
|
|
$this->duration_effective = $obj->duration_effective; |
403
|
|
|
$this->planned_workload = $obj->planned_workload; |
404
|
|
|
$this->date_c = $this->db->jdate($obj->datec); |
405
|
|
|
$this->date_start = $this->db->jdate($obj->date_start); |
406
|
|
|
$this->date_end = $this->db->jdate($obj->date_end); |
407
|
|
|
$this->fk_user_creat = $obj->fk_user_creat; |
408
|
|
|
$this->fk_user_valid = $obj->fk_user_valid; |
409
|
|
|
$this->fk_statut = $obj->status; |
|
|
|
|
410
|
|
|
$this->status = $obj->status; |
411
|
|
|
$this->progress = $obj->progress; |
412
|
|
|
$this->budget_amount = $obj->budget_amount; |
413
|
|
|
$this->priority = $obj->priority; |
414
|
|
|
$this->note_private = $obj->note_private; |
415
|
|
|
$this->note_public = $obj->note_public; |
416
|
|
|
$this->rang = $obj->rang; |
417
|
|
|
|
418
|
|
|
if (!empty($loadparentdata)) { |
419
|
|
|
$this->task_parent_ref = $obj->task_parent_ref; |
420
|
|
|
$this->task_parent_position = $obj->task_parent_position; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
// Retrieve all extrafield |
424
|
|
|
$this->fetch_optionals(); |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
$this->db->free($resql); |
428
|
|
|
|
429
|
|
|
if ($num_rows) { |
430
|
|
|
return 1; |
431
|
|
|
} else { |
432
|
|
|
return 0; |
433
|
|
|
} |
434
|
|
|
} else { |
435
|
|
|
$this->error = "Error " . $this->db->lasterror(); |
436
|
|
|
return -1; |
437
|
|
|
} |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Update database |
443
|
|
|
* |
444
|
|
|
* @param User $user User that modify |
445
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
446
|
|
|
* @return int Return integer <=0 if KO, >0 if OK |
447
|
|
|
*/ |
448
|
|
|
public function update($user = null, $notrigger = 0) |
449
|
|
|
{ |
450
|
|
|
global $conf, $langs; |
451
|
|
|
$error = 0; |
452
|
|
|
|
453
|
|
|
// Clean parameters |
454
|
|
|
if (isset($this->fk_project)) { |
455
|
|
|
$this->fk_project = (int) $this->fk_project; |
456
|
|
|
} |
457
|
|
|
if (isset($this->ref)) { |
458
|
|
|
$this->ref = trim($this->ref); |
459
|
|
|
} |
460
|
|
|
if (isset($this->fk_task_parent)) { |
461
|
|
|
$this->fk_task_parent = (int) $this->fk_task_parent; |
462
|
|
|
} |
463
|
|
|
if (isset($this->label)) { |
464
|
|
|
$this->label = trim($this->label); |
465
|
|
|
} |
466
|
|
|
if (isset($this->description)) { |
467
|
|
|
$this->description = trim($this->description); |
468
|
|
|
} |
469
|
|
|
if (isset($this->note_public)) { |
470
|
|
|
$this->note_public = trim($this->note_public); |
471
|
|
|
} |
472
|
|
|
if (isset($this->note_private)) { |
473
|
|
|
$this->note_private = trim($this->note_private); |
474
|
|
|
} |
475
|
|
|
if (isset($this->duration_effective)) { |
476
|
|
|
$this->duration_effective = trim($this->duration_effective); |
477
|
|
|
} |
478
|
|
|
if (isset($this->planned_workload)) { |
479
|
|
|
$this->planned_workload = trim($this->planned_workload); |
480
|
|
|
} |
481
|
|
|
if (isset($this->budget_amount)) { |
482
|
|
|
$this->budget_amount = (float) $this->budget_amount; |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) { |
486
|
|
|
$this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate'); |
487
|
|
|
return -1; |
488
|
|
|
} |
489
|
|
|
|
490
|
|
|
// Check parameters |
491
|
|
|
// Put here code to add control on parameters values |
492
|
|
|
|
493
|
|
|
// Update request |
494
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task SET"; |
495
|
|
|
$sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ","; |
496
|
|
|
$sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "'" . $this->db->escape($this->id) . "'") . ","; |
497
|
|
|
$sql .= " fk_task_parent=" . (isset($this->fk_task_parent) ? $this->fk_task_parent : "null") . ","; |
498
|
|
|
$sql .= " label=" . (isset($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ","; |
499
|
|
|
$sql .= " description=" . (isset($this->description) ? "'" . $this->db->escape($this->description) . "'" : "null") . ","; |
500
|
|
|
$sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ","; |
501
|
|
|
$sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ","; |
502
|
|
|
$sql .= " duration_effective=" . (isset($this->duration_effective) ? $this->duration_effective : "null") . ","; |
503
|
|
|
$sql .= " planned_workload=" . ((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null") . ","; |
504
|
|
|
$sql .= " dateo=" . ($this->date_start != '' ? "'" . $this->db->idate($this->date_start) . "'" : 'null') . ","; |
505
|
|
|
$sql .= " datee=" . ($this->date_end != '' ? "'" . $this->db->idate($this->date_end) . "'" : 'null') . ","; |
506
|
|
|
$sql .= " progress=" . (($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null') . ","; |
507
|
|
|
$sql .= " budget_amount=" . (($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null') . ","; |
508
|
|
|
$sql .= " rang=" . ((!empty($this->rang)) ? ((int) $this->rang) : "0") . ","; |
509
|
|
|
$sql .= " priority=" . ((!empty($this->priority)) ? ((int) $this->priority) : "0"); |
510
|
|
|
$sql .= " WHERE rowid=" . ((int) $this->id); |
511
|
|
|
|
512
|
|
|
$this->db->begin(); |
513
|
|
|
|
514
|
|
|
dol_syslog(get_only_class($this) . "::update", LOG_DEBUG); |
515
|
|
|
$resql = $this->db->query($sql); |
516
|
|
|
if (!$resql) { |
517
|
|
|
$error++; |
518
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
// Update extrafield |
522
|
|
|
if (!$error) { |
523
|
|
|
$result = $this->insertExtraFields(); |
524
|
|
|
if ($result < 0) { |
525
|
|
|
$error++; |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
if (!$error && getDolGlobalString('PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE')) { |
530
|
|
|
// Close the parent project if it is open (validated) and its tasks are 100% completed |
531
|
|
|
$project = new Project($this->db); |
532
|
|
|
if ($project->fetch($this->fk_project) > 0) { |
533
|
|
|
if ($project->statut == Project::STATUS_VALIDATED) { |
534
|
|
|
$project->getLinesArray(null); // this method does not return <= 0 if fails |
535
|
|
|
$projectCompleted = array_reduce( |
536
|
|
|
$project->lines, |
537
|
|
|
/** |
538
|
|
|
* @param bool $allTasksCompleted |
539
|
|
|
* @param Task $task |
540
|
|
|
* @return bool |
541
|
|
|
*/ |
542
|
|
|
static function ($allTasksCompleted, $task) { |
543
|
|
|
return $allTasksCompleted && $task->progress >= 100; |
544
|
|
|
}, |
545
|
|
|
1 |
546
|
|
|
); |
547
|
|
|
if ($projectCompleted) { |
548
|
|
|
if ($project->setClose($user) <= 0) { |
549
|
|
|
$error++; |
550
|
|
|
} |
551
|
|
|
} |
552
|
|
|
} |
553
|
|
|
} else { |
554
|
|
|
$error++; |
555
|
|
|
} |
556
|
|
|
if ($error) { |
557
|
|
|
$this->errors[] = $project->error; |
558
|
|
|
} |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
if (!$error) { |
562
|
|
|
if (!$notrigger) { |
563
|
|
|
// Call trigger |
564
|
|
|
$result = $this->call_trigger('TASK_MODIFY', $user); |
565
|
|
|
if ($result < 0) { |
566
|
|
|
$error++; |
567
|
|
|
} |
568
|
|
|
// End call triggers |
569
|
|
|
} |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) { |
573
|
|
|
// We remove directory |
574
|
|
|
if ($conf->project->dir_output) { |
575
|
|
|
$project = new Project($this->db); |
576
|
|
|
$project->fetch($this->fk_project); |
577
|
|
|
|
578
|
|
|
$olddir = $conf->project->dir_output . '/' . dol_sanitizeFileName($project->ref) . '/' . dol_sanitizeFileName($this->oldcopy->ref); |
579
|
|
|
$newdir = $conf->project->dir_output . '/' . dol_sanitizeFileName($project->ref) . '/' . dol_sanitizeFileName($this->ref); |
580
|
|
|
if (file_exists($olddir)) { |
581
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; |
582
|
|
|
$res = dol_move_dir($olddir, $newdir); |
583
|
|
|
if (!$res) { |
584
|
|
|
$langs->load("errors"); |
585
|
|
|
$this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir); |
586
|
|
|
$error++; |
587
|
|
|
} |
588
|
|
|
} |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
// Commit or rollback |
593
|
|
|
if ($error) { |
594
|
|
|
foreach ($this->errors as $errmsg) { |
595
|
|
|
dol_syslog(get_only_class($this) . "::update " . $errmsg, LOG_ERR); |
596
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
597
|
|
|
} |
598
|
|
|
$this->db->rollback(); |
599
|
|
|
return -1 * $error; |
600
|
|
|
} else { |
601
|
|
|
$this->db->commit(); |
602
|
|
|
return 1; |
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
|
607
|
|
|
/** |
608
|
|
|
* Delete task from database |
609
|
|
|
* |
610
|
|
|
* @param User $user User that delete |
611
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
612
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
613
|
|
|
*/ |
614
|
|
|
public function delete($user, $notrigger = 0) |
615
|
|
|
{ |
616
|
|
|
global $conf; |
617
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php'; |
618
|
|
|
|
619
|
|
|
$error = 0; |
620
|
|
|
|
621
|
|
|
$this->db->begin(); |
622
|
|
|
|
623
|
|
|
if ($this->hasChildren() > 0) { |
624
|
|
|
dol_syslog(get_only_class($this) . "::delete Can't delete record as it has some sub tasks", LOG_WARNING); |
625
|
|
|
$this->error = 'ErrorRecordHasSubTasks'; |
626
|
|
|
$this->db->rollback(); |
627
|
|
|
return 0; |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
$objectisused = $this->isObjectUsed($this->id); |
631
|
|
|
if (!empty($objectisused)) { |
632
|
|
|
dol_syslog(get_only_class($this) . "::delete Can't delete record as it has some child", LOG_WARNING); |
633
|
|
|
$this->error = 'ErrorRecordHasChildren'; |
634
|
|
|
$this->db->rollback(); |
635
|
|
|
return 0; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
if (!$error) { |
639
|
|
|
// Delete linked contacts |
640
|
|
|
$res = $this->delete_linked_contact(); |
641
|
|
|
if ($res < 0) { |
642
|
|
|
$this->error = 'ErrorFailToDeleteLinkedContact'; |
643
|
|
|
//$error++; |
644
|
|
|
$this->db->rollback(); |
645
|
|
|
return 0; |
646
|
|
|
} |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
if (!$error) { |
650
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "element_time"; |
651
|
|
|
$sql .= " WHERE fk_element = " . ((int) $this->id) . " AND elementtype = 'task'"; |
652
|
|
|
|
653
|
|
|
$resql = $this->db->query($sql); |
654
|
|
|
if (!$resql) { |
655
|
|
|
$error++; |
656
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
657
|
|
|
} |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
if (!$error) { |
661
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task_extrafields"; |
662
|
|
|
$sql .= " WHERE fk_object = " . ((int) $this->id); |
663
|
|
|
|
664
|
|
|
$resql = $this->db->query($sql); |
665
|
|
|
if (!$resql) { |
666
|
|
|
$error++; |
667
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
668
|
|
|
} |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
if (!$error) { |
672
|
|
|
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task"; |
673
|
|
|
$sql .= " WHERE rowid=" . ((int) $this->id); |
674
|
|
|
|
675
|
|
|
$resql = $this->db->query($sql); |
676
|
|
|
if (!$resql) { |
677
|
|
|
$error++; |
678
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
679
|
|
|
} |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
if (!$error) { |
683
|
|
|
if (!$notrigger) { |
684
|
|
|
// Call trigger |
685
|
|
|
$result = $this->call_trigger('TASK_DELETE', $user); |
686
|
|
|
if ($result < 0) { |
687
|
|
|
$error++; |
688
|
|
|
} |
689
|
|
|
// End call triggers |
690
|
|
|
} |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
// Commit or rollback |
694
|
|
|
if ($error) { |
695
|
|
|
foreach ($this->errors as $errmsg) { |
696
|
|
|
dol_syslog(get_only_class($this) . "::delete " . $errmsg, LOG_ERR); |
697
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
698
|
|
|
} |
699
|
|
|
$this->db->rollback(); |
700
|
|
|
return -1 * $error; |
701
|
|
|
} else { |
702
|
|
|
//Delete associated link file |
703
|
|
|
if ($conf->project->dir_output) { |
704
|
|
|
$projectstatic = new Project($this->db); |
705
|
|
|
$projectstatic->fetch($this->fk_project); |
706
|
|
|
|
707
|
|
|
$dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($projectstatic->ref) . '/' . dol_sanitizeFileName($this->id); |
708
|
|
|
dol_syslog(get_only_class($this) . "::delete dir=" . $dir, LOG_DEBUG); |
709
|
|
|
if (file_exists($dir)) { |
710
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php'; |
711
|
|
|
$res = @dol_delete_dir_recursive($dir); |
712
|
|
|
if (!$res) { |
713
|
|
|
$this->error = 'ErrorFailToDeleteDir'; |
714
|
|
|
$this->db->rollback(); |
715
|
|
|
return 0; |
716
|
|
|
} |
717
|
|
|
} |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
$this->db->commit(); |
721
|
|
|
|
722
|
|
|
return 1; |
723
|
|
|
} |
724
|
|
|
} |
725
|
|
|
|
726
|
|
|
/** |
727
|
|
|
* Return nb of children |
728
|
|
|
* |
729
|
|
|
* @return int Return integer <0 if KO, 0 if no children, >0 if OK |
730
|
|
|
*/ |
731
|
|
|
public function hasChildren() |
732
|
|
|
{ |
733
|
|
|
$error = 0; |
734
|
|
|
$ret = 0; |
735
|
|
|
|
736
|
|
|
$sql = "SELECT COUNT(*) as nb"; |
737
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet_task"; |
738
|
|
|
$sql .= " WHERE fk_task_parent = " . ((int) $this->id); |
739
|
|
|
|
740
|
|
|
dol_syslog(get_only_class($this) . "::hasChildren", LOG_DEBUG); |
741
|
|
|
$resql = $this->db->query($sql); |
742
|
|
|
if (!$resql) { |
743
|
|
|
$error++; |
744
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
745
|
|
|
} else { |
746
|
|
|
$obj = $this->db->fetch_object($resql); |
747
|
|
|
if ($obj) { |
748
|
|
|
$ret = $obj->nb; |
749
|
|
|
} |
750
|
|
|
$this->db->free($resql); |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
if (!$error) { |
754
|
|
|
return $ret; |
755
|
|
|
} else { |
756
|
|
|
return -1; |
757
|
|
|
} |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
/** |
761
|
|
|
* Return nb of time spent |
762
|
|
|
* |
763
|
|
|
* @return int Return integer <0 if KO, 0 if no children, >0 if OK |
764
|
|
|
*/ |
765
|
|
|
public function hasTimeSpent() |
766
|
|
|
{ |
767
|
|
|
$error = 0; |
768
|
|
|
$ret = 0; |
769
|
|
|
|
770
|
|
|
$sql = "SELECT COUNT(*) as nb"; |
771
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "element_time"; |
772
|
|
|
$sql .= " WHERE fk_element = " . ((int) $this->id); |
773
|
|
|
$sql .= " AND elementtype = 'task'"; |
774
|
|
|
|
775
|
|
|
dol_syslog(get_only_class($this) . "::hasTimeSpent", LOG_DEBUG); |
776
|
|
|
$resql = $this->db->query($sql); |
777
|
|
|
if (!$resql) { |
778
|
|
|
$error++; |
779
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
780
|
|
|
} else { |
781
|
|
|
$obj = $this->db->fetch_object($resql); |
782
|
|
|
if ($obj) { |
783
|
|
|
$ret = $obj->nb; |
784
|
|
|
} |
785
|
|
|
$this->db->free($resql); |
786
|
|
|
} |
787
|
|
|
|
788
|
|
|
if (!$error) { |
789
|
|
|
return $ret; |
790
|
|
|
} else { |
791
|
|
|
return -1; |
792
|
|
|
} |
793
|
|
|
} |
794
|
|
|
|
795
|
|
|
|
796
|
|
|
/** |
797
|
|
|
* getTooltipContentArray |
798
|
|
|
* |
799
|
|
|
* @param array $params ex option, infologin |
800
|
|
|
* @since v18 |
801
|
|
|
* @return array |
802
|
|
|
*/ |
803
|
|
|
public function getTooltipContentArray($params) |
804
|
|
|
{ |
805
|
|
|
global $langs; |
806
|
|
|
|
807
|
|
|
$langs->load('projects'); |
808
|
|
|
|
809
|
|
|
$datas = []; |
810
|
|
|
$datas['picto'] = img_picto('', $this->picto) . ' <u>' . $langs->trans("Task") . '</u>'; |
811
|
|
|
if (!empty($this->ref)) { |
812
|
|
|
$datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref; |
813
|
|
|
} |
814
|
|
|
if (!empty($this->label)) { |
815
|
|
|
$datas['label'] = '<br><b>' . $langs->trans('LabelTask') . ':</b> ' . $this->label; |
816
|
|
|
} |
817
|
|
|
if ($this->date_start || $this->date_end) { |
818
|
|
|
$datas['range'] = "<br>" . get_date_range($this->date_start, $this->date_end, '', $langs, 0); |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
return $datas; |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
/** |
825
|
|
|
* Return clicable name (with picto eventually) |
826
|
|
|
* |
827
|
|
|
* @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto |
828
|
|
|
* @param string $option 'withproject' or '' |
829
|
|
|
* @param string $mode Mode 'task', 'time', 'contact', 'note', document' define page to link to. |
830
|
|
|
* @param int $addlabel 0=Default, 1=Add label into string, >1=Add first chars into string |
831
|
|
|
* @param string $sep Separator between ref and label if option addlabel is set |
832
|
|
|
* @param int $notooltip 1=Disable tooltip |
833
|
|
|
* @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking |
834
|
|
|
* @return string Chaine avec URL |
835
|
|
|
*/ |
836
|
|
|
public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1) |
837
|
|
|
{ |
838
|
|
|
global $action, $conf, $hookmanager, $langs; |
839
|
|
|
|
840
|
|
|
if (!empty($conf->dol_no_mouse_hover)) { |
841
|
|
|
$notooltip = 1; // Force disable tooltips |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
$result = ''; |
845
|
|
|
$params = [ |
846
|
|
|
'id' => $this->id, |
847
|
|
|
'objecttype' => $this->element, |
848
|
|
|
]; |
849
|
|
|
$classfortooltip = 'classfortooltip'; |
850
|
|
|
$dataparams = ''; |
851
|
|
|
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { |
852
|
|
|
$classfortooltip = 'classforajaxtooltip'; |
853
|
|
|
$dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"'; |
854
|
|
|
$label = ''; |
855
|
|
|
} else { |
856
|
|
|
$label = implode($this->getTooltipContentArray($params)); |
857
|
|
|
} |
858
|
|
|
|
859
|
|
|
$url = constant('BASE_URL') . '/projet/tasks/' . $mode . '.php?id=' . $this->id . ($option == 'withproject' ? '&withproject=1' : ''); |
860
|
|
|
// Add param to save lastsearch_values or not |
861
|
|
|
$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); |
862
|
|
|
if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { |
863
|
|
|
$add_save_lastsearch_values = 1; |
864
|
|
|
} |
865
|
|
|
if ($add_save_lastsearch_values) { |
866
|
|
|
$url .= '&save_lastsearch_values=1'; |
867
|
|
|
} |
868
|
|
|
|
869
|
|
|
$linkclose = ''; |
870
|
|
|
if (empty($notooltip)) { |
871
|
|
|
if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) { |
872
|
|
|
$label = $langs->trans("ShowTask"); |
873
|
|
|
$linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"'; |
874
|
|
|
} |
875
|
|
|
$linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"'); |
876
|
|
|
$linkclose .= $dataparams . ' class="' . $classfortooltip . ' nowraponall"'; |
877
|
|
|
} else { |
878
|
|
|
$linkclose .= ' class="nowraponall"'; |
879
|
|
|
} |
880
|
|
|
|
881
|
|
|
$linkstart = '<a href="' . $url . '"'; |
882
|
|
|
$linkstart .= $linkclose . '>'; |
883
|
|
|
$linkend = '</a>'; |
884
|
|
|
|
885
|
|
|
$picto = 'projecttask'; |
886
|
|
|
|
887
|
|
|
$result .= $linkstart; |
888
|
|
|
if ($withpicto) { |
889
|
|
|
$result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1); |
890
|
|
|
} |
891
|
|
|
if ($withpicto != 2) { |
892
|
|
|
$result .= $this->ref; |
893
|
|
|
} |
894
|
|
|
$result .= $linkend; |
895
|
|
|
if ($withpicto != 2) { |
896
|
|
|
$result .= (($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
$parameters = array('id' => $this->id, 'getnomurl' => &$result); |
900
|
|
|
$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks |
901
|
|
|
if ($reshook > 0) { |
902
|
|
|
$result = $hookmanager->resPrint; |
903
|
|
|
} else { |
904
|
|
|
$result .= $hookmanager->resPrint; |
905
|
|
|
} |
906
|
|
|
|
907
|
|
|
return $result; |
908
|
|
|
} |
909
|
|
|
|
910
|
|
|
/** |
911
|
|
|
* Initialise an instance with random values. |
912
|
|
|
* Used to build previews or test instances. |
913
|
|
|
* id must be 0 if object instance is a specimen. |
914
|
|
|
* |
915
|
|
|
* @return int |
916
|
|
|
*/ |
917
|
|
|
public function initAsSpecimen() |
918
|
|
|
{ |
919
|
|
|
global $user; |
920
|
|
|
|
921
|
|
|
$this->id = 0; |
922
|
|
|
|
923
|
|
|
$this->fk_project = 0; |
924
|
|
|
$this->ref = 'TK01'; |
925
|
|
|
$this->fk_task_parent = 0; |
926
|
|
|
$this->label = 'Specimen task TK01'; |
927
|
|
|
$this->duration_effective = ''; |
928
|
|
|
$this->fk_user_creat = $user->id; |
929
|
|
|
$this->progress = '25'; |
930
|
|
|
$this->status = 0; |
931
|
|
|
$this->priority = 0; |
932
|
|
|
$this->note_private = 'This is a specimen private note'; |
933
|
|
|
$this->note_public = 'This is a specimen public note'; |
934
|
|
|
|
935
|
|
|
return 1; |
936
|
|
|
} |
937
|
|
|
|
938
|
|
|
/** |
939
|
|
|
* Return list of tasks for all projects or for one particular project |
940
|
|
|
* Sort order is on project, then on position of task, and last on start date of first level task |
941
|
|
|
* |
942
|
|
|
* @param User $usert Object user to limit tasks affected to a particular user |
943
|
|
|
* @param User $userp Object user to limit projects of a particular user and public projects |
944
|
|
|
* @param int $projectid Project id |
945
|
|
|
* @param int $socid Third party id |
946
|
|
|
* @param int $mode 0=Return list of tasks and their projects, 1=Return projects and tasks if exists |
947
|
|
|
* @param string $filteronproj Filter on project ref or label |
948
|
|
|
* @param string $filteronprojstatus Filter on project status ('-1'=no filter, '0,1'=Draft+Validated only) |
949
|
|
|
* @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...') |
950
|
|
|
* @param int $filteronprojuser Filter on user that is a contact of project |
951
|
|
|
* @param int $filterontaskuser Filter on user assigned to task |
952
|
|
|
* @param ?Extrafields $extrafields Show additional column from project or task |
953
|
|
|
* @param int $includebilltime Calculate also the time to bill and billed |
954
|
|
|
* @param array $search_array_options Array of search filters. Not Used yet. |
955
|
|
|
* @param int $loadextras Fetch all Extrafields on each project and task |
956
|
|
|
* @param int $loadRoleMode 1= will test Roles on task; 0 used in delete project action |
957
|
|
|
* @param string $sortfield Sort field |
958
|
|
|
* @param string $sortorder Sort order |
959
|
|
|
* @return array|string Array of tasks |
960
|
|
|
*/ |
961
|
|
|
public function getTasksArray($usert = null, $userp = null, $projectid = 0, $socid = 0, $mode = 0, $filteronproj = '', $filteronprojstatus = '-1', $morewherefilter = '', $filteronprojuser = 0, $filterontaskuser = 0, $extrafields = null, $includebilltime = 0, $search_array_options = array(), $loadextras = 0, $loadRoleMode = 1, $sortfield = '', $sortorder = '') |
962
|
|
|
{ |
963
|
|
|
global $hookmanager; |
964
|
|
|
|
965
|
|
|
$tasks = array(); |
966
|
|
|
|
967
|
|
|
//print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>'; |
968
|
|
|
|
969
|
|
|
// List of tasks (does not care about permissions. Filtering will be done later) |
970
|
|
|
$sql = "SELECT "; |
971
|
|
|
if ($filteronprojuser > 0 || $filterontaskuser > 0) { |
972
|
|
|
$sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task |
973
|
|
|
} |
974
|
|
|
$sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,"; |
975
|
|
|
$sql .= " t.rowid as taskid, t.ref as taskref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut as status,"; |
976
|
|
|
$sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,"; |
977
|
|
|
$sql .= " t.budget_amount,"; |
978
|
|
|
$sql .= " t.note_public, t.note_private,"; |
979
|
|
|
$sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,"; |
980
|
|
|
$sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount"; |
981
|
|
|
if ($loadextras) { // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line. |
982
|
|
|
if (!empty($extrafields->attributes['projet']['label'])) { |
983
|
|
|
foreach ($extrafields->attributes['projet']['label'] as $key => $val) { |
984
|
|
|
$sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp." . $key . " as options_" . $key : ''); |
985
|
|
|
} |
986
|
|
|
} |
987
|
|
|
if (!empty($extrafields->attributes['projet_task']['label'])) { |
988
|
|
|
foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) { |
989
|
|
|
$sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt." . $key . " as options_" . $key : ''); |
990
|
|
|
} |
991
|
|
|
} |
992
|
|
|
} |
993
|
|
|
if ($includebilltime) { |
994
|
|
|
$sql .= ", SUM(tt.element_duration * " . $this->db->ifsql("invoice_id IS NULL", "1", "0") . ") as tobill, SUM(tt.element_duration * " . $this->db->ifsql("invoice_id IS NULL", "0", "1") . ") as billed"; |
995
|
|
|
} |
996
|
|
|
|
997
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet as p"; |
998
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid"; |
999
|
|
|
if ($loadextras) { |
1000
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_extrafields as efp ON (p.rowid = efp.fk_object)"; |
1001
|
|
|
} |
1002
|
|
|
|
1003
|
|
|
if ($mode == 0) { |
1004
|
|
|
if ($filteronprojuser > 0) { |
1005
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec"; |
1006
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc"; |
1007
|
|
|
} |
1008
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "projet_task as t"; |
1009
|
|
|
if ($loadextras) { |
1010
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)"; |
1011
|
|
|
} |
1012
|
|
|
if ($includebilltime) { |
1013
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')"; |
1014
|
|
|
} |
1015
|
|
|
if ($filterontaskuser > 0) { |
1016
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec2"; |
1017
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc2"; |
1018
|
|
|
} |
1019
|
|
|
$sql .= " WHERE p.entity IN (" . getEntity('project') . ")"; |
1020
|
|
|
$sql .= " AND t.fk_projet = p.rowid"; |
1021
|
|
|
} elseif ($mode == 1) { |
1022
|
|
|
if ($filteronprojuser > 0) { |
1023
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec"; |
1024
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc"; |
1025
|
|
|
} |
1026
|
|
|
if ($filterontaskuser > 0) { |
1027
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "projet_task as t"; |
1028
|
|
|
if ($includebilltime) { |
1029
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')"; |
1030
|
|
|
} |
1031
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec2"; |
1032
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc2"; |
1033
|
|
|
} else { |
1034
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task as t on t.fk_projet = p.rowid"; |
1035
|
|
|
if ($includebilltime) { |
1036
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')"; |
1037
|
|
|
} |
1038
|
|
|
} |
1039
|
|
|
$sql .= " WHERE p.entity IN (" . getEntity('project') . ")"; |
1040
|
|
|
} else { |
1041
|
|
|
return 'BadValueForParameterMode'; |
1042
|
|
|
} |
1043
|
|
|
|
1044
|
|
|
if ($filteronprojuser > 0) { |
1045
|
|
|
$sql .= " AND p.rowid = ec.element_id"; |
1046
|
|
|
$sql .= " AND ctc.rowid = ec.fk_c_type_contact"; |
1047
|
|
|
$sql .= " AND ctc.element = 'project'"; |
1048
|
|
|
$sql .= " AND ec.fk_socpeople = " . ((int) $filteronprojuser); |
1049
|
|
|
$sql .= " AND ec.statut = 4"; |
1050
|
|
|
$sql .= " AND ctc.source = 'internal'"; |
1051
|
|
|
} |
1052
|
|
|
if ($filterontaskuser > 0) { |
1053
|
|
|
$sql .= " AND t.fk_projet = p.rowid"; |
1054
|
|
|
$sql .= " AND p.rowid = ec2.element_id"; |
1055
|
|
|
$sql .= " AND ctc2.rowid = ec2.fk_c_type_contact"; |
1056
|
|
|
$sql .= " AND ctc2.element = 'project_task'"; |
1057
|
|
|
$sql .= " AND ec2.fk_socpeople = " . ((int) $filterontaskuser); |
1058
|
|
|
$sql .= " AND ec2.statut = 4"; |
1059
|
|
|
$sql .= " AND ctc2.source = 'internal'"; |
1060
|
|
|
} |
1061
|
|
|
if ($socid) { |
1062
|
|
|
$sql .= " AND p.fk_soc = " . ((int) $socid); |
1063
|
|
|
} |
1064
|
|
|
if ($projectid) { |
1065
|
|
|
$sql .= " AND p.rowid IN (" . $this->db->sanitize($projectid) . ")"; |
1066
|
|
|
} |
1067
|
|
|
if ($filteronproj) { |
1068
|
|
|
$sql .= natural_search(array("p.ref", "p.title"), $filteronproj); |
1069
|
|
|
} |
1070
|
|
|
if ($filteronprojstatus && (int) $filteronprojstatus != '-1') { |
1071
|
|
|
$sql .= " AND p.fk_statut IN (" . $this->db->sanitize($filteronprojstatus) . ")"; |
1072
|
|
|
} |
1073
|
|
|
if ($morewherefilter) { |
1074
|
|
|
$sql .= $morewherefilter; |
1075
|
|
|
} |
1076
|
|
|
|
1077
|
|
|
// Add where from extra fields |
1078
|
|
|
$extrafieldsobjectkey = 'projet_task'; |
1079
|
|
|
$extrafieldsobjectprefix = 'efpt.'; |
1080
|
|
|
global $db, $conf; // needed for extrafields_list_search_sql.tpl |
1081
|
|
|
include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_sql.tpl.php'; |
1082
|
|
|
|
1083
|
|
|
// Add where from hooks |
1084
|
|
|
$parameters = array(); |
1085
|
|
|
$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook |
1086
|
|
|
$sql .= $hookmanager->resPrint; |
1087
|
|
|
if ($includebilltime) { |
1088
|
|
|
$sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,"; |
1089
|
|
|
$sql .= " t.datec, t.dateo, t.datee, t.tms,"; |
1090
|
|
|
$sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,"; |
1091
|
|
|
$sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,"; |
1092
|
|
|
$sql .= " t.budget_amount,"; |
1093
|
|
|
$sql .= " t.note_public, t.note_private,"; |
1094
|
|
|
$sql .= " s.rowid, s.nom, s.email,"; |
1095
|
|
|
$sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount"; |
1096
|
|
|
if ($loadextras) { |
1097
|
|
|
if (!empty($extrafields->attributes['projet']['label'])) { |
1098
|
|
|
foreach ($extrafields->attributes['projet']['label'] as $key => $val) { |
1099
|
|
|
$sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp." . $key : ''); |
1100
|
|
|
} |
1101
|
|
|
} |
1102
|
|
|
if (!empty($extrafields->attributes['projet_task']['label'])) { |
1103
|
|
|
foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) { |
1104
|
|
|
$sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt." . $key : ''); |
1105
|
|
|
} |
1106
|
|
|
} |
1107
|
|
|
} |
1108
|
|
|
} |
1109
|
|
|
|
1110
|
|
|
if ($sortfield && $sortorder) { |
1111
|
|
|
$sql .= $this->db->order($sortfield, $sortorder); |
1112
|
|
|
} else { |
1113
|
|
|
$sql .= " ORDER BY p.ref, t.rang, t.dateo"; |
1114
|
|
|
} |
1115
|
|
|
|
1116
|
|
|
//print $sql;exit; |
1117
|
|
|
dol_syslog(get_only_class($this) . "::getTasksArray", LOG_DEBUG); |
1118
|
|
|
$resql = $this->db->query($sql); |
1119
|
|
|
if ($resql) { |
1120
|
|
|
$num = $this->db->num_rows($resql); |
1121
|
|
|
$i = 0; |
1122
|
|
|
// Loop on each record found, so each couple (project id, task id) |
1123
|
|
|
while ($i < $num) { |
1124
|
|
|
$error = 0; |
1125
|
|
|
|
1126
|
|
|
$obj = $this->db->fetch_object($resql); |
1127
|
|
|
|
1128
|
|
|
if ($loadRoleMode) { |
1129
|
|
|
if ((!$obj->public) && (is_object($userp))) { // If not public project and we ask a filter on project owned by a user |
1130
|
|
|
if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) { |
1131
|
|
|
$error++; |
1132
|
|
|
} |
1133
|
|
|
} |
1134
|
|
|
if (is_object($usert)) { // If we ask a filter on a user affected to a task |
1135
|
|
|
if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) { |
1136
|
|
|
$error++; |
1137
|
|
|
} |
1138
|
|
|
} |
1139
|
|
|
} |
1140
|
|
|
|
1141
|
|
|
if (!$error) { |
1142
|
|
|
$tasks[$i] = new Task($this->db); |
1143
|
|
|
$tasks[$i]->id = $obj->taskid; |
1144
|
|
|
$tasks[$i]->ref = $obj->taskref; |
1145
|
|
|
$tasks[$i]->fk_project = $obj->projectid; |
1146
|
|
|
|
1147
|
|
|
// Data from project |
1148
|
|
|
$tasks[$i]->projectref = $obj->ref; |
1149
|
|
|
$tasks[$i]->projectlabel = $obj->plabel; |
1150
|
|
|
$tasks[$i]->projectstatus = $obj->projectstatus; |
1151
|
|
|
$tasks[$i]->fk_opp_status = $obj->fk_opp_status; |
1152
|
|
|
$tasks[$i]->opp_amount = $obj->opp_amount; |
1153
|
|
|
$tasks[$i]->opp_percent = $obj->opp_percent; |
1154
|
|
|
$tasks[$i]->budget_amount = $obj->budget_amount; |
1155
|
|
|
$tasks[$i]->project_budget_amount = $obj->project_budget_amount; |
1156
|
|
|
$tasks[$i]->usage_bill_time = $obj->usage_bill_time; |
1157
|
|
|
|
1158
|
|
|
$tasks[$i]->label = $obj->label; |
1159
|
|
|
$tasks[$i]->description = $obj->description; |
1160
|
|
|
|
1161
|
|
|
$tasks[$i]->fk_task_parent = $obj->fk_task_parent; |
1162
|
|
|
$tasks[$i]->note_public = $obj->note_public; |
1163
|
|
|
$tasks[$i]->note_private = $obj->note_private; |
1164
|
|
|
$tasks[$i]->duration_effective = $obj->duration_effective; |
1165
|
|
|
$tasks[$i]->planned_workload = $obj->planned_workload; |
1166
|
|
|
|
1167
|
|
|
if ($includebilltime) { |
1168
|
|
|
// Data summed from element_time linked to task |
1169
|
|
|
$tasks[$i]->tobill = $obj->tobill; |
1170
|
|
|
$tasks[$i]->billed = $obj->billed; |
1171
|
|
|
} |
1172
|
|
|
|
1173
|
|
|
$tasks[$i]->progress = $obj->progress; |
1174
|
|
|
$tasks[$i]->fk_statut = $obj->status; |
1175
|
|
|
$tasks[$i]->status = $obj->status; |
1176
|
|
|
$tasks[$i]->public = $obj->public; |
1177
|
|
|
$tasks[$i]->date_start = $this->db->jdate($obj->date_start); |
1178
|
|
|
$tasks[$i]->date_end = $this->db->jdate($obj->date_end); |
1179
|
|
|
$tasks[$i]->rang = $obj->rang; |
1180
|
|
|
$tasks[$i]->priority = $obj->priority; |
1181
|
|
|
|
1182
|
|
|
$tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility |
1183
|
|
|
$tasks[$i]->thirdparty_id = $obj->thirdparty_id; |
1184
|
|
|
$tasks[$i]->thirdparty_name = $obj->thirdparty_name; |
1185
|
|
|
$tasks[$i]->thirdparty_email = $obj->thirdparty_email; |
1186
|
|
|
|
1187
|
|
|
if ($loadextras) { |
1188
|
|
|
if (!empty($extrafields->attributes['projet']['label'])) { |
1189
|
|
|
foreach ($extrafields->attributes['projet']['label'] as $key => $val) { |
1190
|
|
|
if ($extrafields->attributes['projet']['type'][$key] != 'separate') { |
1191
|
|
|
$tmpvar = 'options_' . $key; |
1192
|
|
|
$tasks[$i]->array_options_project['options_' . $key] = $obj->$tmpvar; |
1193
|
|
|
} |
1194
|
|
|
} |
1195
|
|
|
} |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
|
|
if ($loadextras) { |
1199
|
|
|
$tasks[$i]->fetch_optionals(); |
1200
|
|
|
} |
1201
|
|
|
} |
1202
|
|
|
|
1203
|
|
|
$i++; |
1204
|
|
|
} |
1205
|
|
|
$this->db->free($resql); |
1206
|
|
|
} else { |
1207
|
|
|
dol_print_error($this->db); |
1208
|
|
|
} |
1209
|
|
|
|
1210
|
|
|
return $tasks; |
1211
|
|
|
} |
1212
|
|
|
|
1213
|
|
|
/** |
1214
|
|
|
* Return list of roles for a user for each projects or each tasks (or a particular project or a particular task). |
1215
|
|
|
* |
1216
|
|
|
* @param User|null $userp Return roles on project for this internal user. If set, usert and taskid must not be defined. |
1217
|
|
|
* @param User|null $usert Return roles on task for this internal user. If set userp must NOT be defined. -1 means no filter. |
1218
|
|
|
* @param string $projectid Project id list separated with , to filter on project |
1219
|
|
|
* @param int $taskid Task id to filter on a task |
1220
|
|
|
* @param integer $filteronprojstatus Filter on project status if userp is set. Not used if userp not defined. |
1221
|
|
|
* @return array|int Array (projectid => 'list of roles for project' or taskid => 'list of roles for task') |
1222
|
|
|
*/ |
1223
|
|
|
public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1) |
1224
|
|
|
{ |
1225
|
|
|
$arrayroles = array(); |
1226
|
|
|
|
1227
|
|
|
dol_syslog(get_only_class($this) . "::getUserRolesForProjectsOrTasks userp=" . json_encode(is_object($userp)) . " usert=" . json_encode(is_object($usert)) . " projectid=" . $projectid . " taskid=" . $taskid); |
1228
|
|
|
|
1229
|
|
|
// We want role of user for a projet or role of user for a task. Both are not possible. |
1230
|
|
|
if (empty($userp) && empty($usert)) { |
1231
|
|
|
$this->error = "CallWithWrongParameters"; |
1232
|
|
|
return -1; |
1233
|
|
|
} |
1234
|
|
|
if (!empty($userp) && !empty($usert)) { |
1235
|
|
|
$this->error = "CallWithWrongParameters"; |
1236
|
|
|
return -1; |
1237
|
|
|
} |
1238
|
|
|
|
1239
|
|
|
/* Liste des taches et role sur les projects ou taches */ |
1240
|
|
|
$sql = "SELECT "; |
1241
|
|
|
if ($userp) { |
1242
|
|
|
$sql .= " p.rowid as pid,"; |
1243
|
|
|
} else { |
1244
|
|
|
$sql .= " pt.rowid as pid,"; |
1245
|
|
|
} |
1246
|
|
|
$sql .= " ec.element_id, ctc.code, ctc.source"; |
1247
|
|
|
if ($userp) { |
1248
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet as p"; |
1249
|
|
|
} |
1250
|
|
|
if ($usert && $filteronprojstatus > -1) { |
1251
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet as p, " . MAIN_DB_PREFIX . "projet_task as pt"; |
1252
|
|
|
} |
1253
|
|
|
if ($usert && $filteronprojstatus <= -1) { |
1254
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet_task as pt"; |
1255
|
|
|
} |
1256
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec"; |
1257
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc"; |
1258
|
|
|
if ($userp) { |
1259
|
|
|
$sql .= " WHERE p.rowid = ec.element_id"; |
1260
|
|
|
} else { |
1261
|
|
|
$sql .= " WHERE pt.rowid = ec.element_id"; |
1262
|
|
|
} |
1263
|
|
|
if ($userp && $filteronprojstatus > -1) { |
1264
|
|
|
$sql .= " AND p.fk_statut = " . ((int) $filteronprojstatus); |
1265
|
|
|
} |
1266
|
|
|
if ($usert && $filteronprojstatus > -1) { |
1267
|
|
|
$sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = " . ((int) $filteronprojstatus); |
1268
|
|
|
} |
1269
|
|
|
if ($userp) { |
1270
|
|
|
$sql .= " AND ctc.element = 'project'"; |
1271
|
|
|
} |
1272
|
|
|
if ($usert) { |
1273
|
|
|
$sql .= " AND ctc.element = 'project_task'"; |
1274
|
|
|
} |
1275
|
|
|
$sql .= " AND ctc.rowid = ec.fk_c_type_contact"; |
1276
|
|
|
if ($userp) { |
1277
|
|
|
$sql .= " AND ec.fk_socpeople = " . ((int) $userp->id); |
1278
|
|
|
} |
1279
|
|
|
if ($usert) { |
1280
|
|
|
$sql .= " AND ec.fk_socpeople = " . ((int) $usert->id); |
1281
|
|
|
} |
1282
|
|
|
$sql .= " AND ec.statut = 4"; |
1283
|
|
|
$sql .= " AND ctc.source = 'internal'"; |
1284
|
|
|
if ($projectid) { |
1285
|
|
|
if ($userp) { |
1286
|
|
|
$sql .= " AND p.rowid IN (" . $this->db->sanitize($projectid) . ")"; |
1287
|
|
|
} |
1288
|
|
|
if ($usert) { |
1289
|
|
|
$sql .= " AND pt.fk_projet IN (" . $this->db->sanitize($projectid) . ")"; |
1290
|
|
|
} |
1291
|
|
|
} |
1292
|
|
|
if ($taskid) { |
1293
|
|
|
if ($userp) { |
1294
|
|
|
$sql .= " ERROR SHOULD NOT HAPPENS"; |
1295
|
|
|
} |
1296
|
|
|
if ($usert) { |
1297
|
|
|
$sql .= " AND pt.rowid = " . ((int) $taskid); |
1298
|
|
|
} |
1299
|
|
|
} |
1300
|
|
|
//print $sql; |
1301
|
|
|
|
1302
|
|
|
dol_syslog(get_only_class($this) . "::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG); |
1303
|
|
|
$resql = $this->db->query($sql); |
1304
|
|
|
if ($resql) { |
1305
|
|
|
$num = $this->db->num_rows($resql); |
1306
|
|
|
$i = 0; |
1307
|
|
|
while ($i < $num) { |
1308
|
|
|
$obj = $this->db->fetch_object($resql); |
1309
|
|
|
if (empty($arrayroles[$obj->pid])) { |
1310
|
|
|
$arrayroles[$obj->pid] = $obj->code; |
1311
|
|
|
} else { |
1312
|
|
|
$arrayroles[$obj->pid] .= ',' . $obj->code; |
1313
|
|
|
} |
1314
|
|
|
$i++; |
1315
|
|
|
} |
1316
|
|
|
$this->db->free($resql); |
1317
|
|
|
} else { |
1318
|
|
|
dol_print_error($this->db); |
1319
|
|
|
} |
1320
|
|
|
|
1321
|
|
|
return $arrayroles; |
1322
|
|
|
} |
1323
|
|
|
|
1324
|
|
|
|
1325
|
|
|
/** |
1326
|
|
|
* Return list of id of contacts of task |
1327
|
|
|
* |
1328
|
|
|
* @param string $source Source |
1329
|
|
|
* @return array Array of id of contacts |
1330
|
|
|
*/ |
1331
|
|
|
public function getListContactId($source = 'internal') |
1332
|
|
|
{ |
1333
|
|
|
$contactAlreadySelected = array(); |
1334
|
|
|
$tab = $this->liste_contact(-1, $source); |
1335
|
|
|
//var_dump($tab); |
1336
|
|
|
$num = count($tab); |
1337
|
|
|
$i = 0; |
1338
|
|
|
while ($i < $num) { |
1339
|
|
|
if ($source == 'thirdparty') { |
1340
|
|
|
$contactAlreadySelected[$i] = $tab[$i]['socid']; |
1341
|
|
|
} else { |
1342
|
|
|
$contactAlreadySelected[$i] = $tab[$i]['id']; |
1343
|
|
|
} |
1344
|
|
|
$i++; |
1345
|
|
|
} |
1346
|
|
|
return $contactAlreadySelected; |
1347
|
|
|
} |
1348
|
|
|
|
1349
|
|
|
/** |
1350
|
|
|
* Merge contact of tasks |
1351
|
|
|
* |
1352
|
|
|
* @param int $origin_id Old task id |
1353
|
|
|
* @param int $dest_id New task id |
1354
|
|
|
* @return bool |
1355
|
|
|
*/ |
1356
|
|
|
public function mergeContactTask($origin_id, $dest_id) |
1357
|
|
|
{ |
1358
|
|
|
$error = 0; |
1359
|
|
|
$origintask = new Task($this->db); |
1360
|
|
|
$result = $origintask->fetch($origin_id); |
1361
|
|
|
if ($result <= 0) { |
1362
|
|
|
return false; |
1363
|
|
|
} |
1364
|
|
|
|
1365
|
|
|
//Get list of origin contacts |
1366
|
|
|
$arraycontactorigin = array_merge($origintask->liste_contact(-1, 'internal'), $origintask->liste_contact(-1, 'external')); |
1367
|
|
|
if (is_array($arraycontactorigin)) { |
1368
|
|
|
foreach ($arraycontactorigin as $key => $contact) { |
1369
|
|
|
$result = $this->add_contact($contact["id"], $contact["fk_c_type_contact"], $contact["source"]); |
1370
|
|
|
if ($result < 0) { |
1371
|
|
|
return false; |
1372
|
|
|
} |
1373
|
|
|
} |
1374
|
|
|
} |
1375
|
|
|
return true; |
1376
|
|
|
} |
1377
|
|
|
|
1378
|
|
|
/** |
1379
|
|
|
* Merge time spent of tasks |
1380
|
|
|
* |
1381
|
|
|
* @param int $origin_id Old task id |
1382
|
|
|
* @param int $dest_id New task id |
1383
|
|
|
* @return bool |
1384
|
|
|
*/ |
1385
|
|
|
public function mergeTimeSpentTask($origin_id, $dest_id) |
1386
|
|
|
{ |
1387
|
|
|
$ret = true; |
1388
|
|
|
|
1389
|
|
|
$this->db->begin(); |
1390
|
|
|
|
1391
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "element_time as et"; |
1392
|
|
|
$sql .= " SET et.fk_element = " . ((int) $dest_id); |
1393
|
|
|
$sql .= " WHERE et.elementtype = 'task'"; |
1394
|
|
|
$sql .= " AND et.fk_element = " . ((int) $origin_id); |
1395
|
|
|
|
1396
|
|
|
dol_syslog(get_only_class($this) . "::mergeTimeSpentTask", LOG_DEBUG); |
1397
|
|
|
if (!$this->db->query($sql)) { |
1398
|
|
|
$this->error = $this->db->lasterror(); |
1399
|
|
|
$ret = false; |
1400
|
|
|
} |
1401
|
|
|
|
1402
|
|
|
if ($ret) { |
1403
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task"; |
1404
|
|
|
$sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM " . MAIN_DB_PREFIX . "element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = " . ((int) $dest_id) . ")"; |
1405
|
|
|
$sql .= " WHERE rowid = " . ((int) $dest_id); |
1406
|
|
|
|
1407
|
|
|
dol_syslog(get_only_class($this) . "::mergeTimeSpentTask update project_task", LOG_DEBUG); |
1408
|
|
|
if (!$this->db->query($sql)) { |
1409
|
|
|
$this->error = $this->db->lasterror(); |
1410
|
|
|
$ret = false; |
1411
|
|
|
} |
1412
|
|
|
} |
1413
|
|
|
|
1414
|
|
|
if ($ret == true) { |
|
|
|
|
1415
|
|
|
$this->db->commit(); |
1416
|
|
|
} else { |
1417
|
|
|
$this->db->rollback(); |
1418
|
|
|
} |
1419
|
|
|
return $ret; |
1420
|
|
|
} |
1421
|
|
|
|
1422
|
|
|
/** |
1423
|
|
|
* Add time spent |
1424
|
|
|
* |
1425
|
|
|
* @param User $user User object |
1426
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
1427
|
|
|
* @return int Return integer <=0 if KO, >0 if OK |
1428
|
|
|
*/ |
1429
|
|
|
public function addTimeSpent($user, $notrigger = 0) |
1430
|
|
|
{ |
1431
|
|
|
global $langs; |
1432
|
|
|
|
1433
|
|
|
dol_syslog(get_only_class($this) . "::addTimeSpent", LOG_DEBUG); |
1434
|
|
|
|
1435
|
|
|
$ret = 0; |
1436
|
|
|
$now = dol_now(); |
1437
|
|
|
|
1438
|
|
|
// Check parameters |
1439
|
|
|
if (!is_object($user)) { |
1440
|
|
|
dol_print_error(null, "Method addTimeSpent was called with wrong parameter user"); |
1441
|
|
|
return -1; |
1442
|
|
|
} |
1443
|
|
|
|
1444
|
|
|
// Clean parameters |
1445
|
|
|
if (isset($this->timespent_note)) { |
1446
|
|
|
$this->timespent_note = trim($this->timespent_note); |
1447
|
|
|
} |
1448
|
|
|
if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) { |
1449
|
|
|
$this->timespent_datehour = $this->timespent_date; |
1450
|
|
|
} |
1451
|
|
|
|
1452
|
|
|
if (getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) { |
1453
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php'; |
1454
|
|
|
$restrictBefore = dol_time_plus_duree(dol_now(), - getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'), 'm'); |
1455
|
|
|
|
1456
|
|
|
if ($this->timespent_date < $restrictBefore) { |
1457
|
|
|
$this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')); |
1458
|
|
|
$this->errors[] = $this->error; |
1459
|
|
|
return -1; |
1460
|
|
|
} |
1461
|
|
|
} |
1462
|
|
|
|
1463
|
|
|
$this->db->begin(); |
1464
|
|
|
|
1465
|
|
|
$timespent = new TimeSpent($this->db); |
1466
|
|
|
$timespent->fk_element = $this->id; |
1467
|
|
|
$timespent->elementtype = 'task'; |
1468
|
|
|
$timespent->element_date = $this->timespent_date; |
1469
|
|
|
$timespent->element_datehour = $this->timespent_datehour; |
1470
|
|
|
$timespent->element_date_withhour = $this->timespent_withhour; |
1471
|
|
|
$timespent->element_duration = $this->timespent_duration; |
1472
|
|
|
$timespent->fk_user = $this->timespent_fk_user; |
1473
|
|
|
$timespent->fk_product = $this->timespent_fk_product; |
1474
|
|
|
$timespent->note = $this->timespent_note; |
1475
|
|
|
$timespent->datec = $this->db->idate($now); |
1476
|
|
|
|
1477
|
|
|
$result = $timespent->create($user); |
1478
|
|
|
|
1479
|
|
|
if ($result > 0) { |
1480
|
|
|
$ret = $result; |
1481
|
|
|
$this->timespent_id = $result; |
1482
|
|
|
|
1483
|
|
|
if (!$notrigger) { |
1484
|
|
|
// Call trigger |
1485
|
|
|
$result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user); |
1486
|
|
|
if ($result < 0) { |
1487
|
|
|
$ret = -1; |
1488
|
|
|
} |
1489
|
|
|
// End call triggers |
1490
|
|
|
} |
1491
|
|
|
} else { |
1492
|
|
|
$this->error = $this->db->lasterror(); |
1493
|
|
|
$ret = -1; |
1494
|
|
|
} |
1495
|
|
|
|
1496
|
|
|
if ($ret > 0) { |
1497
|
|
|
// Recalculate amount of time spent for task and update denormalized field |
1498
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task"; |
1499
|
|
|
$sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM " . MAIN_DB_PREFIX . "element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = " . ((int) $this->id) . ")"; |
1500
|
|
|
if (isset($this->progress)) { |
1501
|
|
|
$sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided |
1502
|
|
|
} |
1503
|
|
|
$sql .= " WHERE rowid = " . ((int) $this->id); |
1504
|
|
|
|
1505
|
|
|
dol_syslog(get_only_class($this) . "::addTimeSpent", LOG_DEBUG); |
1506
|
|
|
if (!$this->db->query($sql)) { |
1507
|
|
|
$this->error = $this->db->lasterror(); |
1508
|
|
|
$ret = -2; |
1509
|
|
|
} |
1510
|
|
|
|
1511
|
|
|
// Update hourly rate of this time spent entry |
1512
|
|
|
$resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user)); |
1513
|
|
|
if (!empty($resql_thm_user)) { |
1514
|
|
|
$obj_thm_user = $this->db->fetch_object($resql_thm_user); |
1515
|
|
|
$timespent->thm = $obj_thm_user->thm; |
1516
|
|
|
} |
1517
|
|
|
$res_update = $timespent->update($user); |
1518
|
|
|
|
1519
|
|
|
dol_syslog(get_only_class($this) . "::addTimeSpent", LOG_DEBUG); |
1520
|
|
|
if ($res_update <= 0) { |
1521
|
|
|
$this->error = $this->db->lasterror(); |
1522
|
|
|
$ret = -2; |
1523
|
|
|
} |
1524
|
|
|
} |
1525
|
|
|
|
1526
|
|
|
if ($ret > 0) { |
1527
|
|
|
$this->db->commit(); |
1528
|
|
|
} else { |
1529
|
|
|
$this->db->rollback(); |
1530
|
|
|
} |
1531
|
|
|
return $ret; |
1532
|
|
|
} |
1533
|
|
|
|
1534
|
|
|
/** |
1535
|
|
|
* Fetch records of time spent of this task |
1536
|
|
|
* |
1537
|
|
|
* @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...') |
1538
|
|
|
* @return int Return integer <0 if KO, array of time spent if OK |
1539
|
|
|
*/ |
1540
|
|
|
public function fetchTimeSpentOnTask($morewherefilter = '') |
1541
|
|
|
{ |
1542
|
|
|
$arrayres = array(); |
1543
|
|
|
|
1544
|
|
|
$sql = "SELECT"; |
1545
|
|
|
$sql .= " s.rowid as socid,"; |
1546
|
|
|
$sql .= " s.nom as thirdparty_name,"; |
1547
|
|
|
$sql .= " s.email as thirdparty_email,"; |
1548
|
|
|
$sql .= " ptt.rowid,"; |
1549
|
|
|
$sql .= " ptt.ref_ext,"; |
1550
|
|
|
$sql .= " ptt.fk_element as fk_task,"; |
1551
|
|
|
$sql .= " ptt.element_date as task_date,"; |
1552
|
|
|
$sql .= " ptt.element_datehour as task_datehour,"; |
1553
|
|
|
$sql .= " ptt.element_date_withhour as task_date_withhour,"; |
1554
|
|
|
$sql .= " ptt.element_duration as task_duration,"; |
1555
|
|
|
$sql .= " ptt.fk_user,"; |
1556
|
|
|
$sql .= " ptt.note,"; |
1557
|
|
|
$sql .= " ptt.thm,"; |
1558
|
|
|
$sql .= " pt.rowid as task_id,"; |
1559
|
|
|
$sql .= " pt.ref as task_ref,"; |
1560
|
|
|
$sql .= " pt.label as task_label,"; |
1561
|
|
|
$sql .= " p.rowid as project_id,"; |
1562
|
|
|
$sql .= " p.ref as project_ref,"; |
1563
|
|
|
$sql .= " p.title as project_label,"; |
1564
|
|
|
$sql .= " p.public as public"; |
1565
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet as p"; |
1566
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid"; |
1567
|
|
|
$sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid"; |
1568
|
|
|
$sql .= " AND ptt.elementtype = 'task'"; |
1569
|
|
|
$sql .= " AND pt.rowid = " . ((int) $this->id); |
1570
|
|
|
$sql .= " AND pt.entity IN (" . getEntity('project') . ")"; |
1571
|
|
|
if ($morewherefilter) { |
1572
|
|
|
$sql .= $morewherefilter; |
1573
|
|
|
} |
1574
|
|
|
|
1575
|
|
|
dol_syslog(get_only_class($this) . "::fetchAllTimeSpent", LOG_DEBUG); |
1576
|
|
|
$resql = $this->db->query($sql); |
1577
|
|
|
if ($resql) { |
1578
|
|
|
$num = $this->db->num_rows($resql); |
1579
|
|
|
|
1580
|
|
|
$i = 0; |
1581
|
|
|
while ($i < $num) { |
1582
|
|
|
$obj = $this->db->fetch_object($resql); |
1583
|
|
|
|
1584
|
|
|
$newobj = new stdClass(); |
1585
|
|
|
|
1586
|
|
|
$newobj->socid = $obj->socid; |
1587
|
|
|
$newobj->thirdparty_name = $obj->thirdparty_name; |
1588
|
|
|
$newobj->thirdparty_email = $obj->thirdparty_email; |
1589
|
|
|
|
1590
|
|
|
$newobj->fk_project = $obj->project_id; |
1591
|
|
|
$newobj->project_ref = $obj->project_ref; |
1592
|
|
|
$newobj->project_label = $obj->project_label; |
1593
|
|
|
$newobj->public = $obj->project_public; |
1594
|
|
|
|
1595
|
|
|
$newobj->fk_task = $obj->task_id; |
1596
|
|
|
$newobj->task_ref = $obj->task_ref; |
1597
|
|
|
$newobj->task_label = $obj->task_label; |
1598
|
|
|
|
1599
|
|
|
$newobj->timespent_line_id = $obj->rowid; |
1600
|
|
|
$newobj->timespent_line_ref_ext = $obj->ref_ext; |
1601
|
|
|
$newobj->timespent_line_date = $this->db->jdate($obj->task_date); |
1602
|
|
|
$newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour); |
1603
|
|
|
$newobj->timespent_line_withhour = $obj->task_date_withhour; |
1604
|
|
|
$newobj->timespent_line_duration = $obj->task_duration; |
1605
|
|
|
$newobj->timespent_line_fk_user = $obj->fk_user; |
1606
|
|
|
$newobj->timespent_line_thm = $obj->thm; // hourly rate |
1607
|
|
|
$newobj->timespent_line_note = $obj->note; |
1608
|
|
|
|
1609
|
|
|
$arrayres[] = $newobj; |
1610
|
|
|
|
1611
|
|
|
$i++; |
1612
|
|
|
} |
1613
|
|
|
|
1614
|
|
|
$this->db->free($resql); |
1615
|
|
|
|
1616
|
|
|
$this->lines = $arrayres; |
1617
|
|
|
return 1; |
1618
|
|
|
} else { |
1619
|
|
|
dol_print_error($this->db); |
1620
|
|
|
$this->error = "Error " . $this->db->lasterror(); |
1621
|
|
|
return -1; |
1622
|
|
|
} |
1623
|
|
|
} |
1624
|
|
|
|
1625
|
|
|
|
1626
|
|
|
/** |
1627
|
|
|
* Calculate total of time spent for task |
1628
|
|
|
* |
1629
|
|
|
* @param User|int $userobj Filter on user. null or 0=No filter |
1630
|
|
|
* @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...') |
1631
|
|
|
* @return array|int Array of info for task array('min_date', 'max_date', 'total_duration', 'total_amount', 'nblines', 'nblinesnull') |
1632
|
|
|
*/ |
1633
|
|
|
public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '') |
1634
|
|
|
{ |
1635
|
|
|
if (is_object($userobj)) { |
1636
|
|
|
$userid = $userobj->id; |
1637
|
|
|
} else { |
1638
|
|
|
$userid = $userobj; // old method |
1639
|
|
|
} |
1640
|
|
|
|
1641
|
|
|
$id = $this->id; |
1642
|
|
|
if (empty($id) && empty($userid)) { |
1643
|
|
|
dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR); |
1644
|
|
|
return -1; |
1645
|
|
|
} |
1646
|
|
|
|
1647
|
|
|
$result = array(); |
1648
|
|
|
|
1649
|
|
|
$sql = "SELECT"; |
1650
|
|
|
$sql .= " MIN(t.element_datehour) as min_date,"; |
1651
|
|
|
$sql .= " MAX(t.element_datehour) as max_date,"; |
1652
|
|
|
$sql .= " SUM(t.element_duration) as total_duration,"; |
1653
|
|
|
$sql .= " SUM(t.element_duration / 3600 * " . $this->db->ifsql("t.thm IS NULL", 0, "t.thm") . ") as total_amount,"; |
1654
|
|
|
$sql .= " COUNT(t.rowid) as nblines,"; |
1655
|
|
|
$sql .= " SUM(" . $this->db->ifsql("t.thm IS NULL", 1, 0) . ") as nblinesnull"; |
1656
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "element_time as t"; |
1657
|
|
|
$sql .= " WHERE t.elementtype='task'"; |
1658
|
|
|
if ($morewherefilter) { |
1659
|
|
|
$sql .= $morewherefilter; |
1660
|
|
|
} |
1661
|
|
|
if ($id > 0) { |
1662
|
|
|
$sql .= " AND t.fk_element = " . ((int) $id); |
1663
|
|
|
} |
1664
|
|
|
if ($userid > 0) { |
1665
|
|
|
$sql .= " AND t.fk_user = " . ((int) $userid); |
1666
|
|
|
} |
1667
|
|
|
|
1668
|
|
|
dol_syslog(get_only_class($this) . "::getSummaryOfTimeSpent", LOG_DEBUG); |
1669
|
|
|
$resql = $this->db->query($sql); |
1670
|
|
|
if ($resql) { |
1671
|
|
|
$obj = $this->db->fetch_object($resql); |
1672
|
|
|
|
1673
|
|
|
$result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead |
1674
|
|
|
$result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead |
1675
|
|
|
$result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead |
1676
|
|
|
|
1677
|
|
|
$this->timespent_min_date = $this->db->jdate($obj->min_date); |
1678
|
|
|
$this->timespent_max_date = $this->db->jdate($obj->max_date); |
1679
|
|
|
$this->timespent_total_duration = $obj->total_duration; |
1680
|
|
|
$this->timespent_total_amount = $obj->total_amount; |
1681
|
|
|
$this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0); |
1682
|
|
|
$this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0); |
1683
|
|
|
|
1684
|
|
|
$this->db->free($resql); |
1685
|
|
|
} else { |
1686
|
|
|
dol_print_error($this->db); |
1687
|
|
|
} |
1688
|
|
|
return $result; |
1689
|
|
|
} |
1690
|
|
|
|
1691
|
|
|
/** |
1692
|
|
|
* Calculate quantity and value of time consumed using the thm (hourly amount value of work for user entering time) |
1693
|
|
|
* |
1694
|
|
|
* @param User|string $fuser Filter on a dedicated user |
1695
|
|
|
* @param string $dates Start date (ex 00:00:00) |
1696
|
|
|
* @param string $datee End date (ex 23:59:59) |
1697
|
|
|
* @return array Array of info for task array('amount','nbseconds','nblinesnull') |
1698
|
|
|
*/ |
1699
|
|
|
public function getSumOfAmount($fuser = '', $dates = '', $datee = '') |
1700
|
|
|
{ |
1701
|
|
|
$id = $this->id; |
1702
|
|
|
|
1703
|
|
|
$result = array(); |
1704
|
|
|
|
1705
|
|
|
$sql = "SELECT"; |
1706
|
|
|
$sql .= " SUM(t.element_duration) as nbseconds,"; |
1707
|
|
|
$sql .= " SUM(t.element_duration / 3600 * " . $this->db->ifsql("t.thm IS NULL", 0, "t.thm") . ") as amount, SUM(" . $this->db->ifsql("t.thm IS NULL", 1, 0) . ") as nblinesnull"; |
1708
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "element_time as t"; |
1709
|
|
|
$sql .= " WHERE t.elementtype='task' AND t.fk_element = " . ((int) $id); |
1710
|
|
|
if (is_object($fuser) && $fuser->id > 0) { |
1711
|
|
|
$sql .= " AND fk_user = " . ((int) $fuser->id); |
1712
|
|
|
} |
1713
|
|
|
if ($dates > 0) { |
1714
|
|
|
$datefieldname = "element_datehour"; |
1715
|
|
|
$sql .= " AND (" . $datefieldname . " >= '" . $this->db->idate($dates) . "' OR " . $datefieldname . " IS NULL)"; |
1716
|
|
|
} |
1717
|
|
|
if ($datee > 0) { |
1718
|
|
|
$datefieldname = "element_datehour"; |
1719
|
|
|
$sql .= " AND (" . $datefieldname . " <= '" . $this->db->idate($datee) . "' OR " . $datefieldname . " IS NULL)"; |
1720
|
|
|
} |
1721
|
|
|
//print $sql; |
1722
|
|
|
|
1723
|
|
|
dol_syslog(get_only_class($this) . "::getSumOfAmount", LOG_DEBUG); |
1724
|
|
|
$resql = $this->db->query($sql); |
1725
|
|
|
if ($resql) { |
1726
|
|
|
$obj = $this->db->fetch_object($resql); |
1727
|
|
|
|
1728
|
|
|
$result['amount'] = $obj->amount; |
1729
|
|
|
$result['nbseconds'] = $obj->nbseconds; |
1730
|
|
|
$result['nblinesnull'] = $obj->nblinesnull; |
1731
|
|
|
|
1732
|
|
|
$this->db->free($resql); |
1733
|
|
|
return $result; |
1734
|
|
|
} else { |
1735
|
|
|
dol_print_error($this->db); |
1736
|
|
|
return $result; |
1737
|
|
|
} |
1738
|
|
|
} |
1739
|
|
|
|
1740
|
|
|
/** |
1741
|
|
|
* Load properties of timespent of a task from the time spent ID. |
1742
|
|
|
* |
1743
|
|
|
* @param int $id Id in time spent table |
1744
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1745
|
|
|
*/ |
1746
|
|
|
public function fetchTimeSpent($id) |
1747
|
|
|
{ |
1748
|
|
|
$timespent = new TimeSpent($this->db); |
1749
|
|
|
$timespent->fetch($id); |
1750
|
|
|
|
1751
|
|
|
dol_syslog(get_only_class($this) . "::fetchTimeSpent", LOG_DEBUG); |
1752
|
|
|
|
1753
|
|
|
if ($timespent->id > 0) { |
1754
|
|
|
$this->timespent_id = $timespent->id; |
1755
|
|
|
$this->id = $timespent->fk_element; |
1756
|
|
|
$this->timespent_date = $timespent->element_date; |
1757
|
|
|
$this->timespent_datehour = $timespent->element_datehour; |
1758
|
|
|
$this->timespent_withhour = $timespent->element_date_withhour; |
1759
|
|
|
$this->timespent_duration = $timespent->element_duration; |
1760
|
|
|
$this->timespent_fk_user = $timespent->fk_user; |
1761
|
|
|
$this->timespent_fk_product = $timespent->fk_product; |
1762
|
|
|
$this->timespent_thm = $timespent->thm; // hourly rate |
1763
|
|
|
$this->timespent_note = $timespent->note; |
1764
|
|
|
|
1765
|
|
|
return 1; |
1766
|
|
|
} |
1767
|
|
|
|
1768
|
|
|
return 0; |
1769
|
|
|
} |
1770
|
|
|
|
1771
|
|
|
/** |
1772
|
|
|
* Load all records of time spent |
1773
|
|
|
* |
1774
|
|
|
* @param User $userobj User object |
1775
|
|
|
* @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...') |
1776
|
|
|
* @return array|int Return integer <0 if KO, array of time spent if OK |
1777
|
|
|
*/ |
1778
|
|
|
public function fetchAllTimeSpent(User $userobj, $morewherefilter = '') |
1779
|
|
|
{ |
1780
|
|
|
$arrayres = array(); |
1781
|
|
|
|
1782
|
|
|
$sql = "SELECT"; |
1783
|
|
|
$sql .= " s.rowid as socid,"; |
1784
|
|
|
$sql .= " s.nom as thirdparty_name,"; |
1785
|
|
|
$sql .= " s.email as thirdparty_email,"; |
1786
|
|
|
$sql .= " ptt.rowid,"; |
1787
|
|
|
$sql .= " ptt.fk_element as fk_task,"; |
1788
|
|
|
$sql .= " ptt.element_date as task_date,"; |
1789
|
|
|
$sql .= " ptt.element_datehour as task_datehour,"; |
1790
|
|
|
$sql .= " ptt.element_date_withhour as task_date_withhour,"; |
1791
|
|
|
$sql .= " ptt.element_duration as task_duration,"; |
1792
|
|
|
$sql .= " ptt.fk_user,"; |
1793
|
|
|
$sql .= " ptt.note,"; |
1794
|
|
|
$sql .= " ptt.thm,"; |
1795
|
|
|
$sql .= " pt.rowid as task_id,"; |
1796
|
|
|
$sql .= " pt.ref as task_ref,"; |
1797
|
|
|
$sql .= " pt.label as task_label,"; |
1798
|
|
|
$sql .= " p.rowid as project_id,"; |
1799
|
|
|
$sql .= " p.ref as project_ref,"; |
1800
|
|
|
$sql .= " p.title as project_label,"; |
1801
|
|
|
$sql .= " p.public as public"; |
1802
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet as p"; |
1803
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid"; |
1804
|
|
|
$sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid"; |
1805
|
|
|
$sql .= " AND ptt.elementtype = 'task'"; |
1806
|
|
|
$sql .= " AND ptt.fk_user = " . ((int) $userobj->id); |
1807
|
|
|
$sql .= " AND pt.entity IN (" . getEntity('project') . ")"; |
1808
|
|
|
if ($morewherefilter) { |
1809
|
|
|
$sql .= $morewherefilter; |
1810
|
|
|
} |
1811
|
|
|
|
1812
|
|
|
dol_syslog(get_only_class($this) . "::fetchAllTimeSpent", LOG_DEBUG); |
1813
|
|
|
$resql = $this->db->query($sql); |
1814
|
|
|
if ($resql) { |
1815
|
|
|
$num = $this->db->num_rows($resql); |
1816
|
|
|
|
1817
|
|
|
$i = 0; |
1818
|
|
|
while ($i < $num) { |
1819
|
|
|
$obj = $this->db->fetch_object($resql); |
1820
|
|
|
|
1821
|
|
|
$newobj = new stdClass(); |
1822
|
|
|
|
1823
|
|
|
$newobj->socid = $obj->socid; |
1824
|
|
|
$newobj->thirdparty_name = $obj->thirdparty_name; |
1825
|
|
|
$newobj->thirdparty_email = $obj->thirdparty_email; |
1826
|
|
|
|
1827
|
|
|
$newobj->fk_project = $obj->project_id; |
1828
|
|
|
$newobj->project_ref = $obj->project_ref; |
1829
|
|
|
$newobj->project_label = $obj->project_label; |
1830
|
|
|
$newobj->public = $obj->project_public; |
1831
|
|
|
|
1832
|
|
|
$newobj->fk_task = $obj->task_id; |
1833
|
|
|
$newobj->task_ref = $obj->task_ref; |
1834
|
|
|
$newobj->task_label = $obj->task_label; |
1835
|
|
|
|
1836
|
|
|
$newobj->timespent_id = $obj->rowid; |
1837
|
|
|
$newobj->timespent_date = $this->db->jdate($obj->task_date); |
1838
|
|
|
$newobj->timespent_datehour = $this->db->jdate($obj->task_datehour); |
1839
|
|
|
$newobj->timespent_withhour = $obj->task_date_withhour; |
1840
|
|
|
$newobj->timespent_duration = $obj->task_duration; |
1841
|
|
|
$newobj->timespent_fk_user = $obj->fk_user; |
1842
|
|
|
$newobj->timespent_thm = $obj->thm; // hourly rate |
1843
|
|
|
$newobj->timespent_note = $obj->note; |
1844
|
|
|
|
1845
|
|
|
$arrayres[] = $newobj; |
1846
|
|
|
|
1847
|
|
|
$i++; |
1848
|
|
|
} |
1849
|
|
|
|
1850
|
|
|
$this->db->free($resql); |
1851
|
|
|
} else { |
1852
|
|
|
dol_print_error($this->db); |
1853
|
|
|
$this->error = "Error " . $this->db->lasterror(); |
1854
|
|
|
return -1; |
1855
|
|
|
} |
1856
|
|
|
|
1857
|
|
|
return $arrayres; |
1858
|
|
|
} |
1859
|
|
|
|
1860
|
|
|
/** |
1861
|
|
|
* Update time spent |
1862
|
|
|
* |
1863
|
|
|
* @param User $user User id |
1864
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
1865
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1866
|
|
|
*/ |
1867
|
|
|
public function updateTimeSpent($user, $notrigger = 0) |
1868
|
|
|
{ |
1869
|
|
|
global $conf, $langs; |
1870
|
|
|
|
1871
|
|
|
$ret = 0; |
1872
|
|
|
|
1873
|
|
|
// Check parameters |
1874
|
|
|
if ($this->timespent_date == '') { |
1875
|
|
|
$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date")); |
1876
|
|
|
return -1; |
1877
|
|
|
} |
1878
|
|
|
if (!($this->timespent_fk_user > 0)) { |
1879
|
|
|
$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User")); |
1880
|
|
|
return -1; |
1881
|
|
|
} |
1882
|
|
|
|
1883
|
|
|
// Clean parameters |
1884
|
|
|
if (empty($this->timespent_datehour)) { |
1885
|
|
|
$this->timespent_datehour = $this->timespent_date; |
1886
|
|
|
} |
1887
|
|
|
if (isset($this->timespent_note)) { |
1888
|
|
|
$this->timespent_note = trim($this->timespent_note); |
1889
|
|
|
} |
1890
|
|
|
|
1891
|
|
|
if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) { |
1892
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php'; |
1893
|
|
|
$restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm'); |
1894
|
|
|
|
1895
|
|
|
if ($this->timespent_date < $restrictBefore) { |
1896
|
|
|
$this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')); |
1897
|
|
|
$this->errors[] = $this->error; |
1898
|
|
|
return -1; |
1899
|
|
|
} |
1900
|
|
|
} |
1901
|
|
|
|
1902
|
|
|
$this->db->begin(); |
1903
|
|
|
|
1904
|
|
|
$timespent = new TimeSpent($this->db); |
1905
|
|
|
$timespent->fetch($this->timespent_id); |
1906
|
|
|
$timespent->element_date = $this->timespent_date; |
1907
|
|
|
$timespent->element_datehour = $this->timespent_datehour; |
1908
|
|
|
$timespent->element_date_withhour = $this->timespent_withhour; |
1909
|
|
|
$timespent->element_duration = $this->timespent_duration; |
1910
|
|
|
$timespent->fk_user = $this->timespent_fk_user; |
1911
|
|
|
$timespent->fk_product = $this->timespent_fk_product; |
1912
|
|
|
$timespent->note = $this->timespent_note; |
1913
|
|
|
$timespent->invoice_id = $this->timespent_invoiceid; |
1914
|
|
|
$timespent->invoice_line_id = $this->timespent_invoicelineid; |
1915
|
|
|
|
1916
|
|
|
dol_syslog(get_only_class($this) . "::updateTimeSpent", LOG_DEBUG); |
1917
|
|
|
if ($timespent->update($user) > 0) { |
1918
|
|
|
if (!$notrigger) { |
1919
|
|
|
// Call trigger |
1920
|
|
|
$result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user); |
1921
|
|
|
if ($result < 0) { |
1922
|
|
|
$this->db->rollback(); |
1923
|
|
|
$ret = -1; |
1924
|
|
|
} else { |
1925
|
|
|
$ret = 1; |
1926
|
|
|
} |
1927
|
|
|
// End call triggers |
1928
|
|
|
} else { |
1929
|
|
|
$ret = 1; |
1930
|
|
|
} |
1931
|
|
|
} else { |
1932
|
|
|
$this->error = $this->db->lasterror(); |
1933
|
|
|
$this->db->rollback(); |
1934
|
|
|
$ret = -1; |
1935
|
|
|
} |
1936
|
|
|
|
1937
|
|
|
if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) { |
1938
|
|
|
if ($this->timespent_old_duration != $this->timespent_duration) { |
1939
|
|
|
// Recalculate amount of time spent for task and update denormalized field |
1940
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task"; |
1941
|
|
|
$sql .= " SET duration_effective = (SELECT SUM(element_duration) FROM " . MAIN_DB_PREFIX . "element_time as ptt where ptt.elementtype = 'task' AND ptt.fk_element = " . ((int) $this->id) . ")"; |
1942
|
|
|
if (isset($this->progress)) { |
1943
|
|
|
$sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided |
1944
|
|
|
} |
1945
|
|
|
$sql .= " WHERE rowid = " . ((int) $this->id); |
1946
|
|
|
|
1947
|
|
|
dol_syslog(get_only_class($this) . "::updateTimeSpent", LOG_DEBUG); |
1948
|
|
|
if (!$this->db->query($sql)) { |
1949
|
|
|
$this->error = $this->db->lasterror(); |
1950
|
|
|
$this->db->rollback(); |
1951
|
|
|
$ret = -2; |
1952
|
|
|
} |
1953
|
|
|
} |
1954
|
|
|
|
1955
|
|
|
// Update hourly rate of this time spent entry, but only if it was not set initially |
1956
|
|
|
$res_update = 1; |
1957
|
|
|
if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) { |
1958
|
|
|
$resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user)); |
1959
|
|
|
if (!empty($resql_thm_user)) { |
1960
|
|
|
$obj_thm_user = $this->db->fetch_object($resql_thm_user); |
1961
|
|
|
$timespent->thm = $obj_thm_user->thm; |
1962
|
|
|
} |
1963
|
|
|
$res_update = $timespent->update($user); |
1964
|
|
|
} |
1965
|
|
|
|
1966
|
|
|
dol_syslog(get_only_class($this) . "::updateTimeSpent", LOG_DEBUG); |
1967
|
|
|
if ($res_update <= 0) { |
1968
|
|
|
$this->error = $this->db->lasterror(); |
1969
|
|
|
$ret = -2; |
1970
|
|
|
} |
1971
|
|
|
} |
1972
|
|
|
|
1973
|
|
|
if ($ret >= 0) { |
1974
|
|
|
$this->db->commit(); |
1975
|
|
|
} |
1976
|
|
|
return $ret; |
1977
|
|
|
} |
1978
|
|
|
|
1979
|
|
|
/** |
1980
|
|
|
* Delete time spent |
1981
|
|
|
* |
1982
|
|
|
* @param User $user User that delete |
1983
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
1984
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1985
|
|
|
*/ |
1986
|
|
|
public function delTimeSpent($user, $notrigger = 0) |
1987
|
|
|
{ |
1988
|
|
|
global $conf, $langs; |
1989
|
|
|
|
1990
|
|
|
$error = 0; |
1991
|
|
|
|
1992
|
|
|
if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) { |
1993
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php'; |
1994
|
|
|
$restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm'); |
1995
|
|
|
|
1996
|
|
|
if ($this->timespent_date < $restrictBefore) { |
1997
|
|
|
$this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')); |
1998
|
|
|
$this->errors[] = $this->error; |
1999
|
|
|
return -1; |
2000
|
|
|
} |
2001
|
|
|
} |
2002
|
|
|
|
2003
|
|
|
$this->db->begin(); |
2004
|
|
|
|
2005
|
|
|
if (!$notrigger) { |
2006
|
|
|
// Call trigger |
2007
|
|
|
$result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user); |
2008
|
|
|
if ($result < 0) { |
2009
|
|
|
$error++; |
2010
|
|
|
} |
2011
|
|
|
// End call triggers |
2012
|
|
|
} |
2013
|
|
|
|
2014
|
|
|
if (!$error) { |
2015
|
|
|
$timespent = new TimeSpent($this->db); |
2016
|
|
|
$timespent->fetch($this->timespent_id); |
2017
|
|
|
|
2018
|
|
|
$res_del = $timespent->delete($user); |
2019
|
|
|
|
2020
|
|
|
if ($res_del < 0) { |
2021
|
|
|
$error++; |
2022
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
2023
|
|
|
} |
2024
|
|
|
} |
2025
|
|
|
|
2026
|
|
|
if (!$error) { |
2027
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task"; |
2028
|
|
|
$sql .= " SET duration_effective = duration_effective - " . $this->db->escape($this->timespent_duration ? $this->timespent_duration : 0); |
2029
|
|
|
$sql .= " WHERE rowid = " . ((int) $this->id); |
2030
|
|
|
|
2031
|
|
|
dol_syslog(get_only_class($this) . "::delTimeSpent", LOG_DEBUG); |
2032
|
|
|
if ($this->db->query($sql)) { |
2033
|
|
|
$result = 0; |
2034
|
|
|
} else { |
2035
|
|
|
$this->error = $this->db->lasterror(); |
2036
|
|
|
$result = -2; |
2037
|
|
|
} |
2038
|
|
|
} |
2039
|
|
|
|
2040
|
|
|
// Commit or rollback |
2041
|
|
|
if ($error) { |
2042
|
|
|
foreach ($this->errors as $errmsg) { |
2043
|
|
|
dol_syslog(get_only_class($this) . "::delTimeSpent " . $errmsg, LOG_ERR); |
2044
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
2045
|
|
|
} |
2046
|
|
|
$this->db->rollback(); |
2047
|
|
|
return -1 * $error; |
2048
|
|
|
} else { |
2049
|
|
|
$this->db->commit(); |
2050
|
|
|
return 1; |
2051
|
|
|
} |
2052
|
|
|
} |
2053
|
|
|
|
2054
|
|
|
/** Load an object from its id and create a new one in database |
2055
|
|
|
* |
2056
|
|
|
* @param User $user User making the clone |
2057
|
|
|
* @param int $fromid Id of object to clone |
2058
|
|
|
* @param int $project_id Id of project to attach clone task |
2059
|
|
|
* @param int $parent_task_id Id of task to attach clone task |
2060
|
|
|
* @param bool $clone_change_dt recalculate date of task regarding new project start date |
2061
|
|
|
* @param bool $clone_affectation clone affectation of project |
2062
|
|
|
* @param bool $clone_time clone time of project |
2063
|
|
|
* @param bool $clone_file clone file of project |
2064
|
|
|
* @param bool $clone_note clone note of project |
2065
|
|
|
* @param bool $clone_prog clone progress of project |
2066
|
|
|
* @return int New id of clone |
2067
|
|
|
*/ |
2068
|
|
|
public function createFromClone(User $user, $fromid, $project_id, $parent_task_id, $clone_change_dt = false, $clone_affectation = false, $clone_time = false, $clone_file = false, $clone_note = false, $clone_prog = false) |
2069
|
|
|
{ |
2070
|
|
|
global $langs, $conf; |
2071
|
|
|
|
2072
|
|
|
$error = 0; |
2073
|
|
|
|
2074
|
|
|
//Use 00:00 of today if time is use on task. |
2075
|
|
|
$now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y')); |
2076
|
|
|
|
2077
|
|
|
$datec = $now; |
2078
|
|
|
|
2079
|
|
|
$clone_task = new Task($this->db); |
2080
|
|
|
$origin_task = new Task($this->db); |
2081
|
|
|
|
2082
|
|
|
$clone_task->context['createfromclone'] = 'createfromclone'; |
2083
|
|
|
|
2084
|
|
|
$this->db->begin(); |
2085
|
|
|
|
2086
|
|
|
// Load source object |
2087
|
|
|
$clone_task->fetch($fromid); |
2088
|
|
|
$clone_task->fetch_optionals(); |
2089
|
|
|
//var_dump($clone_task->array_options);exit; |
2090
|
|
|
|
2091
|
|
|
$origin_task->fetch($fromid); |
2092
|
|
|
|
2093
|
|
|
$defaultref = ''; |
2094
|
|
|
$obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON; |
2095
|
|
|
if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON') . ".php")) { |
2096
|
|
|
require_once DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON') . '.php'; |
2097
|
|
|
$modTask = new $obj(); |
2098
|
|
|
$defaultref = $modTask->getNextValue(0, $clone_task); |
2099
|
|
|
} |
2100
|
|
|
|
2101
|
|
|
$ori_project_id = $clone_task->fk_project; |
2102
|
|
|
|
2103
|
|
|
$clone_task->id = 0; |
2104
|
|
|
$clone_task->ref = $defaultref; |
2105
|
|
|
$clone_task->fk_project = $project_id; |
2106
|
|
|
$clone_task->fk_task_parent = $parent_task_id; |
2107
|
|
|
$clone_task->date_c = $datec; |
2108
|
|
|
$clone_task->planned_workload = $origin_task->planned_workload; |
2109
|
|
|
$clone_task->rang = $origin_task->rang; |
2110
|
|
|
$clone_task->priority = $origin_task->priority; |
2111
|
|
|
|
2112
|
|
|
//Manage Task Date |
2113
|
|
|
if ($clone_change_dt) { |
2114
|
|
|
$projectstatic = new Project($this->db); |
2115
|
|
|
$projectstatic->fetch($ori_project_id); |
2116
|
|
|
|
2117
|
|
|
//Origin project start date |
2118
|
|
|
$orign_project_dt_start = $projectstatic->date_start; |
2119
|
|
|
|
2120
|
|
|
//Calculate new task start date with difference between origin proj start date and origin task start date |
2121
|
|
|
if (!empty($clone_task->date_start)) { |
2122
|
|
|
$clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start; |
2123
|
|
|
} |
2124
|
|
|
|
2125
|
|
|
//Calculate new task end date with difference between origin proj end date and origin task end date |
2126
|
|
|
if (!empty($clone_task->date_end)) { |
2127
|
|
|
$clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start; |
2128
|
|
|
} |
2129
|
|
|
} |
2130
|
|
|
|
2131
|
|
|
if (!$clone_prog) { |
2132
|
|
|
$clone_task->progress = 0; |
2133
|
|
|
} |
2134
|
|
|
|
2135
|
|
|
// Create clone |
2136
|
|
|
$result = $clone_task->create($user); |
2137
|
|
|
|
2138
|
|
|
// Other options |
2139
|
|
|
if ($result < 0) { |
2140
|
|
|
$this->error = $clone_task->error; |
2141
|
|
|
$error++; |
2142
|
|
|
} |
2143
|
|
|
// End |
2144
|
|
|
if ($error) { |
2145
|
|
|
$clone_task_id = 0; // For static tool check |
2146
|
|
|
} else { |
2147
|
|
|
$clone_task_id = $clone_task->id; |
2148
|
|
|
$clone_task_ref = $clone_task->ref; |
2149
|
|
|
|
2150
|
|
|
//Note Update |
2151
|
|
|
if (!$clone_note) { |
2152
|
|
|
$clone_task->note_private = ''; |
2153
|
|
|
$clone_task->note_public = ''; |
2154
|
|
|
} else { |
2155
|
|
|
$this->db->begin(); |
2156
|
|
|
$res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public'); |
2157
|
|
|
if ($res < 0) { |
2158
|
|
|
$this->error .= $clone_task->error; |
2159
|
|
|
$error++; |
2160
|
|
|
$this->db->rollback(); |
2161
|
|
|
} else { |
2162
|
|
|
$this->db->commit(); |
2163
|
|
|
} |
2164
|
|
|
|
2165
|
|
|
$this->db->begin(); |
2166
|
|
|
$res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private'); |
2167
|
|
|
if ($res < 0) { |
2168
|
|
|
$this->error .= $clone_task->error; |
2169
|
|
|
$error++; |
2170
|
|
|
$this->db->rollback(); |
2171
|
|
|
} else { |
2172
|
|
|
$this->db->commit(); |
2173
|
|
|
} |
2174
|
|
|
} |
2175
|
|
|
|
2176
|
|
|
//Duplicate file |
2177
|
|
|
if ($clone_file) { |
2178
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php'; |
2179
|
|
|
|
2180
|
|
|
//retrieve project origin ref to know folder to copy |
2181
|
|
|
$projectstatic = new Project($this->db); |
2182
|
|
|
$projectstatic->fetch($ori_project_id); |
2183
|
|
|
$ori_project_ref = $projectstatic->ref; |
2184
|
|
|
|
2185
|
|
|
if ($ori_project_id != $project_id) { |
2186
|
|
|
$projectstatic->fetch($project_id); |
2187
|
|
|
$clone_project_ref = $projectstatic->ref; |
2188
|
|
|
} else { |
2189
|
|
|
$clone_project_ref = $ori_project_ref; |
2190
|
|
|
} |
2191
|
|
|
|
2192
|
|
|
$clone_task_dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($clone_project_ref) . "/" . dol_sanitizeFileName($clone_task_ref); |
2193
|
|
|
$ori_task_dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($ori_project_ref) . "/" . dol_sanitizeFileName($fromid); |
2194
|
|
|
|
2195
|
|
|
$filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1); |
2196
|
|
|
foreach ($filearray as $key => $file) { |
2197
|
|
|
if (!file_exists($clone_task_dir)) { |
2198
|
|
|
if (dol_mkdir($clone_task_dir) < 0) { |
2199
|
|
|
$this->error .= $langs->trans('ErrorInternalErrorDetected') . ':dol_mkdir'; |
2200
|
|
|
$error++; |
2201
|
|
|
} |
2202
|
|
|
} |
2203
|
|
|
|
2204
|
|
|
$rescopy = dol_copy($ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name'], 0, 1); |
2205
|
|
|
if (is_numeric($rescopy) && $rescopy < 0) { |
2206
|
|
|
$this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name']); |
2207
|
|
|
$error++; |
2208
|
|
|
} |
2209
|
|
|
} |
2210
|
|
|
} |
2211
|
|
|
|
2212
|
|
|
// clone affectation |
2213
|
|
|
if ($clone_affectation) { |
2214
|
|
|
$origin_task = new Task($this->db); |
2215
|
|
|
$origin_task->fetch($fromid); |
2216
|
|
|
|
2217
|
|
|
foreach (array('internal', 'external') as $source) { |
2218
|
|
|
$tab = $origin_task->liste_contact(-1, $source); |
2219
|
|
|
$num = count($tab); |
2220
|
|
|
$i = 0; |
2221
|
|
|
while ($i < $num) { |
2222
|
|
|
$clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']); |
2223
|
|
|
if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') { |
2224
|
|
|
$langs->load("errors"); |
2225
|
|
|
$this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); |
2226
|
|
|
$error++; |
2227
|
|
|
} else { |
2228
|
|
|
if ($clone_task->error != '') { |
2229
|
|
|
$this->error .= $clone_task->error; |
2230
|
|
|
$error++; |
2231
|
|
|
} |
2232
|
|
|
} |
2233
|
|
|
$i++; |
2234
|
|
|
} |
2235
|
|
|
} |
2236
|
|
|
} |
2237
|
|
|
|
2238
|
|
|
if ($clone_time) { |
2239
|
|
|
//TODO clone time of affectation |
2240
|
|
|
} |
2241
|
|
|
} |
2242
|
|
|
|
2243
|
|
|
unset($clone_task->context['createfromclone']); |
2244
|
|
|
|
2245
|
|
|
if (!$error) { |
2246
|
|
|
$this->db->commit(); |
2247
|
|
|
return $clone_task_id; |
2248
|
|
|
} else { |
2249
|
|
|
$this->db->rollback(); |
2250
|
|
|
dol_syslog(get_only_class($this) . "::createFromClone nbError: " . $error . " error : " . $this->error, LOG_ERR); |
2251
|
|
|
return -1; |
2252
|
|
|
} |
2253
|
|
|
} |
2254
|
|
|
|
2255
|
|
|
|
2256
|
|
|
/** |
2257
|
|
|
* Return status label of object |
2258
|
|
|
* |
2259
|
|
|
* @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto |
2260
|
|
|
* @return string Label |
2261
|
|
|
*/ |
2262
|
|
|
public function getLibStatut($mode = 0) |
2263
|
|
|
{ |
2264
|
|
|
return $this->LibStatut($this->status, $mode); |
2265
|
|
|
} |
2266
|
|
|
|
2267
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2268
|
|
|
/** |
2269
|
|
|
* Return status label for an object |
2270
|
|
|
* |
2271
|
|
|
* @param int $status Id status |
2272
|
|
|
* @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto |
2273
|
|
|
* @return string Label |
2274
|
|
|
*/ |
2275
|
|
|
public function LibStatut($status, $mode = 0) |
2276
|
|
|
{ |
2277
|
|
|
// phpcs:enable |
2278
|
|
|
global $langs; |
2279
|
|
|
|
2280
|
|
|
// list of Statut of the task |
2281
|
|
|
$this->labelStatus[0] = 'Draft'; |
2282
|
|
|
$this->labelStatus[1] = 'ToDo'; |
2283
|
|
|
$this->labelStatus[2] = 'Running'; |
2284
|
|
|
$this->labelStatus[3] = 'Finish'; |
2285
|
|
|
$this->labelStatus[4] = 'Transfered'; |
2286
|
|
|
$this->labelStatusShort[0] = 'Draft'; |
2287
|
|
|
$this->labelStatusShort[1] = 'ToDo'; |
2288
|
|
|
$this->labelStatusShort[2] = 'Running'; |
2289
|
|
|
$this->labelStatusShort[3] = 'Completed'; |
2290
|
|
|
$this->labelStatusShort[4] = 'Transfered'; |
2291
|
|
|
|
2292
|
|
|
if ($mode == 0) { |
2293
|
|
|
return $langs->trans($this->labelStatus[$status]); |
2294
|
|
|
} elseif ($mode == 1) { |
2295
|
|
|
return $langs->trans($this->labelStatusShort[$status]); |
2296
|
|
|
} elseif ($mode == 2) { |
2297
|
|
|
if ($status == 0) { |
2298
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0') . ' ' . $langs->trans($this->labelStatusShort[$status]); |
2299
|
|
|
} elseif ($status == 1) { |
2300
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1') . ' ' . $langs->trans($this->labelStatusShort[$status]); |
2301
|
|
|
} elseif ($status == 2) { |
2302
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3') . ' ' . $langs->trans($this->labelStatusShort[$status]); |
2303
|
|
|
} elseif ($status == 3) { |
2304
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatusShort[$status]); |
2305
|
|
|
} elseif ($status == 4) { |
2306
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatusShort[$status]); |
2307
|
|
|
} elseif ($status == 5) { |
2308
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5') . ' ' . $langs->trans($this->labelStatusShort[$status]); |
2309
|
|
|
} |
2310
|
|
|
} elseif ($mode == 3) { |
2311
|
|
|
if ($status == 0) { |
2312
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0'); |
2313
|
|
|
} elseif ($status == 1) { |
2314
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1'); |
2315
|
|
|
} elseif ($status == 2) { |
2316
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3'); |
2317
|
|
|
} elseif ($status == 3) { |
2318
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6'); |
2319
|
|
|
} elseif ($status == 4) { |
2320
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6'); |
2321
|
|
|
} elseif ($status == 5) { |
2322
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5'); |
2323
|
|
|
} |
2324
|
|
|
} elseif ($mode == 4) { |
2325
|
|
|
if ($status == 0) { |
2326
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0') . ' ' . $langs->trans($this->labelStatus[$status]); |
2327
|
|
|
} elseif ($status == 1) { |
2328
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1') . ' ' . $langs->trans($this->labelStatus[$status]); |
2329
|
|
|
} elseif ($status == 2) { |
2330
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3') . ' ' . $langs->trans($this->labelStatus[$status]); |
2331
|
|
|
} elseif ($status == 3) { |
2332
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatus[$status]); |
2333
|
|
|
} elseif ($status == 4) { |
2334
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatus[$status]); |
2335
|
|
|
} elseif ($status == 5) { |
2336
|
|
|
return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5') . ' ' . $langs->trans($this->labelStatus[$status]); |
2337
|
|
|
} |
2338
|
|
|
} elseif ($mode == 5) { |
2339
|
|
|
/*if ($status==0) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0'); |
2340
|
|
|
elseif ($status==1) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1'); |
2341
|
|
|
elseif ($status==2) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3'); |
2342
|
|
|
elseif ($status==3) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6'); |
2343
|
|
|
elseif ($status==4) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6'); |
2344
|
|
|
elseif ($status==5) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5'); |
2345
|
|
|
*/ |
2346
|
|
|
//else return $this->progress.' %'; |
2347
|
|
|
return ' '; |
2348
|
|
|
} elseif ($mode == 6) { |
2349
|
|
|
/*if ($status==0) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0'); |
2350
|
|
|
elseif ($status==1) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1'); |
2351
|
|
|
elseif ($status==2) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3'); |
2352
|
|
|
elseif ($status==3) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6'); |
2353
|
|
|
elseif ($status==4) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6'); |
2354
|
|
|
elseif ($status==5) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5'); |
2355
|
|
|
*/ |
2356
|
|
|
//else return $this->progress.' %'; |
2357
|
|
|
return ' '; |
2358
|
|
|
} |
2359
|
|
|
return ""; |
2360
|
|
|
} |
2361
|
|
|
|
2362
|
|
|
/** |
2363
|
|
|
* Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF |
2364
|
|
|
* |
2365
|
|
|
* @param string $modele force le modele a utiliser ('' par default) |
2366
|
|
|
* @param Translate $outputlangs object lang a utiliser pour traduction |
2367
|
|
|
* @param int $hidedetails Hide details of lines |
2368
|
|
|
* @param int $hidedesc Hide description |
2369
|
|
|
* @param int $hideref Hide ref |
2370
|
|
|
* @return int 0 if KO, 1 if OK |
2371
|
|
|
*/ |
2372
|
|
|
public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0) |
2373
|
|
|
{ |
2374
|
|
|
$outputlangs->load("projects"); |
2375
|
|
|
|
2376
|
|
|
if (!dol_strlen($modele)) { |
2377
|
|
|
$modele = 'nodefault'; |
2378
|
|
|
|
2379
|
|
|
if (!empty($this->model_pdf)) { |
2380
|
|
|
$modele = $this->model_pdf; |
2381
|
|
|
} elseif (getDolGlobalString('PROJECT_TASK_ADDON_PDF')) { |
2382
|
|
|
$modele = getDolGlobalString('PROJECT_TASK_ADDON_PDF'); |
2383
|
|
|
} |
2384
|
|
|
} |
2385
|
|
|
|
2386
|
|
|
$modelpath = "core/modules/project/task/doc/"; |
2387
|
|
|
|
2388
|
|
|
return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); |
2389
|
|
|
} |
2390
|
|
|
|
2391
|
|
|
|
2392
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2393
|
|
|
/** |
2394
|
|
|
* Load indicators for dashboard (this->nbtodo and this->nbtodolate) |
2395
|
|
|
* |
2396
|
|
|
* @param User $user Object user |
2397
|
|
|
* @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK |
2398
|
|
|
*/ |
2399
|
|
|
public function load_board($user) |
2400
|
|
|
{ |
2401
|
|
|
// phpcs:enable |
2402
|
|
|
global $conf, $langs; |
2403
|
|
|
|
2404
|
|
|
// For external user, no check is done on company because readability is managed by public status of project and assignment. |
2405
|
|
|
//$socid = $user->socid; |
2406
|
|
|
$socid = 0; |
2407
|
|
|
|
2408
|
|
|
$projectstatic = new Project($this->db); |
2409
|
|
|
$projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid); |
2410
|
|
|
|
2411
|
|
|
// List of tasks (does not care about permissions. Filtering will be done later) |
2412
|
|
|
$sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,"; |
2413
|
|
|
$sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,"; |
2414
|
|
|
$sql .= " t.dateo as date_start, t.datee as date_end"; |
2415
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet as p"; |
2416
|
|
|
//$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid"; |
2417
|
|
|
//if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; |
2418
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "projet_task as t"; |
2419
|
|
|
$sql .= " WHERE p.entity IN (" . getEntity('project', 0) . ')'; |
2420
|
|
|
$sql .= " AND p.fk_statut = 1"; |
2421
|
|
|
$sql .= " AND t.fk_projet = p.rowid"; |
2422
|
|
|
$sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do |
2423
|
|
|
if (!$user->hasRight('projet', 'all', 'lire')) { |
2424
|
|
|
$sql .= " AND p.rowid IN (" . $this->db->sanitize($projectsListId) . ")"; |
2425
|
|
|
} |
2426
|
|
|
// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser |
2427
|
|
|
//if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")"; |
2428
|
|
|
// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser |
2429
|
|
|
// if (! $user->rights->societe->client->voir && ! $socid) $sql.= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id).") OR (s.rowid IS NULL))"; |
2430
|
|
|
|
2431
|
|
|
//print $sql; |
2432
|
|
|
$resql = $this->db->query($sql); |
2433
|
|
|
if ($resql) { |
2434
|
|
|
$task_static = new Task($this->db); |
2435
|
|
|
|
2436
|
|
|
$response = new WorkboardResponse(); |
2437
|
|
|
$response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24; |
2438
|
|
|
$response->label = $langs->trans("OpenedTasks"); |
2439
|
|
|
if ($user->hasRight("projet", "all", "lire")) { |
2440
|
|
|
$response->url = constant('BASE_URL') . '/projet/tasks/list.php?mainmenu=project'; |
2441
|
|
|
} else { |
2442
|
|
|
$response->url = constant('BASE_URL') . '/projet/tasks/list.php?mode=mine&mainmenu=project'; |
2443
|
|
|
} |
2444
|
|
|
$response->img = img_object('', "task"); |
2445
|
|
|
|
2446
|
|
|
// This assignment in condition is not a bug. It allows walking the results. |
2447
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
2448
|
|
|
$response->nbtodo++; |
2449
|
|
|
|
2450
|
|
|
$task_static->projectstatus = $obj->projectstatus; |
2451
|
|
|
$task_static->progress = $obj->progress; |
2452
|
|
|
$task_static->fk_statut = $obj->status; |
|
|
|
|
2453
|
|
|
$task_static->status = $obj->status; |
2454
|
|
|
$task_static->date_start = $this->db->jdate($obj->date_start); |
2455
|
|
|
$task_static->date_end = $this->db->jdate($obj->date_end); |
2456
|
|
|
|
2457
|
|
|
if ($task_static->hasDelay()) { |
2458
|
|
|
$response->nbtodolate++; |
2459
|
|
|
} |
2460
|
|
|
} |
2461
|
|
|
|
2462
|
|
|
return $response; |
2463
|
|
|
} else { |
2464
|
|
|
$this->error = $this->db->error(); |
2465
|
|
|
return -1; |
2466
|
|
|
} |
2467
|
|
|
} |
2468
|
|
|
|
2469
|
|
|
|
2470
|
|
|
/** |
2471
|
|
|
* Load indicators this->nb for state board |
2472
|
|
|
* |
2473
|
|
|
* @return int Return integer <0 if ko, >0 if ok |
2474
|
|
|
*/ |
2475
|
|
|
public function loadStateBoard() |
2476
|
|
|
{ |
2477
|
|
|
global $user; |
2478
|
|
|
|
2479
|
|
|
$mine = 0; |
2480
|
|
|
$socid = $user->socid; |
2481
|
|
|
|
2482
|
|
|
$projectstatic = new Project($this->db); |
2483
|
|
|
$projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid); |
2484
|
|
|
|
2485
|
|
|
// List of tasks (does not care about permissions. Filtering will be done later) |
2486
|
|
|
$sql = "SELECT count(p.rowid) as nb"; |
2487
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "projet as p"; |
2488
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s on p.fk_soc = s.rowid"; |
2489
|
|
|
if (!$user->hasRight('societe', 'client', 'voir')) { |
2490
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON sc.fk_soc = s.rowid"; |
2491
|
|
|
} |
2492
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "projet_task as t"; |
2493
|
|
|
$sql .= " WHERE p.entity IN (" . getEntity('project', 0) . ')'; |
2494
|
|
|
$sql .= " AND t.fk_projet = p.rowid"; // tasks to do |
2495
|
|
|
if ($mine || !$user->hasRight('projet', 'all', 'lire')) { |
2496
|
|
|
$sql .= " AND p.rowid IN (" . $this->db->sanitize($projectsListId) . ")"; |
2497
|
|
|
} |
2498
|
|
|
// No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser |
2499
|
|
|
//if ($socid || ! $user->rights->societe->client->voir) $sql.= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")"; |
2500
|
|
|
if ($socid) { |
2501
|
|
|
$sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . ((int) $socid) . ")"; |
2502
|
|
|
} |
2503
|
|
|
if (!$user->hasRight('societe', 'client', 'voir')) { |
2504
|
|
|
$sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id) . ") OR (s.rowid IS NULL))"; |
2505
|
|
|
} |
2506
|
|
|
|
2507
|
|
|
$resql = $this->db->query($sql); |
2508
|
|
|
if ($resql) { |
2509
|
|
|
// This assignment in condition is not a bug. It allows walking the results. |
2510
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
2511
|
|
|
$this->nb["tasks"] = $obj->nb; |
2512
|
|
|
} |
2513
|
|
|
$this->db->free($resql); |
2514
|
|
|
return 1; |
2515
|
|
|
} else { |
2516
|
|
|
dol_print_error($this->db); |
2517
|
|
|
$this->error = $this->db->error(); |
2518
|
|
|
return -1; |
2519
|
|
|
} |
2520
|
|
|
} |
2521
|
|
|
|
2522
|
|
|
/** |
2523
|
|
|
* Is the task delayed? |
2524
|
|
|
* |
2525
|
|
|
* @return bool |
2526
|
|
|
*/ |
2527
|
|
|
public function hasDelay() |
2528
|
|
|
{ |
2529
|
|
|
global $conf; |
2530
|
|
|
|
2531
|
|
|
if (!($this->progress >= 0 && $this->progress < 100)) { |
2532
|
|
|
return false; |
2533
|
|
|
} |
2534
|
|
|
|
2535
|
|
|
$now = dol_now(); |
2536
|
|
|
|
2537
|
|
|
$datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0); |
|
|
|
|
2538
|
|
|
|
2539
|
|
|
return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay))); |
2540
|
|
|
} |
2541
|
|
|
|
2542
|
|
|
/** |
2543
|
|
|
* Return clicable link of object (with eventually picto) |
2544
|
|
|
* |
2545
|
|
|
* @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) |
2546
|
|
|
* @param array $arraydata Array of data |
2547
|
|
|
* @return string HTML Code for Kanban thumb. |
2548
|
|
|
*/ |
2549
|
|
|
public function getKanbanView($option = '', $arraydata = null) |
2550
|
|
|
{ |
2551
|
|
|
$selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); |
2552
|
|
|
|
2553
|
|
|
$return = '<div class="box-flex-item box-flex-grow-zero">'; |
2554
|
|
|
$return .= '<div class="info-box info-box-sm info-box-kanban">'; |
2555
|
|
|
$return .= '<span class="info-box-icon bg-infobox-action">'; |
2556
|
|
|
$return .= img_picto('', $this->picto); |
2557
|
|
|
//$return .= '<i class="fa fa-dol-action"></i>'; // Can be image |
2558
|
|
|
$return .= '</span>'; |
2559
|
|
|
$return .= '<div class="info-box-content">'; |
2560
|
|
|
$return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>'; |
2561
|
|
|
if ($selected >= 0) { |
2562
|
|
|
$return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>'; |
2563
|
|
|
} |
2564
|
|
|
if (!empty($arraydata['projectlink'])) { |
2565
|
|
|
//$tmpproject = $arraydata['project']; |
2566
|
|
|
//$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>'; |
2567
|
|
|
$return .= '<br><span class="info-box-status ">' . $arraydata['projectlink'] . '</span>'; |
2568
|
|
|
} |
2569
|
|
|
if (property_exists($this, 'budget_amount')) { |
2570
|
|
|
//$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>'; |
2571
|
|
|
} |
2572
|
|
|
if (property_exists($this, 'duration_effective')) { |
2573
|
|
|
$return .= '<br><div class="info-box-label progressinkanban paddingtop">' . getTaskProgressView($this, false, true) . '</div>'; |
2574
|
|
|
} |
2575
|
|
|
$return .= '</div>'; |
2576
|
|
|
$return .= '</div>'; |
2577
|
|
|
$return .= '</div>'; |
2578
|
|
|
|
2579
|
|
|
return $return; |
2580
|
|
|
} |
2581
|
|
|
|
2582
|
|
|
/** |
2583
|
|
|
* Merge a task with another one, deleting the given task. |
2584
|
|
|
* The task given in parameter will be removed. |
2585
|
|
|
* |
2586
|
|
|
* @param int $task_origin_id Task to merge the data from |
2587
|
|
|
* @return int -1 if error |
2588
|
|
|
*/ |
2589
|
|
|
public function mergeTask($task_origin_id) |
2590
|
|
|
{ |
2591
|
|
|
global $langs, $hookmanager, $user, $action; |
2592
|
|
|
|
2593
|
|
|
$error = 0; |
2594
|
|
|
$task_origin = new Task($this->db); // The thirdparty that we will delete |
2595
|
|
|
|
2596
|
|
|
dol_syslog("mergeTask merge task id=" . $task_origin_id . " (will be deleted) into the task id=" . $this->id); |
2597
|
|
|
|
2598
|
|
|
$langs->load('error'); |
2599
|
|
|
|
2600
|
|
|
if (!$error && $task_origin->fetch($task_origin_id) < 1) { |
2601
|
|
|
$this->error = $langs->trans('ErrorRecordNotFound'); |
2602
|
|
|
$error++; |
2603
|
|
|
} |
2604
|
|
|
|
2605
|
|
|
if (!$error) { |
2606
|
|
|
$this->db->begin(); |
2607
|
|
|
|
2608
|
|
|
// Recopy some data |
2609
|
|
|
$listofproperties = array( |
2610
|
|
|
'label', 'description', 'duration_effective', 'planned_workload', 'datec', 'date_start', |
2611
|
|
|
'date_end', 'fk_user_creat', 'fk_user_valid', 'fk_statut', 'progress', 'budget_amount', |
2612
|
|
|
'priority', 'rang', 'fk_projet', 'fk_task_parent' |
2613
|
|
|
); |
2614
|
|
|
foreach ($listofproperties as $property) { |
2615
|
|
|
if (empty($this->$property)) { |
2616
|
|
|
$this->$property = $task_origin->$property; |
2617
|
|
|
} |
2618
|
|
|
} |
2619
|
|
|
|
2620
|
|
|
// Concat some data |
2621
|
|
|
$listofproperties = array( |
2622
|
|
|
'note_public', 'note_private' |
2623
|
|
|
); |
2624
|
|
|
foreach ($listofproperties as $property) { |
2625
|
|
|
$this->$property = dol_concatdesc($this->$property, $task_origin->$property); |
2626
|
|
|
} |
2627
|
|
|
|
2628
|
|
|
// Merge extrafields |
2629
|
|
|
if (is_array($task_origin->array_options)) { |
2630
|
|
|
foreach ($task_origin->array_options as $key => $val) { |
2631
|
|
|
if (empty($this->array_options[$key])) { |
2632
|
|
|
$this->array_options[$key] = $val; |
2633
|
|
|
} |
2634
|
|
|
} |
2635
|
|
|
} |
2636
|
|
|
|
2637
|
|
|
// Update |
2638
|
|
|
$result = $this->update($user); |
2639
|
|
|
|
2640
|
|
|
if ($result < 0) { |
2641
|
|
|
$error++; |
2642
|
|
|
} |
2643
|
|
|
|
2644
|
|
|
// Merge time spent |
2645
|
|
|
if (!$error) { |
2646
|
|
|
$result = $this->mergeTimeSpentTask($task_origin_id, $this->id); |
2647
|
|
|
if ($result != true) { |
|
|
|
|
2648
|
|
|
$error++; |
2649
|
|
|
} |
2650
|
|
|
} |
2651
|
|
|
|
2652
|
|
|
// Merge contacts |
2653
|
|
|
if (!$error) { |
2654
|
|
|
$result = $this->mergeContactTask($task_origin_id, $this->id); |
2655
|
|
|
if ($result != true) { |
|
|
|
|
2656
|
|
|
$error++; |
2657
|
|
|
} |
2658
|
|
|
} |
2659
|
|
|
|
2660
|
|
|
// External modules should update their ones too |
2661
|
|
|
if (!$error) { |
2662
|
|
|
$parameters = array('task_origin' => $task_origin->id, 'task_dest' => $this->id); |
2663
|
|
|
$reshook = $hookmanager->executeHooks('replaceThirdparty', $parameters, $this, $action); |
2664
|
|
|
|
2665
|
|
|
if ($reshook < 0) { |
2666
|
|
|
$this->error = $hookmanager->error; |
2667
|
|
|
$this->errors = $hookmanager->errors; |
2668
|
|
|
$error++; |
2669
|
|
|
} |
2670
|
|
|
} |
2671
|
|
|
|
2672
|
|
|
|
2673
|
|
|
if (!$error) { |
2674
|
|
|
$this->context = array('merge' => 1, 'mergefromid' => $task_origin->id, 'mergefromref' => $task_origin->ref); |
2675
|
|
|
|
2676
|
|
|
// Call trigger |
2677
|
|
|
$result = $this->call_trigger('TASK_MODIFY', $user); |
2678
|
|
|
if ($result < 0) { |
2679
|
|
|
$error++; |
2680
|
|
|
} |
2681
|
|
|
// End call triggers |
2682
|
|
|
} |
2683
|
|
|
|
2684
|
|
|
if (!$error) { |
2685
|
|
|
// We finally remove the old task |
2686
|
|
|
if ($task_origin->delete($user) < 1) { |
2687
|
|
|
$this->error = $task_origin->error; |
2688
|
|
|
$this->errors = $task_origin->errors; |
2689
|
|
|
$error++; |
2690
|
|
|
} |
2691
|
|
|
} |
2692
|
|
|
|
2693
|
|
|
if (!$error) { |
2694
|
|
|
$this->db->commit(); |
2695
|
|
|
return 0; |
2696
|
|
|
} else { |
2697
|
|
|
$langs->load("errors"); |
2698
|
|
|
$this->error = $langs->trans('ErrorsTaskMerge'); |
2699
|
|
|
$this->db->rollback(); |
2700
|
|
|
return -1; |
2701
|
|
|
} |
2702
|
|
|
} |
2703
|
|
|
|
2704
|
|
|
return -1; |
2705
|
|
|
} |
2706
|
|
|
} |
2707
|
|
|
|
This property has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.