Passed
Push — EXTRACT_CLASSES ( ff35ec...a2ff75 )
by Rafael
48:13
created

Task::create()   F

Complexity

Conditions 25
Paths > 20000

Size

Total Lines 97
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 70
nc 393217
nop 2
dl 0
loc 97
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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\Core\Base\CommonObjectLine;
30
31
/**
32
 *      \file       htdocs/projet/class/task.class.php
33
 *      \ingroup    project
34
 *      \brief      This file is a CRUD class file for Task (Create/Read/Update/Delete)
35
 */
36
37
require_once constant('DOL_DOCUMENT_ROOT') . '/projet/class/project.class.php';
38
require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/timespent.class.php';
39
40
/**
41
 *  Class to manage tasks
42
 */
43
class Task extends CommonObjectLine
44
{
45
    /**
46
     * @var string ID to identify managed object
47
     */
48
    public $element = 'project_task';
49
50
    /**
51
     * @var string  Name of table without prefix where object is stored
52
     */
53
    public $table_element = 'projet_task';
54
55
    /**
56
     * @var string Field with ID of parent key if this field has a parent
57
     */
58
    public $fk_element = 'fk_element';
59
60
    /**
61
     * @var string String with name of icon for myobject.
62
     */
63
    public $picto = 'projecttask';
64
65
    /**
66
     * @var array<string, array<string>>    List of child tables. To test if we can delete object.
67
     */
68
    protected $childtables = array(
69
        'element_time' => array('name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task')
70
    );
71
72
    /**
73
     * @var int ID parent task
74
     */
75
    public $fk_task_parent = 0;
76
77
    /**
78
     * @var string Label of task
79
     */
80
    public $label;
81
82
    /**
83
     * @var string description
84
     */
85
    public $description;
86
87
    public $duration_effective; // total of time spent on this task
88
    public $planned_workload;
89
    public $date_c;
90
    public $progress;
91
92
    /**
93
     * @deprecated Use date_start instead
94
     */
95
    public $dateo;
96
97
    public $date_start;
98
99
    /**
100
     * @deprecated Use date_end instead
101
     */
102
    public $datee;
103
104
    public $date_end;
105
106
    /**
107
     * @var int ID
108
     * @deprecated use status instead
109
     */
110
    public $fk_statut;
111
112
    /**
113
     * @var int ID
114
     */
115
    public $status;
116
117
    public $priority;
118
119
    /**
120
     * @var int ID
121
     */
122
    public $fk_user_creat;
123
124
    /**
125
     * @var int ID
126
     */
127
    public $fk_user_valid;
128
129
    public $rang;
130
131
    public $timespent_min_date;
132
    public $timespent_max_date;
133
    public $timespent_total_duration;
134
    public $timespent_total_amount;
135
    public $timespent_nblinesnull;
136
    public $timespent_nblines;
137
    // For detail of lines of timespent record, there is the property ->lines in common
138
139
    // Var used to call method addTimeSpent(). Bad practice.
140
    public $timespent_id;
141
    public $timespent_duration;
142
    public $timespent_old_duration;
143
    public $timespent_date;
144
    public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
145
    public $timespent_withhour; // 1 = we entered also start hours for timesheet line
146
    public $timespent_fk_user;
147
    public $timespent_thm;
148
    public $timespent_note;
149
    public $timespent_fk_product;
150
    public $timespent_invoiceid;
151
    public $timespent_invoicelineid;
152
153
    public $comments = array();
154
155
    // Properties calculated from sum of llx_element_time linked to task
156
    public $tobill;
157
    public $billed;
158
159
    // Properties to store project information
160
    public $projectref;
161
    public $projectstatus;
162
    public $projectlabel;
163
    public $opp_amount;
164
    public $opp_percent;
165
    public $fk_opp_status;
166
    public $usage_bill_time;
167
    public $public;
168
    public $array_options_project;
169
170
    // Properties to store thirdparty of project information
171
    public $socid;
172
    public $thirdparty_id;
173
    public $thirdparty_name;
174
    public $thirdparty_email;
175
176
    // store parent ref and position
177
    public $task_parent_ref;
178
    public $task_parent_position;
179
180
181
182
    /**
183
     * @var float budget_amount
184
     */
185
    public $budget_amount;
186
187
    /**
188
     * @var float project_budget_amount
189
     */
190
    public $project_budget_amount;
191
192
    /**
193
     * Draft status
194
     */
195
    const STATUS_DRAFT = 0;
196
197
    /**
198
     * Validated status (To do). Note: We also have the field progress to know the progression from 0 to 100%.
199
     */
200
    const STATUS_VALIDATED = 1;
201
202
    /**
203
     * Finished status
204
     */
205
    const STATUS_CLOSED = 3;
206
207
    /**
208
     * Transferred status
209
     */
210
    const STATUS_TRANSFERRED = 4;
211
212
    /**
213
     * status canceled
214
     */
215
    const STATUS_CANCELED = 9;
216
217
218
    /**
219
     *  Constructor
220
     *
221
     *  @param      DoliDB      $db      Database handler
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Projet\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
222
     */
223
    public function __construct($db)
224
    {
225
        $this->db = $db;
226
    }
227
228
229
    /**
230
     *  Create into database
231
     *
232
     *  @param  User    $user           User that create
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Projet\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
233
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
234
     *  @return int                     Return integer <0 if KO, Id of created object if OK
235
     */
236
    public function create($user, $notrigger = 0)
237
    {
238
        global $conf, $langs;
239
240
        //For the date
241
        $now = dol_now();
242
243
        $error = 0;
244
245
        // Clean parameters
246
        $this->label = trim($this->label);
247
        $this->description = trim($this->description);
248
        $this->note_public = trim($this->note_public);
249
        $this->note_private = trim($this->note_private);
250
251
        if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
252
            $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
253
            return -1;
254
        }
255
256
        // Insert request
257
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet_task (";
258
        $sql .= "entity";
259
        $sql .= ", fk_projet";
260
        $sql .= ", ref";
261
        $sql .= ", fk_task_parent";
262
        $sql .= ", label";
263
        $sql .= ", description";
264
        $sql .= ", note_public";
265
        $sql .= ", note_private";
266
        $sql .= ", datec";
267
        $sql .= ", fk_user_creat";
268
        $sql .= ", dateo";
269
        $sql .= ", datee";
270
        $sql .= ", planned_workload";
271
        $sql .= ", progress";
272
        $sql .= ", budget_amount";
273
        $sql .= ", priority";
274
        $sql .= ") VALUES (";
275
        $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
276
        $sql .= ", " . ((int) $this->fk_project);
277
        $sql .= ", " . (!empty($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : 'null');
278
        $sql .= ", " . ((int) $this->fk_task_parent);
279
        $sql .= ", '" . $this->db->escape($this->label) . "'";
280
        $sql .= ", '" . $this->db->escape($this->description) . "'";
281
        $sql .= ", '" . $this->db->escape($this->note_public) . "'";
282
        $sql .= ", '" . $this->db->escape($this->note_private) . "'";
283
        $sql .= ", '" . $this->db->idate($now) . "'";
284
        $sql .= ", " . ((int) $user->id);
285
        $sql .= ", " . ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : 'null');
286
        $sql .= ", " . ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : 'null');
287
        $sql .= ", " . (($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
288
        $sql .= ", " . (($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
289
        $sql .= ", " . (($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
290
        $sql .= ", " . (($this->priority != '' && $this->priority >= 0) ? (int) $this->priority : 'null');
291
        $sql .= ")";
292
293
        $this->db->begin();
294
295
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
296
        $resql = $this->db->query($sql);
297
        if (!$resql) {
298
            $error++;
299
            $this->errors[] = "Error " . $this->db->lasterror();
300
        }
301
302
        if (!$error) {
303
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "projet_task");
304
            // Update extrafield
305
            $result = $this->insertExtraFields();
306
            if ($result < 0) {
307
                $error++;
308
            }
309
        }
310
311
        if (!$error) {
312
            if (!$notrigger) {
313
                // Call trigger
314
                $result = $this->call_trigger('TASK_CREATE', $user);
315
                if ($result < 0) {
316
                    $error++;
317
                }
318
                // End call triggers
319
            }
320
        }
321
322
        // Commit or rollback
323
        if ($error) {
324
            foreach ($this->errors as $errmsg) {
325
                dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR);
326
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
327
            }
328
            $this->db->rollback();
329
            return -1 * $error;
330
        } else {
331
            $this->db->commit();
332
            return $this->id;
333
        }
334
    }
335
336
337
    /**
338
     *  Load object in memory from database
339
     *
340
     *  @param  int     $id                 Id object
341
     *  @param  string  $ref                ref object
342
     *  @param  int     $loadparentdata     Also load parent data
343
     *  @return int                         Return integer <0 if KO, 0 if not found, >0 if OK
344
     */
345
    public function fetch($id, $ref = '', $loadparentdata = 0)
346
    {
347
        $sql = "SELECT";
348
        $sql .= " t.rowid,";
349
        $sql .= " t.ref,";
350
        $sql .= " t.entity,";
351
        $sql .= " t.fk_projet as fk_project,";
352
        $sql .= " t.fk_task_parent,";
353
        $sql .= " t.label,";
354
        $sql .= " t.description,";
355
        $sql .= " t.duration_effective,";
356
        $sql .= " t.planned_workload,";
357
        $sql .= " t.datec,";
358
        $sql .= " t.dateo as date_start,";
359
        $sql .= " t.datee as date_end,";
360
        $sql .= " t.fk_user_creat,";
361
        $sql .= " t.fk_user_valid,";
362
        $sql .= " t.fk_statut as status,";
363
        $sql .= " t.progress,";
364
        $sql .= " t.budget_amount,";
365
        $sql .= " t.priority,";
366
        $sql .= " t.note_private,";
367
        $sql .= " t.note_public,";
368
        $sql .= " t.rang";
369
        if (!empty($loadparentdata)) {
370
            $sql .= ", t2.ref as task_parent_ref";
371
            $sql .= ", t2.rang as task_parent_position";
372
        }
373
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet_task as t";
374
        if (!empty($loadparentdata)) {
375
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task as t2 ON t.fk_task_parent = t2.rowid";
376
        }
377
        $sql .= " WHERE ";
378
        if (!empty($ref)) {
379
            $sql .= "entity IN (" . getEntity('project') . ")";
380
            $sql .= " AND t.ref = '" . $this->db->escape($ref) . "'";
381
        } else {
382
            $sql .= "t.rowid = " . ((int) $id);
383
        }
384
385
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
386
        $resql = $this->db->query($sql);
387
        if ($resql) {
388
            $num_rows = $this->db->num_rows($resql);
389
390
            if ($num_rows) {
391
                $obj = $this->db->fetch_object($resql);
392
393
                $this->id = $obj->rowid;
394
                $this->ref = $obj->ref;
395
                $this->entity = $obj->entity;
396
                $this->fk_project = $obj->fk_project;
397
                $this->fk_task_parent = $obj->fk_task_parent;
398
                $this->label = $obj->label;
399
                $this->description = $obj->description;
400
                $this->duration_effective = $obj->duration_effective;
401
                $this->planned_workload = $obj->planned_workload;
402
                $this->date_c = $this->db->jdate($obj->datec);
403
                $this->date_start = $this->db->jdate($obj->date_start);
404
                $this->date_end             = $this->db->jdate($obj->date_end);
405
                $this->fk_user_creat        = $obj->fk_user_creat;
406
                $this->fk_user_valid        = $obj->fk_user_valid;
407
                $this->fk_statut            = $obj->status;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Projet\Classes\Task::$fk_statut has been deprecated: use status instead ( Ignorable by Annotation )

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

407
                /** @scrutinizer ignore-deprecated */ $this->fk_statut            = $obj->status;

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.

Loading history...
408
                $this->status               = $obj->status;
409
                $this->progress             = $obj->progress;
410
                $this->budget_amount        = $obj->budget_amount;
411
                $this->priority             = $obj->priority;
412
                $this->note_private = $obj->note_private;
413
                $this->note_public = $obj->note_public;
414
                $this->rang = $obj->rang;
415
416
                if (!empty($loadparentdata)) {
417
                    $this->task_parent_ref      = $obj->task_parent_ref;
418
                    $this->task_parent_position = $obj->task_parent_position;
419
                }
420
421
                // Retrieve all extrafield
422
                $this->fetch_optionals();
423
            }
424
425
            $this->db->free($resql);
426
427
            if ($num_rows) {
428
                return 1;
429
            } else {
430
                return 0;
431
            }
432
        } else {
433
            $this->error = "Error " . $this->db->lasterror();
434
            return -1;
435
        }
436
    }
437
438
439
    /**
440
     *  Update database
441
     *
442
     *  @param  User    $user           User that modify
443
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
444
     *  @return int                     Return integer <=0 if KO, >0 if OK
445
     */
446
    public function update($user = null, $notrigger = 0)
447
    {
448
        global $conf, $langs;
449
        $error = 0;
450
451
        // Clean parameters
452
        if (isset($this->fk_project)) {
453
            $this->fk_project = (int) $this->fk_project;
454
        }
455
        if (isset($this->ref)) {
456
            $this->ref = trim($this->ref);
457
        }
458
        if (isset($this->fk_task_parent)) {
459
            $this->fk_task_parent = (int) $this->fk_task_parent;
460
        }
461
        if (isset($this->label)) {
462
            $this->label = trim($this->label);
463
        }
464
        if (isset($this->description)) {
465
            $this->description = trim($this->description);
466
        }
467
        if (isset($this->note_public)) {
468
            $this->note_public = trim($this->note_public);
469
        }
470
        if (isset($this->note_private)) {
471
            $this->note_private = trim($this->note_private);
472
        }
473
        if (isset($this->duration_effective)) {
474
            $this->duration_effective = trim($this->duration_effective);
475
        }
476
        if (isset($this->planned_workload)) {
477
            $this->planned_workload = trim($this->planned_workload);
478
        }
479
        if (isset($this->budget_amount)) {
480
            $this->budget_amount = (float) $this->budget_amount;
481
        }
482
483
        if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
484
            $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
485
            return -1;
486
        }
487
488
        // Check parameters
489
        // Put here code to add control on parameters values
490
491
        // Update request
492
        $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task SET";
493
        $sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ",";
494
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "'" . $this->db->escape($this->id) . "'") . ",";
495
        $sql .= " fk_task_parent=" . (isset($this->fk_task_parent) ? $this->fk_task_parent : "null") . ",";
496
        $sql .= " label=" . (isset($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
497
        $sql .= " description=" . (isset($this->description) ? "'" . $this->db->escape($this->description) . "'" : "null") . ",";
498
        $sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
499
        $sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
500
        $sql .= " duration_effective=" . (isset($this->duration_effective) ? $this->duration_effective : "null") . ",";
501
        $sql .= " planned_workload=" . ((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null") . ",";
502
        $sql .= " dateo=" . ($this->date_start != '' ? "'" . $this->db->idate($this->date_start) . "'" : 'null') . ",";
503
        $sql .= " datee=" . ($this->date_end != '' ? "'" . $this->db->idate($this->date_end) . "'" : 'null') . ",";
504
        $sql .= " progress=" . (($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null') . ",";
505
        $sql .= " budget_amount=" . (($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null') . ",";
506
        $sql .= " rang=" . ((!empty($this->rang)) ? ((int) $this->rang) : "0") . ",";
507
        $sql .= " priority=" . ((!empty($this->priority)) ? ((int) $this->priority) : "0");
508
        $sql .= " WHERE rowid=" . ((int) $this->id);
509
510
        $this->db->begin();
511
512
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
513
        $resql = $this->db->query($sql);
514
        if (!$resql) {
515
            $error++;
516
            $this->errors[] = "Error " . $this->db->lasterror();
517
        }
518
519
        // Update extrafield
520
        if (!$error) {
521
            $result = $this->insertExtraFields();
522
            if ($result < 0) {
523
                $error++;
524
            }
525
        }
526
527
        if (!$error && getDolGlobalString('PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE')) {
528
            // Close the parent project if it is open (validated) and its tasks are 100% completed
529
            $project = new Project($this->db);
530
            if ($project->fetch($this->fk_project) > 0) {
531
                if ($project->statut == Project::STATUS_VALIDATED) {
532
                    $project->getLinesArray(null); // this method does not return <= 0 if fails
533
                    $projectCompleted = array_reduce(
534
                        $project->lines,
535
                        /**
536
                         * @param bool $allTasksCompleted
537
                         * @param Task $task
538
                         * @return bool
539
                         */
540
                        static function ($allTasksCompleted, $task) {
541
                            return $allTasksCompleted && $task->progress >= 100;
542
                        },
543
                        1
544
                    );
545
                    if ($projectCompleted) {
546
                        if ($project->setClose($user) <= 0) {
547
                            $error++;
548
                        }
549
                    }
550
                }
551
            } else {
552
                $error++;
553
            }
554
            if ($error) {
555
                $this->errors[] = $project->error;
556
            }
557
        }
558
559
        if (!$error) {
560
            if (!$notrigger) {
561
                // Call trigger
562
                $result = $this->call_trigger('TASK_MODIFY', $user);
563
                if ($result < 0) {
564
                    $error++;
565
                }
566
                // End call triggers
567
            }
568
        }
569
570
        if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
571
            // We remove directory
572
            if ($conf->project->dir_output) {
573
                $project = new Project($this->db);
574
                $project->fetch($this->fk_project);
575
576
                $olddir = $conf->project->dir_output . '/' . dol_sanitizeFileName($project->ref) . '/' . dol_sanitizeFileName($this->oldcopy->ref);
577
                $newdir = $conf->project->dir_output . '/' . dol_sanitizeFileName($project->ref) . '/' . dol_sanitizeFileName($this->ref);
578
                if (file_exists($olddir)) {
579
                    include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
580
                    $res = dol_move_dir($olddir, $newdir);
581
                    if (!$res) {
582
                        $langs->load("errors");
583
                        $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
584
                        $error++;
585
                    }
586
                }
587
            }
588
        }
589
590
        // Commit or rollback
591
        if ($error) {
592
            foreach ($this->errors as $errmsg) {
593
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
594
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
595
            }
596
            $this->db->rollback();
597
            return -1 * $error;
598
        } else {
599
            $this->db->commit();
600
            return 1;
601
        }
602
    }
603
604
605
    /**
606
     *  Delete task from database
607
     *
608
     *  @param  User    $user           User that delete
609
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
610
     *  @return int                     Return integer <0 if KO, >0 if OK
611
     */
612
    public function delete($user, $notrigger = 0)
613
    {
614
        global $conf;
615
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
616
617
        $error = 0;
618
619
        $this->db->begin();
620
621
        if ($this->hasChildren() > 0) {
622
            dol_syslog(get_class($this) . "::delete Can't delete record as it has some sub tasks", LOG_WARNING);
623
            $this->error = 'ErrorRecordHasSubTasks';
624
            $this->db->rollback();
625
            return 0;
626
        }
627
628
        $objectisused = $this->isObjectUsed($this->id);
629
        if (!empty($objectisused)) {
630
            dol_syslog(get_class($this) . "::delete Can't delete record as it has some child", LOG_WARNING);
631
            $this->error = 'ErrorRecordHasChildren';
632
            $this->db->rollback();
633
            return 0;
634
        }
635
636
        if (!$error) {
637
            // Delete linked contacts
638
            $res = $this->delete_linked_contact();
639
            if ($res < 0) {
640
                $this->error = 'ErrorFailToDeleteLinkedContact';
641
                //$error++;
642
                $this->db->rollback();
643
                return 0;
644
            }
645
        }
646
647
        if (!$error) {
648
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "element_time";
649
            $sql .= " WHERE fk_element = " . ((int) $this->id) . " AND elementtype = 'task'";
650
651
            $resql = $this->db->query($sql);
652
            if (!$resql) {
653
                $error++;
654
                $this->errors[] = "Error " . $this->db->lasterror();
655
            }
656
        }
657
658
        if (!$error) {
659
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task_extrafields";
660
            $sql .= " WHERE fk_object = " . ((int) $this->id);
661
662
            $resql = $this->db->query($sql);
663
            if (!$resql) {
664
                $error++;
665
                $this->errors[] = "Error " . $this->db->lasterror();
666
            }
667
        }
668
669
        if (!$error) {
670
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task";
671
            $sql .= " WHERE rowid=" . ((int) $this->id);
672
673
            $resql = $this->db->query($sql);
674
            if (!$resql) {
675
                $error++;
676
                $this->errors[] = "Error " . $this->db->lasterror();
677
            }
678
        }
679
680
        if (!$error) {
681
            if (!$notrigger) {
682
                // Call trigger
683
                $result = $this->call_trigger('TASK_DELETE', $user);
684
                if ($result < 0) {
685
                    $error++;
686
                }
687
                // End call triggers
688
            }
689
        }
690
691
        // Commit or rollback
692
        if ($error) {
693
            foreach ($this->errors as $errmsg) {
694
                dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
695
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
696
            }
697
            $this->db->rollback();
698
            return -1 * $error;
699
        } else {
700
            //Delete associated link file
701
            if ($conf->project->dir_output) {
702
                $projectstatic = new Project($this->db);
703
                $projectstatic->fetch($this->fk_project);
704
705
                $dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($projectstatic->ref) . '/' . dol_sanitizeFileName($this->id);
706
                dol_syslog(get_class($this) . "::delete dir=" . $dir, LOG_DEBUG);
707
                if (file_exists($dir)) {
708
                    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
709
                    $res = @dol_delete_dir_recursive($dir);
710
                    if (!$res) {
711
                        $this->error = 'ErrorFailToDeleteDir';
712
                        $this->db->rollback();
713
                        return 0;
714
                    }
715
                }
716
            }
717
718
            $this->db->commit();
719
720
            return 1;
721
        }
722
    }
723
724
    /**
725
     *  Return nb of children
726
     *
727
     *  @return int     Return integer <0 if KO, 0 if no children, >0 if OK
728
     */
729
    public function hasChildren()
730
    {
731
        $error = 0;
732
        $ret = 0;
733
734
        $sql = "SELECT COUNT(*) as nb";
735
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet_task";
736
        $sql .= " WHERE fk_task_parent = " . ((int) $this->id);
737
738
        dol_syslog(get_class($this) . "::hasChildren", LOG_DEBUG);
739
        $resql = $this->db->query($sql);
740
        if (!$resql) {
741
            $error++;
742
            $this->errors[] = "Error " . $this->db->lasterror();
743
        } else {
744
            $obj = $this->db->fetch_object($resql);
745
            if ($obj) {
746
                $ret = $obj->nb;
747
            }
748
            $this->db->free($resql);
749
        }
750
751
        if (!$error) {
752
            return $ret;
753
        } else {
754
            return -1;
755
        }
756
    }
757
758
    /**
759
     *  Return nb of time spent
760
     *
761
     *  @return int     Return integer <0 if KO, 0 if no children, >0 if OK
762
     */
763
    public function hasTimeSpent()
764
    {
765
        $error = 0;
766
        $ret = 0;
767
768
        $sql = "SELECT COUNT(*) as nb";
769
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time";
770
        $sql .= " WHERE fk_element = " . ((int) $this->id);
771
        $sql .= " AND elementtype = 'task'";
772
773
        dol_syslog(get_class($this) . "::hasTimeSpent", LOG_DEBUG);
774
        $resql = $this->db->query($sql);
775
        if (!$resql) {
776
            $error++;
777
            $this->errors[] = "Error " . $this->db->lasterror();
778
        } else {
779
            $obj = $this->db->fetch_object($resql);
780
            if ($obj) {
781
                $ret = $obj->nb;
782
            }
783
            $this->db->free($resql);
784
        }
785
786
        if (!$error) {
787
            return $ret;
788
        } else {
789
            return -1;
790
        }
791
    }
792
793
794
    /**
795
     * getTooltipContentArray
796
     *
797
     * @param array $params ex option, infologin
798
     * @since v18
799
     * @return array
800
     */
801
    public function getTooltipContentArray($params)
802
    {
803
        global $langs;
804
805
        $langs->load('projects');
806
807
        $datas = [];
808
        $datas['picto'] = img_picto('', $this->picto) . ' <u>' . $langs->trans("Task") . '</u>';
809
        if (!empty($this->ref)) {
810
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
811
        }
812
        if (!empty($this->label)) {
813
            $datas['label'] = '<br><b>' . $langs->trans('LabelTask') . ':</b> ' . $this->label;
814
        }
815
        if ($this->date_start || $this->date_end) {
816
            $datas['range'] = "<br>" . get_date_range($this->date_start, $this->date_end, '', $langs, 0);
817
        }
818
819
        return $datas;
820
    }
821
822
    /**
823
     *  Return clicable name (with picto eventually)
824
     *
825
     *  @param  int     $withpicto      0=No picto, 1=Include picto into link, 2=Only picto
826
     *  @param  string  $option         'withproject' or ''
827
     *  @param  string  $mode           Mode 'task', 'time', 'contact', 'note', document' define page to link to.
828
     *  @param  int     $addlabel       0=Default, 1=Add label into string, >1=Add first chars into string
829
     *  @param  string  $sep            Separator between ref and label if option addlabel is set
830
     *  @param  int     $notooltip      1=Disable tooltip
831
     *  @param  int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
832
     *  @return string                  Chaine avec URL
833
     */
834
    public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
835
    {
836
        global $action, $conf, $hookmanager, $langs;
837
838
        if (!empty($conf->dol_no_mouse_hover)) {
839
            $notooltip = 1; // Force disable tooltips
840
        }
841
842
        $result = '';
843
        $params = [
844
            'id' => $this->id,
845
            'objecttype' => $this->element,
846
        ];
847
        $classfortooltip = 'classfortooltip';
848
        $dataparams = '';
849
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
850
            $classfortooltip = 'classforajaxtooltip';
851
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
852
            $label = '';
853
        } else {
854
            $label = implode($this->getTooltipContentArray($params));
855
        }
856
857
        $url = constant('BASE_URL') . '/projet/tasks/' . $mode . '.php?id=' . $this->id . ($option == 'withproject' ? '&withproject=1' : '');
858
        // Add param to save lastsearch_values or not
859
        $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
860
        if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
861
            $add_save_lastsearch_values = 1;
862
        }
863
        if ($add_save_lastsearch_values) {
864
            $url .= '&save_lastsearch_values=1';
865
        }
866
867
        $linkclose = '';
868
        if (empty($notooltip)) {
869
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
870
                $label = $langs->trans("ShowTask");
871
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
872
            }
873
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
874
            $linkclose .= $dataparams . ' class="' . $classfortooltip . ' nowraponall"';
875
        } else {
876
            $linkclose .= ' class="nowraponall"';
877
        }
878
879
        $linkstart = '<a href="' . $url . '"';
880
        $linkstart .= $linkclose . '>';
881
        $linkend = '</a>';
882
883
        $picto = 'projecttask';
884
885
        $result .= $linkstart;
886
        if ($withpicto) {
887
            $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
888
        }
889
        if ($withpicto != 2) {
890
            $result .= $this->ref;
891
        }
892
        $result .= $linkend;
893
        if ($withpicto != 2) {
894
            $result .= (($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
895
        }
896
897
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
898
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
899
        if ($reshook > 0) {
900
            $result = $hookmanager->resPrint;
901
        } else {
902
            $result .= $hookmanager->resPrint;
903
        }
904
905
        return $result;
906
    }
907
908
    /**
909
     *  Initialise an instance with random values.
910
     *  Used to build previews or test instances.
911
     *  id must be 0 if object instance is a specimen.
912
     *
913
     *  @return int
914
     */
915
    public function initAsSpecimen()
916
    {
917
        global $user;
918
919
        $this->id = 0;
920
921
        $this->fk_project = 0;
922
        $this->ref = 'TK01';
923
        $this->fk_task_parent = 0;
924
        $this->label = 'Specimen task TK01';
925
        $this->duration_effective = '';
926
        $this->fk_user_creat = $user->id;
927
        $this->progress = '25';
928
        $this->status = 0;
929
        $this->priority = 0;
930
        $this->note_private = 'This is a specimen private note';
931
        $this->note_public = 'This is a specimen public note';
932
933
        return 1;
934
    }
935
936
    /**
937
     * Return list of tasks for all projects or for one particular project
938
     * Sort order is on project, then on position of task, and last on start date of first level task
939
     *
940
     * @param   User    $usert                  Object user to limit tasks affected to a particular user
941
     * @param   User    $userp                  Object user to limit projects of a particular user and public projects
942
     * @param   int     $projectid              Project id
943
     * @param   int     $socid                  Third party id
944
     * @param   int     $mode                   0=Return list of tasks and their projects, 1=Return projects and tasks if exists
945
     * @param   string  $filteronproj           Filter on project ref or label
946
     * @param   string  $filteronprojstatus     Filter on project status ('-1'=no filter, '0,1'=Draft+Validated only)
947
     * @param   string  $morewherefilter        Add more filter into where SQL request (must start with ' AND ...')
948
     * @param   int     $filteronprojuser       Filter on user that is a contact of project
949
     * @param   int     $filterontaskuser       Filter on user assigned to task
950
     * @param   ?Extrafields    $extrafields    Show additional column from project or task
951
     * @param   int     $includebilltime        Calculate also the time to bill and billed
952
     * @param   array   $search_array_options   Array of search filters. Not Used yet.
953
     * @param   int     $loadextras             Fetch all Extrafields on each project and task
954
     * @param   int     $loadRoleMode           1= will test Roles on task;  0 used in delete project action
955
     * @param   string  $sortfield              Sort field
956
     * @param   string  $sortorder              Sort order
957
     * @return  array|string                    Array of tasks
958
     */
959
    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 = '')
960
    {
961
        global $hookmanager;
962
963
        $tasks = array();
964
965
        //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
966
967
        // List of tasks (does not care about permissions. Filtering will be done later)
968
        $sql = "SELECT ";
969
        if ($filteronprojuser > 0 || $filterontaskuser > 0) {
970
            $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
971
        }
972
        $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
973
        $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,";
974
        $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang, t.priority,";
975
        $sql .= " t.budget_amount,";
976
        $sql .= " t.note_public, t.note_private,";
977
        $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
978
        $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
979
        if ($loadextras) {  // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
980
            if (!empty($extrafields->attributes['projet']['label'])) {
981
                foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
982
                    $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp." . $key . " as options_" . $key : '');
983
                }
984
            }
985
            if (!empty($extrafields->attributes['projet_task']['label'])) {
986
                foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
987
                    $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt." . $key . " as options_" . $key : '');
988
                }
989
            }
990
        }
991
        if ($includebilltime) {
992
            $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";
993
        }
994
995
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
996
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
997
        if ($loadextras) {
998
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_extrafields as efp ON (p.rowid = efp.fk_object)";
999
        }
1000
1001
        if ($mode == 0) {
1002
            if ($filteronprojuser > 0) {
1003
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec";
1004
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc";
1005
            }
1006
            $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
1007
            if ($loadextras) {
1008
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
1009
            }
1010
            if ($includebilltime) {
1011
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1012
            }
1013
            if ($filterontaskuser > 0) {
1014
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec2";
1015
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc2";
1016
            }
1017
            $sql .= " WHERE p.entity IN (" . getEntity('project') . ")";
1018
            $sql .= " AND t.fk_projet = p.rowid";
1019
        } elseif ($mode == 1) {
1020
            if ($filteronprojuser > 0) {
1021
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec";
1022
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc";
1023
            }
1024
            if ($filterontaskuser > 0) {
1025
                $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
1026
                if ($includebilltime) {
1027
                    $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
1028
                }
1029
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec2";
1030
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc2";
1031
            } else {
1032
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task as t on t.fk_projet = p.rowid";
1033
                if ($includebilltime) {
1034
                    $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
1035
                }
1036
            }
1037
            $sql .= " WHERE p.entity IN (" . getEntity('project') . ")";
1038
        } else {
1039
            return 'BadValueForParameterMode';
1040
        }
1041
1042
        if ($filteronprojuser > 0) {
1043
            $sql .= " AND p.rowid = ec.element_id";
1044
            $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1045
            $sql .= " AND ctc.element = 'project'";
1046
            $sql .= " AND ec.fk_socpeople = " . ((int) $filteronprojuser);
1047
            $sql .= " AND ec.statut = 4";
1048
            $sql .= " AND ctc.source = 'internal'";
1049
        }
1050
        if ($filterontaskuser > 0) {
1051
            $sql .= " AND t.fk_projet = p.rowid";
1052
            $sql .= " AND p.rowid = ec2.element_id";
1053
            $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
1054
            $sql .= " AND ctc2.element = 'project_task'";
1055
            $sql .= " AND ec2.fk_socpeople = " . ((int) $filterontaskuser);
1056
            $sql .= " AND ec2.statut = 4";
1057
            $sql .= " AND ctc2.source = 'internal'";
1058
        }
1059
        if ($socid) {
1060
            $sql .= " AND p.fk_soc = " . ((int) $socid);
1061
        }
1062
        if ($projectid) {
1063
            $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectid) . ")";
1064
        }
1065
        if ($filteronproj) {
1066
            $sql .= natural_search(array("p.ref", "p.title"), $filteronproj);
1067
        }
1068
        if ($filteronprojstatus && (int) $filteronprojstatus != '-1') {
1069
            $sql .= " AND p.fk_statut IN (" . $this->db->sanitize($filteronprojstatus) . ")";
1070
        }
1071
        if ($morewherefilter) {
1072
            $sql .= $morewherefilter;
1073
        }
1074
1075
        // Add where from extra fields
1076
        $extrafieldsobjectkey = 'projet_task';
1077
        $extrafieldsobjectprefix = 'efpt.';
1078
        global $db, $conf; // needed for extrafields_list_search_sql.tpl
1079
        include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_sql.tpl.php';
1080
1081
        // Add where from hooks
1082
        $parameters = array();
1083
        $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
1084
        $sql .= $hookmanager->resPrint;
1085
        if ($includebilltime) {
1086
            $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
1087
            $sql .= " t.datec, t.dateo, t.datee, t.tms,";
1088
            $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
1089
            $sql .= " t.dateo, t.datee, t.planned_workload, t.rang, t.priority,";
1090
            $sql .= " t.budget_amount,";
1091
            $sql .= " t.note_public, t.note_private,";
1092
            $sql .= " s.rowid, s.nom, s.email,";
1093
            $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
1094
            if ($loadextras) {
1095
                if (!empty($extrafields->attributes['projet']['label'])) {
1096
                    foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1097
                        $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp." . $key : '');
1098
                    }
1099
                }
1100
                if (!empty($extrafields->attributes['projet_task']['label'])) {
1101
                    foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
1102
                        $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt." . $key : '');
1103
                    }
1104
                }
1105
            }
1106
        }
1107
1108
        if ($sortfield && $sortorder) {
1109
            $sql .= $this->db->order($sortfield, $sortorder);
1110
        } else {
1111
            $sql .= " ORDER BY p.ref, t.rang, t.dateo";
1112
        }
1113
1114
        //print $sql;exit;
1115
        dol_syslog(get_class($this) . "::getTasksArray", LOG_DEBUG);
1116
        $resql = $this->db->query($sql);
1117
        if ($resql) {
1118
            $num = $this->db->num_rows($resql);
1119
            $i = 0;
1120
            // Loop on each record found, so each couple (project id, task id)
1121
            while ($i < $num) {
1122
                $error = 0;
1123
1124
                $obj = $this->db->fetch_object($resql);
1125
1126
                if ($loadRoleMode) {
1127
                    if ((!$obj->public) && (is_object($userp))) {    // If not public project and we ask a filter on project owned by a user
1128
                        if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
1129
                            $error++;
1130
                        }
1131
                    }
1132
                    if (is_object($usert)) {                            // If we ask a filter on a user affected to a task
1133
                        if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
1134
                            $error++;
1135
                        }
1136
                    }
1137
                }
1138
1139
                if (!$error) {
1140
                    $tasks[$i] = new Task($this->db);
1141
                    $tasks[$i]->id = $obj->taskid;
1142
                    $tasks[$i]->ref = $obj->taskref;
1143
                    $tasks[$i]->fk_project = $obj->projectid;
1144
1145
                    // Data from project
1146
                    $tasks[$i]->projectref = $obj->ref;
1147
                    $tasks[$i]->projectlabel = $obj->plabel;
1148
                    $tasks[$i]->projectstatus = $obj->projectstatus;
1149
                    $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
1150
                    $tasks[$i]->opp_amount = $obj->opp_amount;
1151
                    $tasks[$i]->opp_percent = $obj->opp_percent;
1152
                    $tasks[$i]->budget_amount = $obj->budget_amount;
1153
                    $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
1154
                    $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
1155
1156
                    $tasks[$i]->label = $obj->label;
1157
                    $tasks[$i]->description = $obj->description;
1158
1159
                    $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
1160
                    $tasks[$i]->note_public = $obj->note_public;
1161
                    $tasks[$i]->note_private = $obj->note_private;
1162
                    $tasks[$i]->duration_effective = $obj->duration_effective;
1163
                    $tasks[$i]->planned_workload = $obj->planned_workload;
1164
1165
                    if ($includebilltime) {
1166
                        // Data summed from element_time linked to task
1167
                        $tasks[$i]->tobill = $obj->tobill;
1168
                        $tasks[$i]->billed = $obj->billed;
1169
                    }
1170
1171
                    $tasks[$i]->progress        = $obj->progress;
1172
                    $tasks[$i]->fk_statut       = $obj->status;
1173
                    $tasks[$i]->status          = $obj->status;
1174
                    $tasks[$i]->public = $obj->public;
1175
                    $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
1176
                    $tasks[$i]->date_end        = $this->db->jdate($obj->date_end);
1177
                    $tasks[$i]->rang            = $obj->rang;
1178
                    $tasks[$i]->priority        = $obj->priority;
1179
1180
                    $tasks[$i]->socid           = $obj->thirdparty_id; // For backward compatibility
1181
                    $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
1182
                    $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
1183
                    $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
1184
1185
                    if ($loadextras) {
1186
                        if (!empty($extrafields->attributes['projet']['label'])) {
1187
                            foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
1188
                                if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
1189
                                    $tmpvar = 'options_' . $key;
1190
                                    $tasks[$i]->array_options_project['options_' . $key] = $obj->$tmpvar;
1191
                                }
1192
                            }
1193
                        }
1194
                    }
1195
1196
                    if ($loadextras) {
1197
                        $tasks[$i]->fetch_optionals();
1198
                    }
1199
                }
1200
1201
                $i++;
1202
            }
1203
            $this->db->free($resql);
1204
        } else {
1205
            dol_print_error($this->db);
1206
        }
1207
1208
        return $tasks;
1209
    }
1210
1211
    /**
1212
     * Return list of roles for a user for each projects or each tasks (or a particular project or a particular task).
1213
     *
1214
     * @param   User|null   $userp                Return roles on project for this internal user. If set, usert and taskid must not be defined.
1215
     * @param   User|null   $usert                Return roles on task for this internal user. If set userp must NOT be defined. -1 means no filter.
1216
     * @param   string      $projectid            Project id list separated with , to filter on project
1217
     * @param   int         $taskid               Task id to filter on a task
1218
     * @param   integer     $filteronprojstatus   Filter on project status if userp is set. Not used if userp not defined.
1219
     * @return  array|int                         Array (projectid => 'list of roles for project' or taskid => 'list of roles for task')
1220
     */
1221
    public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
1222
    {
1223
        $arrayroles = array();
1224
1225
        dol_syslog(get_class($this) . "::getUserRolesForProjectsOrTasks userp=" . json_encode(is_object($userp)) . " usert=" . json_encode(is_object($usert)) . " projectid=" . $projectid . " taskid=" . $taskid);
1226
1227
        // We want role of user for a projet or role of user for a task. Both are not possible.
1228
        if (empty($userp) && empty($usert)) {
1229
            $this->error = "CallWithWrongParameters";
1230
            return -1;
1231
        }
1232
        if (!empty($userp) && !empty($usert)) {
1233
            $this->error = "CallWithWrongParameters";
1234
            return -1;
1235
        }
1236
1237
        /* Liste des taches et role sur les projects ou taches */
1238
        $sql = "SELECT ";
1239
        if ($userp) {
1240
            $sql .= " p.rowid as pid,";
1241
        } else {
1242
            $sql .= " pt.rowid as pid,";
1243
        }
1244
        $sql .= " ec.element_id, ctc.code, ctc.source";
1245
        if ($userp) {
1246
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
1247
        }
1248
        if ($usert && $filteronprojstatus > -1) {
1249
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p, " . MAIN_DB_PREFIX . "projet_task as pt";
1250
        }
1251
        if ($usert && $filteronprojstatus <= -1) {
1252
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet_task as pt";
1253
        }
1254
        $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec";
1255
        $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc";
1256
        if ($userp) {
1257
            $sql .= " WHERE p.rowid = ec.element_id";
1258
        } else {
1259
            $sql .= " WHERE pt.rowid = ec.element_id";
1260
        }
1261
        if ($userp && $filteronprojstatus > -1) {
1262
            $sql .= " AND p.fk_statut = " . ((int) $filteronprojstatus);
1263
        }
1264
        if ($usert && $filteronprojstatus > -1) {
1265
            $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = " . ((int) $filteronprojstatus);
1266
        }
1267
        if ($userp) {
1268
            $sql .= " AND ctc.element = 'project'";
1269
        }
1270
        if ($usert) {
1271
            $sql .= " AND ctc.element = 'project_task'";
1272
        }
1273
        $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
1274
        if ($userp) {
1275
            $sql .= " AND ec.fk_socpeople = " . ((int) $userp->id);
1276
        }
1277
        if ($usert) {
1278
            $sql .= " AND ec.fk_socpeople = " . ((int) $usert->id);
1279
        }
1280
        $sql .= " AND ec.statut = 4";
1281
        $sql .= " AND ctc.source = 'internal'";
1282
        if ($projectid) {
1283
            if ($userp) {
1284
                $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectid) . ")";
1285
            }
1286
            if ($usert) {
1287
                $sql .= " AND pt.fk_projet IN (" . $this->db->sanitize($projectid) . ")";
1288
            }
1289
        }
1290
        if ($taskid) {
1291
            if ($userp) {
1292
                $sql .= " ERROR SHOULD NOT HAPPENS";
1293
            }
1294
            if ($usert) {
1295
                $sql .= " AND pt.rowid = " . ((int) $taskid);
1296
            }
1297
        }
1298
        //print $sql;
1299
1300
        dol_syslog(get_class($this) . "::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
1301
        $resql = $this->db->query($sql);
1302
        if ($resql) {
1303
            $num = $this->db->num_rows($resql);
1304
            $i = 0;
1305
            while ($i < $num) {
1306
                $obj = $this->db->fetch_object($resql);
1307
                if (empty($arrayroles[$obj->pid])) {
1308
                    $arrayroles[$obj->pid] = $obj->code;
1309
                } else {
1310
                    $arrayroles[$obj->pid] .= ',' . $obj->code;
1311
                }
1312
                $i++;
1313
            }
1314
            $this->db->free($resql);
1315
        } else {
1316
            dol_print_error($this->db);
1317
        }
1318
1319
        return $arrayroles;
1320
    }
1321
1322
1323
    /**
1324
     *  Return list of id of contacts of task
1325
     *
1326
     *  @param  string  $source     Source
1327
     *  @return array               Array of id of contacts
1328
     */
1329
    public function getListContactId($source = 'internal')
1330
    {
1331
        $contactAlreadySelected = array();
1332
        $tab = $this->liste_contact(-1, $source);
1333
        //var_dump($tab);
1334
        $num = count($tab);
1335
        $i = 0;
1336
        while ($i < $num) {
1337
            if ($source == 'thirdparty') {
1338
                $contactAlreadySelected[$i] = $tab[$i]['socid'];
1339
            } else {
1340
                $contactAlreadySelected[$i] = $tab[$i]['id'];
1341
            }
1342
            $i++;
1343
        }
1344
        return $contactAlreadySelected;
1345
    }
1346
1347
    /**
1348
     * Merge contact of tasks
1349
     *
1350
     * @param   int     $origin_id  Old task id
1351
     * @param   int     $dest_id    New task id
1352
     * @return  bool
1353
     */
1354
    public function mergeContactTask($origin_id, $dest_id)
1355
    {
1356
        $error = 0;
1357
        $origintask = new Task($this->db);
1358
        $result = $origintask->fetch($origin_id);
1359
        if ($result <= 0) {
1360
            return false;
1361
        }
1362
1363
        //Get list of origin contacts
1364
        $arraycontactorigin = array_merge($origintask->liste_contact(-1, 'internal'), $origintask->liste_contact(-1, 'external'));
1365
        if (is_array($arraycontactorigin)) {
1366
            foreach ($arraycontactorigin as $key => $contact) {
1367
                $result = $this->add_contact($contact["id"], $contact["fk_c_type_contact"], $contact["source"]);
1368
                if ($result < 0) {
1369
                    return false;
1370
                }
1371
            }
1372
        }
1373
        return true;
1374
    }
1375
1376
    /**
1377
     * Merge time spent of tasks
1378
     *
1379
     * @param   int     $origin_id  Old task id
1380
     * @param   int     $dest_id    New task id
1381
     * @return  bool
1382
     */
1383
    public function mergeTimeSpentTask($origin_id, $dest_id)
1384
    {
1385
        $ret = true;
1386
1387
        $this->db->begin();
1388
1389
        $sql = "UPDATE " . MAIN_DB_PREFIX . "element_time as et";
1390
        $sql .= " SET et.fk_element = " . ((int) $dest_id);
1391
        $sql .= " WHERE et.elementtype = 'task'";
1392
        $sql .= " AND et.fk_element = " . ((int) $origin_id);
1393
1394
        dol_syslog(get_class($this) . "::mergeTimeSpentTask", LOG_DEBUG);
1395
        if (!$this->db->query($sql)) {
1396
            $this->error = $this->db->lasterror();
1397
            $ret = false;
1398
        }
1399
1400
        if ($ret) {
1401
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1402
            $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) . ")";
1403
            $sql .= " WHERE rowid = " . ((int) $dest_id);
1404
1405
            dol_syslog(get_class($this) . "::mergeTimeSpentTask update project_task", LOG_DEBUG);
1406
            if (!$this->db->query($sql)) {
1407
                $this->error = $this->db->lasterror();
1408
                $ret = false;
1409
            }
1410
        }
1411
1412
        if ($ret == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1413
            $this->db->commit();
1414
        } else {
1415
            $this->db->rollback();
1416
        }
1417
        return $ret;
1418
    }
1419
1420
    /**
1421
     *  Add time spent
1422
     *
1423
     *  @param  User    $user           User object
1424
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
1425
     *  @return int                     Return integer <=0 if KO, >0 if OK
1426
     */
1427
    public function addTimeSpent($user, $notrigger = 0)
1428
    {
1429
        global $langs;
1430
1431
        dol_syslog(get_class($this) . "::addTimeSpent", LOG_DEBUG);
1432
1433
        $ret = 0;
1434
        $now = dol_now();
1435
1436
        // Check parameters
1437
        if (!is_object($user)) {
1438
            dol_print_error(null, "Method addTimeSpent was called with wrong parameter user");
1439
            return -1;
1440
        }
1441
1442
        // Clean parameters
1443
        if (isset($this->timespent_note)) {
1444
            $this->timespent_note = trim($this->timespent_note);
1445
        }
1446
        if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
1447
            $this->timespent_datehour = $this->timespent_date;
1448
        }
1449
1450
        if (getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1451
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php';
1452
            $restrictBefore = dol_time_plus_duree(dol_now(), - getDolGlobalInt('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'), 'm');
1453
1454
            if ($this->timespent_date < $restrictBefore) {
1455
                $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1456
                $this->errors[] = $this->error;
1457
                return -1;
1458
            }
1459
        }
1460
1461
        $this->db->begin();
1462
1463
        $timespent = new TimeSpent($this->db);
1464
        $timespent->fk_element = $this->id;
1465
        $timespent->elementtype = 'task';
1466
        $timespent->element_date = $this->timespent_date;
1467
        $timespent->element_datehour = $this->timespent_datehour;
1468
        $timespent->element_date_withhour = $this->timespent_withhour;
1469
        $timespent->element_duration = $this->timespent_duration;
1470
        $timespent->fk_user = $this->timespent_fk_user;
1471
        $timespent->fk_product = $this->timespent_fk_product;
1472
        $timespent->note = $this->timespent_note;
1473
        $timespent->datec = $this->db->idate($now);
1474
1475
        $result = $timespent->create($user);
1476
1477
        if ($result > 0) {
1478
            $ret = $result;
1479
            $this->timespent_id = $result;
1480
1481
            if (!$notrigger) {
1482
                // Call trigger
1483
                $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
1484
                if ($result < 0) {
1485
                    $ret = -1;
1486
                }
1487
                // End call triggers
1488
            }
1489
        } else {
1490
            $this->error = $this->db->lasterror();
1491
            $ret = -1;
1492
        }
1493
1494
        if ($ret > 0) {
1495
            // Recalculate amount of time spent for task and update denormalized field
1496
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1497
            $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) . ")";
1498
            if (isset($this->progress)) {
1499
                $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1500
            }
1501
            $sql .= " WHERE rowid = " . ((int) $this->id);
1502
1503
            dol_syslog(get_class($this) . "::addTimeSpent", LOG_DEBUG);
1504
            if (!$this->db->query($sql)) {
1505
                $this->error = $this->db->lasterror();
1506
                $ret = -2;
1507
            }
1508
1509
            // Update hourly rate of this time spent entry
1510
            $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1511
            if (!empty($resql_thm_user)) {
1512
                $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1513
                $timespent->thm = $obj_thm_user->thm;
1514
            }
1515
            $res_update = $timespent->update($user);
1516
1517
            dol_syslog(get_class($this) . "::addTimeSpent", LOG_DEBUG);
1518
            if ($res_update <= 0) {
1519
                $this->error = $this->db->lasterror();
1520
                $ret = -2;
1521
            }
1522
        }
1523
1524
        if ($ret > 0) {
1525
            $this->db->commit();
1526
        } else {
1527
            $this->db->rollback();
1528
        }
1529
        return $ret;
1530
    }
1531
1532
    /**
1533
     *  Fetch records of time spent of this task
1534
     *
1535
     *  @param  string  $morewherefilter    Add more filter into where SQL request (must start with ' AND ...')
1536
     *  @return int                         Return integer <0 if KO, array of time spent if OK
1537
     */
1538
    public function fetchTimeSpentOnTask($morewherefilter = '')
1539
    {
1540
        $arrayres = array();
1541
1542
        $sql = "SELECT";
1543
        $sql .= " s.rowid as socid,";
1544
        $sql .= " s.nom as thirdparty_name,";
1545
        $sql .= " s.email as thirdparty_email,";
1546
        $sql .= " ptt.rowid,";
1547
        $sql .= " ptt.ref_ext,";
1548
        $sql .= " ptt.fk_element as fk_task,";
1549
        $sql .= " ptt.element_date as task_date,";
1550
        $sql .= " ptt.element_datehour as task_datehour,";
1551
        $sql .= " ptt.element_date_withhour as task_date_withhour,";
1552
        $sql .= " ptt.element_duration as task_duration,";
1553
        $sql .= " ptt.fk_user,";
1554
        $sql .= " ptt.note,";
1555
        $sql .= " ptt.thm,";
1556
        $sql .= " pt.rowid as task_id,";
1557
        $sql .= " pt.ref as task_ref,";
1558
        $sql .= " pt.label as task_label,";
1559
        $sql .= " p.rowid as project_id,";
1560
        $sql .= " p.ref as project_ref,";
1561
        $sql .= " p.title as project_label,";
1562
        $sql .= " p.public as public";
1563
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet as p";
1564
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
1565
        $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1566
        $sql .= " AND ptt.elementtype = 'task'";
1567
        $sql .= " AND pt.rowid = " . ((int) $this->id);
1568
        $sql .= " AND pt.entity IN (" . getEntity('project') . ")";
1569
        if ($morewherefilter) {
1570
            $sql .= $morewherefilter;
1571
        }
1572
1573
        dol_syslog(get_class($this) . "::fetchAllTimeSpent", LOG_DEBUG);
1574
        $resql = $this->db->query($sql);
1575
        if ($resql) {
1576
            $num = $this->db->num_rows($resql);
1577
1578
            $i = 0;
1579
            while ($i < $num) {
1580
                $obj = $this->db->fetch_object($resql);
1581
1582
                $newobj = new stdClass();
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Projet\Classes\stdClass was not found. Did you mean stdClass? If so, make sure to prefix the type with \.
Loading history...
1583
1584
                $newobj->socid              = $obj->socid;
1585
                $newobj->thirdparty_name    = $obj->thirdparty_name;
1586
                $newobj->thirdparty_email   = $obj->thirdparty_email;
1587
1588
                $newobj->fk_project         = $obj->project_id;
1589
                $newobj->project_ref        = $obj->project_ref;
1590
                $newobj->project_label = $obj->project_label;
1591
                $newobj->public             = $obj->project_public;
1592
1593
                $newobj->fk_task            = $obj->task_id;
1594
                $newobj->task_ref = $obj->task_ref;
1595
                $newobj->task_label = $obj->task_label;
1596
1597
                $newobj->timespent_line_id = $obj->rowid;
1598
                $newobj->timespent_line_ref_ext = $obj->ref_ext;
1599
                $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1600
                $newobj->timespent_line_datehour    = $this->db->jdate($obj->task_datehour);
1601
                $newobj->timespent_line_withhour = $obj->task_date_withhour;
1602
                $newobj->timespent_line_duration = $obj->task_duration;
1603
                $newobj->timespent_line_fk_user = $obj->fk_user;
1604
                $newobj->timespent_line_thm = $obj->thm;    // hourly rate
1605
                $newobj->timespent_line_note = $obj->note;
1606
1607
                $arrayres[] = $newobj;
1608
1609
                $i++;
1610
            }
1611
1612
            $this->db->free($resql);
1613
1614
            $this->lines = $arrayres;
1615
            return 1;
1616
        } else {
1617
            dol_print_error($this->db);
1618
            $this->error = "Error " . $this->db->lasterror();
1619
            return -1;
1620
        }
1621
    }
1622
1623
1624
    /**
1625
     *  Calculate total of time spent for task
1626
     *
1627
     *  @param  User|int    $userobj            Filter on user. null or 0=No filter
1628
     *  @param  string      $morewherefilter    Add more filter into where SQL request (must start with ' AND ...')
1629
     *  @return array|int                       Array of info for task array('min_date', 'max_date', 'total_duration', 'total_amount', 'nblines', 'nblinesnull')
1630
     */
1631
    public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1632
    {
1633
        if (is_object($userobj)) {
1634
            $userid = $userobj->id;
1635
        } else {
1636
            $userid = $userobj; // old method
1637
        }
1638
1639
        $id = $this->id;
1640
        if (empty($id) && empty($userid)) {
1641
            dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1642
            return -1;
1643
        }
1644
1645
        $result = array();
1646
1647
        $sql = "SELECT";
1648
        $sql .= " MIN(t.element_datehour) as min_date,";
1649
        $sql .= " MAX(t.element_datehour) as max_date,";
1650
        $sql .= " SUM(t.element_duration) as total_duration,";
1651
        $sql .= " SUM(t.element_duration / 3600 * " . $this->db->ifsql("t.thm IS NULL", 0, "t.thm") . ") as total_amount,";
1652
        $sql .= " COUNT(t.rowid) as nblines,";
1653
        $sql .= " SUM(" . $this->db->ifsql("t.thm IS NULL", 1, 0) . ") as nblinesnull";
1654
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as t";
1655
        $sql .= " WHERE t.elementtype='task'";
1656
        if ($morewherefilter) {
1657
            $sql .= $morewherefilter;
1658
        }
1659
        if ($id > 0) {
1660
            $sql .= " AND t.fk_element = " . ((int) $id);
1661
        }
1662
        if ($userid > 0) {
1663
            $sql .= " AND t.fk_user = " . ((int) $userid);
1664
        }
1665
1666
        dol_syslog(get_class($this) . "::getSummaryOfTimeSpent", LOG_DEBUG);
1667
        $resql = $this->db->query($sql);
1668
        if ($resql) {
1669
            $obj = $this->db->fetch_object($resql);
1670
1671
            $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1672
            $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1673
            $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1674
1675
            $this->timespent_min_date = $this->db->jdate($obj->min_date);
1676
            $this->timespent_max_date = $this->db->jdate($obj->max_date);
1677
            $this->timespent_total_duration = $obj->total_duration;
1678
            $this->timespent_total_amount = $obj->total_amount;
1679
            $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1680
            $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1681
1682
            $this->db->free($resql);
1683
        } else {
1684
            dol_print_error($this->db);
1685
        }
1686
        return $result;
1687
    }
1688
1689
    /**
1690
     *  Calculate quantity and value of time consumed using the thm (hourly amount value of work for user entering time)
1691
     *
1692
     *  @param      User|string $fuser      Filter on a dedicated user
1693
     *  @param      string      $dates      Start date (ex 00:00:00)
1694
     *  @param      string      $datee      End date (ex 23:59:59)
1695
     *  @return     array                   Array of info for task array('amount','nbseconds','nblinesnull')
1696
     */
1697
    public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1698
    {
1699
        $id = $this->id;
1700
1701
        $result = array();
1702
1703
        $sql = "SELECT";
1704
        $sql .= " SUM(t.element_duration) as nbseconds,";
1705
        $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";
1706
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as t";
1707
        $sql .= " WHERE t.elementtype='task' AND t.fk_element = " . ((int) $id);
1708
        if (is_object($fuser) && $fuser->id > 0) {
1709
            $sql .= " AND fk_user = " . ((int) $fuser->id);
1710
        }
1711
        if ($dates > 0) {
1712
            $datefieldname = "element_datehour";
1713
            $sql .= " AND (" . $datefieldname . " >= '" . $this->db->idate($dates) . "' OR " . $datefieldname . " IS NULL)";
1714
        }
1715
        if ($datee > 0) {
1716
            $datefieldname = "element_datehour";
1717
            $sql .= " AND (" . $datefieldname . " <= '" . $this->db->idate($datee) . "' OR " . $datefieldname . " IS NULL)";
1718
        }
1719
        //print $sql;
1720
1721
        dol_syslog(get_class($this) . "::getSumOfAmount", LOG_DEBUG);
1722
        $resql = $this->db->query($sql);
1723
        if ($resql) {
1724
            $obj = $this->db->fetch_object($resql);
1725
1726
            $result['amount'] = $obj->amount;
1727
            $result['nbseconds'] = $obj->nbseconds;
1728
            $result['nblinesnull'] = $obj->nblinesnull;
1729
1730
            $this->db->free($resql);
1731
            return $result;
1732
        } else {
1733
            dol_print_error($this->db);
1734
            return $result;
1735
        }
1736
    }
1737
1738
    /**
1739
     *  Load properties of timespent of a task from the time spent ID.
1740
     *
1741
     *  @param  int     $id     Id in time spent table
1742
     *  @return int             Return integer <0 if KO, >0 if OK
1743
     */
1744
    public function fetchTimeSpent($id)
1745
    {
1746
        $timespent = new TimeSpent($this->db);
1747
        $timespent->fetch($id);
1748
1749
        dol_syslog(get_class($this) . "::fetchTimeSpent", LOG_DEBUG);
1750
1751
        if ($timespent->id > 0) {
1752
            $this->timespent_id = $timespent->id;
1753
            $this->id = $timespent->fk_element;
1754
            $this->timespent_date = $timespent->element_date;
1755
            $this->timespent_datehour   = $timespent->element_datehour;
1756
            $this->timespent_withhour   = $timespent->element_date_withhour;
1757
            $this->timespent_duration = $timespent->element_duration;
1758
            $this->timespent_fk_user    = $timespent->fk_user;
1759
            $this->timespent_fk_product = $timespent->fk_product;
1760
            $this->timespent_thm        = $timespent->thm; // hourly rate
1761
            $this->timespent_note = $timespent->note;
1762
1763
            return 1;
1764
        }
1765
1766
        return 0;
1767
    }
1768
1769
    /**
1770
     *  Load all records of time spent
1771
     *
1772
     *  @param  User        $userobj            User object
1773
     *  @param  string      $morewherefilter    Add more filter into where SQL request (must start with ' AND ...')
1774
     *  @return array|int                       Return integer <0 if KO, array of time spent if OK
1775
     */
1776
    public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1777
    {
1778
        $arrayres = array();
1779
1780
        $sql = "SELECT";
1781
        $sql .= " s.rowid as socid,";
1782
        $sql .= " s.nom as thirdparty_name,";
1783
        $sql .= " s.email as thirdparty_email,";
1784
        $sql .= " ptt.rowid,";
1785
        $sql .= " ptt.fk_element as fk_task,";
1786
        $sql .= " ptt.element_date as task_date,";
1787
        $sql .= " ptt.element_datehour as task_datehour,";
1788
        $sql .= " ptt.element_date_withhour as task_date_withhour,";
1789
        $sql .= " ptt.element_duration as task_duration,";
1790
        $sql .= " ptt.fk_user,";
1791
        $sql .= " ptt.note,";
1792
        $sql .= " ptt.thm,";
1793
        $sql .= " pt.rowid as task_id,";
1794
        $sql .= " pt.ref as task_ref,";
1795
        $sql .= " pt.label as task_label,";
1796
        $sql .= " p.rowid as project_id,";
1797
        $sql .= " p.ref as project_ref,";
1798
        $sql .= " p.title as project_label,";
1799
        $sql .= " p.public as public";
1800
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet as p";
1801
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
1802
        $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1803
        $sql .= " AND ptt.elementtype = 'task'";
1804
        $sql .= " AND ptt.fk_user = " . ((int) $userobj->id);
1805
        $sql .= " AND pt.entity IN (" . getEntity('project') . ")";
1806
        if ($morewherefilter) {
1807
            $sql .= $morewherefilter;
1808
        }
1809
1810
        dol_syslog(get_class($this) . "::fetchAllTimeSpent", LOG_DEBUG);
1811
        $resql = $this->db->query($sql);
1812
        if ($resql) {
1813
            $num = $this->db->num_rows($resql);
1814
1815
            $i = 0;
1816
            while ($i < $num) {
1817
                $obj = $this->db->fetch_object($resql);
1818
1819
                $newobj = new stdClass();
1820
1821
                $newobj->socid              = $obj->socid;
1822
                $newobj->thirdparty_name    = $obj->thirdparty_name;
1823
                $newobj->thirdparty_email   = $obj->thirdparty_email;
1824
1825
                $newobj->fk_project         = $obj->project_id;
1826
                $newobj->project_ref        = $obj->project_ref;
1827
                $newobj->project_label = $obj->project_label;
1828
                $newobj->public             = $obj->project_public;
1829
1830
                $newobj->fk_task            = $obj->task_id;
1831
                $newobj->task_ref = $obj->task_ref;
1832
                $newobj->task_label = $obj->task_label;
1833
1834
                $newobj->timespent_id = $obj->rowid;
1835
                $newobj->timespent_date = $this->db->jdate($obj->task_date);
1836
                $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1837
                $newobj->timespent_withhour = $obj->task_date_withhour;
1838
                $newobj->timespent_duration = $obj->task_duration;
1839
                $newobj->timespent_fk_user = $obj->fk_user;
1840
                $newobj->timespent_thm = $obj->thm; // hourly rate
1841
                $newobj->timespent_note = $obj->note;
1842
1843
                $arrayres[] = $newobj;
1844
1845
                $i++;
1846
            }
1847
1848
            $this->db->free($resql);
1849
        } else {
1850
            dol_print_error($this->db);
1851
            $this->error = "Error " . $this->db->lasterror();
1852
            return -1;
1853
        }
1854
1855
        return $arrayres;
1856
    }
1857
1858
    /**
1859
     *  Update time spent
1860
     *
1861
     *  @param  User    $user           User id
1862
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
1863
     *  @return int                     Return integer <0 if KO, >0 if OK
1864
     */
1865
    public function updateTimeSpent($user, $notrigger = 0)
1866
    {
1867
        global $conf, $langs;
1868
1869
        $ret = 0;
1870
1871
        // Check parameters
1872
        if ($this->timespent_date == '') {
1873
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1874
            return -1;
1875
        }
1876
        if (!($this->timespent_fk_user > 0)) {
1877
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1878
            return -1;
1879
        }
1880
1881
        // Clean parameters
1882
        if (empty($this->timespent_datehour)) {
1883
            $this->timespent_datehour = $this->timespent_date;
1884
        }
1885
        if (isset($this->timespent_note)) {
1886
            $this->timespent_note = trim($this->timespent_note);
1887
        }
1888
1889
        if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1890
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php';
1891
            $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1892
1893
            if ($this->timespent_date < $restrictBefore) {
1894
                $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1895
                $this->errors[] = $this->error;
1896
                return -1;
1897
            }
1898
        }
1899
1900
        $this->db->begin();
1901
1902
        $timespent = new TimeSpent($this->db);
1903
        $timespent->fetch($this->timespent_id);
1904
        $timespent->element_date = $this->timespent_date;
1905
        $timespent->element_datehour = $this->timespent_datehour;
1906
        $timespent->element_date_withhour = $this->timespent_withhour;
1907
        $timespent->element_duration = $this->timespent_duration;
1908
        $timespent->fk_user = $this->timespent_fk_user;
1909
        $timespent->fk_product = $this->timespent_fk_product;
1910
        $timespent->note = $this->timespent_note;
1911
        $timespent->invoice_id = $this->timespent_invoiceid;
1912
        $timespent->invoice_line_id = $this->timespent_invoicelineid;
1913
1914
        dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1915
        if ($timespent->update($user) > 0) {
1916
            if (!$notrigger) {
1917
                // Call trigger
1918
                $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1919
                if ($result < 0) {
1920
                    $this->db->rollback();
1921
                    $ret = -1;
1922
                } else {
1923
                    $ret = 1;
1924
                }
1925
                // End call triggers
1926
            } else {
1927
                $ret = 1;
1928
            }
1929
        } else {
1930
            $this->error = $this->db->lasterror();
1931
            $this->db->rollback();
1932
            $ret = -1;
1933
        }
1934
1935
        if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) {
1936
            if ($this->timespent_old_duration != $this->timespent_duration) {
1937
                // Recalculate amount of time spent for task and update denormalized field
1938
                $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1939
                $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) . ")";
1940
                if (isset($this->progress)) {
1941
                    $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1942
                }
1943
                $sql .= " WHERE rowid = " . ((int) $this->id);
1944
1945
                dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1946
                if (!$this->db->query($sql)) {
1947
                    $this->error = $this->db->lasterror();
1948
                    $this->db->rollback();
1949
                    $ret = -2;
1950
                }
1951
            }
1952
1953
            // Update hourly rate of this time spent entry, but only if it was not set initially
1954
            $res_update = 1;
1955
            if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) {
1956
                $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1957
                if (!empty($resql_thm_user)) {
1958
                    $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1959
                    $timespent->thm = $obj_thm_user->thm;
1960
                }
1961
                $res_update = $timespent->update($user);
1962
            }
1963
1964
            dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1965
            if ($res_update <= 0) {
1966
                $this->error = $this->db->lasterror();
1967
                $ret = -2;
1968
            }
1969
        }
1970
1971
        if ($ret >= 0) {
1972
            $this->db->commit();
1973
        }
1974
        return $ret;
1975
    }
1976
1977
    /**
1978
     *  Delete time spent
1979
     *
1980
     *  @param  User    $user           User that delete
1981
     *  @param  int     $notrigger      0=launch triggers after, 1=disable triggers
1982
     *  @return int                     Return integer <0 if KO, >0 if OK
1983
     */
1984
    public function delTimeSpent($user, $notrigger = 0)
1985
    {
1986
        global $conf, $langs;
1987
1988
        $error = 0;
1989
1990
        if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1991
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php';
1992
            $restrictBefore = dol_time_plus_duree(dol_now(), - $conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1993
1994
            if ($this->timespent_date < $restrictBefore) {
1995
                $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1996
                $this->errors[] = $this->error;
1997
                return -1;
1998
            }
1999
        }
2000
2001
        $this->db->begin();
2002
2003
        if (!$notrigger) {
2004
            // Call trigger
2005
            $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
2006
            if ($result < 0) {
2007
                $error++;
2008
            }
2009
            // End call triggers
2010
        }
2011
2012
        if (!$error) {
2013
            $timespent = new TimeSpent($this->db);
2014
            $timespent->fetch($this->timespent_id);
2015
2016
            $res_del = $timespent->delete($user);
2017
2018
            if ($res_del < 0) {
2019
                $error++;
2020
                $this->errors[] = "Error " . $this->db->lasterror();
2021
            }
2022
        }
2023
2024
        if (!$error) {
2025
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
2026
            $sql .= " SET duration_effective = duration_effective - " . $this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
2027
            $sql .= " WHERE rowid = " . ((int) $this->id);
2028
2029
            dol_syslog(get_class($this) . "::delTimeSpent", LOG_DEBUG);
2030
            if ($this->db->query($sql)) {
2031
                $result = 0;
2032
            } else {
2033
                $this->error = $this->db->lasterror();
2034
                $result = -2;
2035
            }
2036
        }
2037
2038
        // Commit or rollback
2039
        if ($error) {
2040
            foreach ($this->errors as $errmsg) {
2041
                dol_syslog(get_class($this) . "::delTimeSpent " . $errmsg, LOG_ERR);
2042
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2043
            }
2044
            $this->db->rollback();
2045
            return -1 * $error;
2046
        } else {
2047
            $this->db->commit();
2048
            return 1;
2049
        }
2050
    }
2051
2052
    /** Load an object from its id and create a new one in database
2053
     *
2054
     *  @param  User    $user                   User making the clone
2055
     *  @param  int     $fromid                 Id of object to clone
2056
     *  @param  int     $project_id             Id of project to attach clone task
2057
     *  @param  int     $parent_task_id         Id of task to attach clone task
2058
     *  @param  bool    $clone_change_dt        recalculate date of task regarding new project start date
2059
     *  @param  bool    $clone_affectation      clone affectation of project
2060
     *  @param  bool    $clone_time             clone time of project
2061
     *  @param  bool    $clone_file             clone file of project
2062
     *  @param  bool    $clone_note             clone note of project
2063
     *  @param  bool    $clone_prog             clone progress of project
2064
     *  @return int                             New id of clone
2065
     */
2066
    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)
2067
    {
2068
        global $langs, $conf;
2069
2070
        $error = 0;
2071
2072
        //Use 00:00 of today if time is use on task.
2073
        $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
2074
2075
        $datec = $now;
2076
2077
        $clone_task = new Task($this->db);
2078
        $origin_task = new Task($this->db);
2079
2080
        $clone_task->context['createfromclone'] = 'createfromclone';
2081
2082
        $this->db->begin();
2083
2084
        // Load source object
2085
        $clone_task->fetch($fromid);
2086
        $clone_task->fetch_optionals();
2087
        //var_dump($clone_task->array_options);exit;
2088
2089
        $origin_task->fetch($fromid);
2090
2091
        $defaultref = '';
2092
        $obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
2093
        if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON') . ".php")) {
2094
            require_once DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON') . '.php';
2095
            $modTask = new $obj();
2096
            $defaultref = $modTask->getNextValue(0, $clone_task);
2097
        }
2098
2099
        $ori_project_id                 = $clone_task->fk_project;
2100
2101
        $clone_task->id                 = 0;
2102
        $clone_task->ref                = $defaultref;
2103
        $clone_task->fk_project = $project_id;
2104
        $clone_task->fk_task_parent = $parent_task_id;
2105
        $clone_task->date_c = $datec;
2106
        $clone_task->planned_workload = $origin_task->planned_workload;
2107
        $clone_task->rang = $origin_task->rang;
2108
        $clone_task->priority = $origin_task->priority;
2109
2110
        //Manage Task Date
2111
        if ($clone_change_dt) {
2112
            $projectstatic = new Project($this->db);
2113
            $projectstatic->fetch($ori_project_id);
2114
2115
            //Origin project start date
2116
            $orign_project_dt_start = $projectstatic->date_start;
2117
2118
            //Calculate new task start date with difference between origin proj start date and origin task start date
2119
            if (!empty($clone_task->date_start)) {
2120
                $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
2121
            }
2122
2123
            //Calculate new task end date with difference between origin proj end date and origin task end date
2124
            if (!empty($clone_task->date_end)) {
2125
                $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
2126
            }
2127
        }
2128
2129
        if (!$clone_prog) {
2130
            $clone_task->progress = 0;
2131
        }
2132
2133
        // Create clone
2134
        $result = $clone_task->create($user);
2135
2136
        // Other options
2137
        if ($result < 0) {
2138
            $this->error = $clone_task->error;
2139
            $error++;
2140
        }
2141
        // End
2142
        if ($error) {
2143
            $clone_task_id = 0;  // For static tool check
2144
        } else {
2145
            $clone_task_id = $clone_task->id;
2146
            $clone_task_ref = $clone_task->ref;
2147
2148
            //Note Update
2149
            if (!$clone_note) {
2150
                $clone_task->note_private = '';
2151
                $clone_task->note_public = '';
2152
            } else {
2153
                $this->db->begin();
2154
                $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
2155
                if ($res < 0) {
2156
                    $this->error .= $clone_task->error;
2157
                    $error++;
2158
                    $this->db->rollback();
2159
                } else {
2160
                    $this->db->commit();
2161
                }
2162
2163
                $this->db->begin();
2164
                $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
2165
                if ($res < 0) {
2166
                    $this->error .= $clone_task->error;
2167
                    $error++;
2168
                    $this->db->rollback();
2169
                } else {
2170
                    $this->db->commit();
2171
                }
2172
            }
2173
2174
            //Duplicate file
2175
            if ($clone_file) {
2176
                require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
2177
2178
                //retrieve project origin ref to know folder to copy
2179
                $projectstatic = new Project($this->db);
2180
                $projectstatic->fetch($ori_project_id);
2181
                $ori_project_ref = $projectstatic->ref;
2182
2183
                if ($ori_project_id != $project_id) {
2184
                    $projectstatic->fetch($project_id);
2185
                    $clone_project_ref = $projectstatic->ref;
2186
                } else {
2187
                    $clone_project_ref = $ori_project_ref;
2188
                }
2189
2190
                $clone_task_dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($clone_project_ref) . "/" . dol_sanitizeFileName($clone_task_ref);
2191
                $ori_task_dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($ori_project_ref) . "/" . dol_sanitizeFileName($fromid);
2192
2193
                $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
2194
                foreach ($filearray as $key => $file) {
2195
                    if (!file_exists($clone_task_dir)) {
2196
                        if (dol_mkdir($clone_task_dir) < 0) {
2197
                            $this->error .= $langs->trans('ErrorInternalErrorDetected') . ':dol_mkdir';
2198
                            $error++;
2199
                        }
2200
                    }
2201
2202
                    $rescopy = dol_copy($ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name'], 0, 1);
2203
                    if (is_numeric($rescopy) && $rescopy < 0) {
2204
                        $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name']);
2205
                        $error++;
2206
                    }
2207
                }
2208
            }
2209
2210
            // clone affectation
2211
            if ($clone_affectation) {
2212
                $origin_task = new Task($this->db);
2213
                $origin_task->fetch($fromid);
2214
2215
                foreach (array('internal', 'external') as $source) {
2216
                    $tab = $origin_task->liste_contact(-1, $source);
2217
                    $num = count($tab);
2218
                    $i = 0;
2219
                    while ($i < $num) {
2220
                        $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2221
                        if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2222
                            $langs->load("errors");
2223
                            $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2224
                            $error++;
2225
                        } else {
2226
                            if ($clone_task->error != '') {
2227
                                $this->error .= $clone_task->error;
2228
                                $error++;
2229
                            }
2230
                        }
2231
                        $i++;
2232
                    }
2233
                }
2234
            }
2235
2236
            if ($clone_time) {
2237
                //TODO clone time of affectation
2238
            }
2239
        }
2240
2241
        unset($clone_task->context['createfromclone']);
2242
2243
        if (!$error) {
2244
            $this->db->commit();
2245
            return $clone_task_id;
2246
        } else {
2247
            $this->db->rollback();
2248
            dol_syslog(get_class($this) . "::createFromClone nbError: " . $error . " error : " . $this->error, LOG_ERR);
2249
            return -1;
2250
        }
2251
    }
2252
2253
2254
    /**
2255
     *  Return status label of object
2256
     *
2257
     *  @param  integer $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
2258
     *  @return string              Label
2259
     */
2260
    public function getLibStatut($mode = 0)
2261
    {
2262
        return $this->LibStatut($this->status, $mode);
2263
    }
2264
2265
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2266
    /**
2267
     *  Return status label for an object
2268
     *
2269
     *  @param  int         $status     Id status
2270
     *  @param  integer     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
2271
     *  @return string                  Label
2272
     */
2273
    public function LibStatut($status, $mode = 0)
2274
    {
2275
		// phpcs:enable
2276
        global $langs;
2277
2278
        // list of Statut of the task
2279
        $this->labelStatus[0] = 'Draft';
2280
        $this->labelStatus[1] = 'ToDo';
2281
        $this->labelStatus[2] = 'Running';
2282
        $this->labelStatus[3] = 'Finish';
2283
        $this->labelStatus[4] = 'Transfered';
2284
        $this->labelStatusShort[0] = 'Draft';
2285
        $this->labelStatusShort[1] = 'ToDo';
2286
        $this->labelStatusShort[2] = 'Running';
2287
        $this->labelStatusShort[3] = 'Completed';
2288
        $this->labelStatusShort[4] = 'Transfered';
2289
2290
        if ($mode == 0) {
2291
            return $langs->trans($this->labelStatus[$status]);
2292
        } elseif ($mode == 1) {
2293
            return $langs->trans($this->labelStatusShort[$status]);
2294
        } elseif ($mode == 2) {
2295
            if ($status == 0) {
2296
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2297
            } elseif ($status == 1) {
2298
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2299
            } elseif ($status == 2) {
2300
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2301
            } elseif ($status == 3) {
2302
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2303
            } elseif ($status == 4) {
2304
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2305
            } elseif ($status == 5) {
2306
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2307
            }
2308
        } elseif ($mode == 3) {
2309
            if ($status == 0) {
2310
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0');
2311
            } elseif ($status == 1) {
2312
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1');
2313
            } elseif ($status == 2) {
2314
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3');
2315
            } elseif ($status == 3) {
2316
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2317
            } elseif ($status == 4) {
2318
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2319
            } elseif ($status == 5) {
2320
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5');
2321
            }
2322
        } elseif ($mode == 4) {
2323
            if ($status == 0) {
2324
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0') . ' ' . $langs->trans($this->labelStatus[$status]);
2325
            } elseif ($status == 1) {
2326
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1') . ' ' . $langs->trans($this->labelStatus[$status]);
2327
            } elseif ($status == 2) {
2328
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3') . ' ' . $langs->trans($this->labelStatus[$status]);
2329
            } elseif ($status == 3) {
2330
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatus[$status]);
2331
            } elseif ($status == 4) {
2332
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatus[$status]);
2333
            } elseif ($status == 5) {
2334
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5') . ' ' . $langs->trans($this->labelStatus[$status]);
2335
            }
2336
        } elseif ($mode == 5) {
2337
            /*if ($status==0) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2338
            elseif ($status==1) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2339
            elseif ($status==2) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2340
            elseif ($status==3) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2341
            elseif ($status==4) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2342
            elseif ($status==5) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2343
            */
2344
            //else return $this->progress.' %';
2345
            return '&nbsp;';
2346
        } elseif ($mode == 6) {
2347
            /*if ($status==0) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2348
            elseif ($status==1) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2349
            elseif ($status==2) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2350
            elseif ($status==3) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2351
            elseif ($status==4) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2352
            elseif ($status==5) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2353
            */
2354
            //else return $this->progress.' %';
2355
            return '&nbsp;';
2356
        }
2357
        return "";
2358
    }
2359
2360
    /**
2361
     *  Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF
2362
     *
2363
     *  @param  string      $modele         force le modele a utiliser ('' par default)
2364
     *  @param  Translate   $outputlangs    object lang a utiliser pour traduction
2365
     *  @param  int         $hidedetails    Hide details of lines
2366
     *  @param  int         $hidedesc       Hide description
2367
     *  @param  int         $hideref        Hide ref
2368
     *  @return int                         0 if KO, 1 if OK
2369
     */
2370
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2371
    {
2372
        $outputlangs->load("projects");
2373
2374
        if (!dol_strlen($modele)) {
2375
            $modele = 'nodefault';
2376
2377
            if (!empty($this->model_pdf)) {
2378
                $modele = $this->model_pdf;
2379
            } elseif (getDolGlobalString('PROJECT_TASK_ADDON_PDF')) {
2380
                $modele = getDolGlobalString('PROJECT_TASK_ADDON_PDF');
2381
            }
2382
        }
2383
2384
        $modelpath = "core/modules/project/task/doc/";
2385
2386
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2387
    }
2388
2389
2390
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2391
    /**
2392
     * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2393
     *
2394
     * @param   User    $user   Object user
2395
     * @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK
2396
     */
2397
    public function load_board($user)
2398
    {
2399
		// phpcs:enable
2400
        global $conf, $langs;
2401
2402
        // For external user, no check is done on company because readability is managed by public status of project and assignment.
2403
        //$socid = $user->socid;
2404
        $socid = 0;
2405
2406
        $projectstatic = new Project($this->db);
2407
        $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2408
2409
        // List of tasks (does not care about permissions. Filtering will be done later)
2410
        $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2411
        $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2412
        $sql .= " t.dateo as date_start, t.datee as date_end";
2413
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
2414
        //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2415
        //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2416
        $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
2417
        $sql .= " WHERE p.entity IN (" . getEntity('project', 0) . ')';
2418
        $sql .= " AND p.fk_statut = 1";
2419
        $sql .= " AND t.fk_projet = p.rowid";
2420
        $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2421
        if (!$user->hasRight('projet', 'all', 'lire')) {
2422
            $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectsListId) . ")";
2423
        }
2424
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2425
        //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).")";
2426
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2427
        // 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))";
2428
2429
        //print $sql;
2430
        $resql = $this->db->query($sql);
2431
        if ($resql) {
2432
            $task_static = new Task($this->db);
2433
2434
            $response = new WorkboardResponse();
2435
            $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2436
            $response->label = $langs->trans("OpenedTasks");
2437
            if ($user->hasRight("projet", "all", "lire")) {
2438
                $response->url = constant('BASE_URL') . '/projet/tasks/list.php?mainmenu=project';
2439
            } else {
2440
                $response->url = constant('BASE_URL') . '/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2441
            }
2442
            $response->img = img_object('', "task");
2443
2444
            // This assignment in condition is not a bug. It allows walking the results.
2445
            while ($obj = $this->db->fetch_object($resql)) {
2446
                $response->nbtodo++;
2447
2448
                $task_static->projectstatus = $obj->projectstatus;
2449
                $task_static->progress = $obj->progress;
2450
                $task_static->fk_statut = $obj->status;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Projet\Classes\Task::$fk_statut has been deprecated: use status instead ( Ignorable by Annotation )

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

2450
                /** @scrutinizer ignore-deprecated */ $task_static->fk_statut = $obj->status;

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.

Loading history...
2451
                $task_static->status = $obj->status;
2452
                $task_static->date_start = $this->db->jdate($obj->date_start);
2453
                $task_static->date_end = $this->db->jdate($obj->date_end);
2454
2455
                if ($task_static->hasDelay()) {
2456
                    $response->nbtodolate++;
2457
                }
2458
            }
2459
2460
            return $response;
2461
        } else {
2462
            $this->error = $this->db->error();
2463
            return -1;
2464
        }
2465
    }
2466
2467
2468
    /**
2469
     *      Load indicators this->nb for state board
2470
     *
2471
     *      @return     int         Return integer <0 if ko, >0 if ok
2472
     */
2473
    public function loadStateBoard()
2474
    {
2475
        global $user;
2476
2477
        $mine = 0;
2478
        $socid = $user->socid;
2479
2480
        $projectstatic = new Project($this->db);
2481
        $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2482
2483
        // List of tasks (does not care about permissions. Filtering will be done later)
2484
        $sql = "SELECT count(p.rowid) as nb";
2485
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
2486
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s on p.fk_soc = s.rowid";
2487
        if (!$user->hasRight('societe', 'client', 'voir')) {
2488
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2489
        }
2490
        $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
2491
        $sql .= " WHERE p.entity IN (" . getEntity('project', 0) . ')';
2492
        $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2493
        if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
2494
            $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectsListId) . ")";
2495
        }
2496
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2497
        //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).")";
2498
        if ($socid) {
2499
            $sql .= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . ((int) $socid) . ")";
2500
        }
2501
        if (!$user->hasRight('societe', 'client', 'voir')) {
2502
            $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id) . ") OR (s.rowid IS NULL))";
2503
        }
2504
2505
        $resql = $this->db->query($sql);
2506
        if ($resql) {
2507
            // This assignment in condition is not a bug. It allows walking the results.
2508
            while ($obj = $this->db->fetch_object($resql)) {
2509
                $this->nb["tasks"] = $obj->nb;
2510
            }
2511
            $this->db->free($resql);
2512
            return 1;
2513
        } else {
2514
            dol_print_error($this->db);
2515
            $this->error = $this->db->error();
2516
            return -1;
2517
        }
2518
    }
2519
2520
    /**
2521
     * Is the task delayed?
2522
     *
2523
     * @return bool
2524
     */
2525
    public function hasDelay()
2526
    {
2527
        global $conf;
2528
2529
        if (!($this->progress >= 0 && $this->progress < 100)) {
2530
            return false;
2531
        }
2532
2533
        $now = dol_now();
2534
2535
        $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset($this->datee) && $this->datee > 0) ? $this->datee : 0);
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Projet\Classes\Task::$datee has been deprecated: Use date_end instead ( Ignorable by Annotation )

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

2535
        $datetouse = ($this->date_end > 0) ? $this->date_end : ((isset(/** @scrutinizer ignore-deprecated */ $this->datee) && $this->datee > 0) ? $this->datee : 0);

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.

Loading history...
2536
2537
        return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2538
    }
2539
2540
    /**
2541
     *  Return clicable link of object (with eventually picto)
2542
     *
2543
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
2544
     *  @param      array       $arraydata              Array of data
2545
     *  @return     string                              HTML Code for Kanban thumb.
2546
     */
2547
    public function getKanbanView($option = '', $arraydata = null)
2548
    {
2549
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2550
2551
        $return = '<div class="box-flex-item box-flex-grow-zero">';
2552
        $return .= '<div class="info-box info-box-sm info-box-kanban">';
2553
        $return .= '<span class="info-box-icon bg-infobox-action">';
2554
        $return .= img_picto('', $this->picto);
2555
        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2556
        $return .= '</span>';
2557
        $return .= '<div class="info-box-content">';
2558
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>';
2559
        if ($selected >= 0) {
2560
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
2561
        }
2562
        if (!empty($arraydata['projectlink'])) {
2563
            //$tmpproject = $arraydata['project'];
2564
            //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
2565
            $return .= '<br><span class="info-box-status ">' . $arraydata['projectlink'] . '</span>';
2566
        }
2567
        if (property_exists($this, 'budget_amount')) {
2568
            //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
2569
        }
2570
        if (property_exists($this, 'duration_effective')) {
2571
            $return .= '<br><div class="info-box-label progressinkanban paddingtop">' . getTaskProgressView($this, false, true) . '</div>';
2572
        }
2573
        $return .= '</div>';
2574
        $return .= '</div>';
2575
        $return .= '</div>';
2576
2577
        return $return;
2578
    }
2579
2580
    /**
2581
     *    Merge a task with another one, deleting the given task.
2582
     *    The task given in parameter will be removed.
2583
     *
2584
     *    @param    int     $task_origin_id     Task to merge the data from
2585
     *    @return   int                         -1 if error
2586
     */
2587
    public function mergeTask($task_origin_id)
2588
    {
2589
        global $langs, $hookmanager, $user, $action;
2590
2591
        $error = 0;
2592
        $task_origin = new Task($this->db);     // The thirdparty that we will delete
2593
2594
        dol_syslog("mergeTask merge task id=" . $task_origin_id . " (will be deleted) into the task id=" . $this->id);
2595
2596
        $langs->load('error');
2597
2598
        if (!$error && $task_origin->fetch($task_origin_id) < 1) {
2599
            $this->error = $langs->trans('ErrorRecordNotFound');
2600
            $error++;
2601
        }
2602
2603
        if (!$error) {
2604
            $this->db->begin();
2605
2606
            // Recopy some data
2607
            $listofproperties = array(
2608
                'label', 'description', 'duration_effective', 'planned_workload', 'datec', 'date_start',
2609
                'date_end', 'fk_user_creat', 'fk_user_valid', 'fk_statut', 'progress', 'budget_amount',
2610
                'priority', 'rang', 'fk_projet', 'fk_task_parent'
2611
            );
2612
            foreach ($listofproperties as $property) {
2613
                if (empty($this->$property)) {
2614
                    $this->$property = $task_origin->$property;
2615
                }
2616
            }
2617
2618
            // Concat some data
2619
            $listofproperties = array(
2620
                'note_public', 'note_private'
2621
            );
2622
            foreach ($listofproperties as $property) {
2623
                $this->$property = dol_concatdesc($this->$property, $task_origin->$property);
2624
            }
2625
2626
            // Merge extrafields
2627
            if (is_array($task_origin->array_options)) {
2628
                foreach ($task_origin->array_options as $key => $val) {
2629
                    if (empty($this->array_options[$key])) {
2630
                        $this->array_options[$key] = $val;
2631
                    }
2632
                }
2633
            }
2634
2635
            // Update
2636
            $result = $this->update($user);
2637
2638
            if ($result < 0) {
2639
                $error++;
2640
            }
2641
2642
            // Merge time spent
2643
            if (!$error) {
2644
                $result = $this->mergeTimeSpentTask($task_origin_id, $this->id);
2645
                if ($result != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2646
                    $error++;
2647
                }
2648
            }
2649
2650
            // Merge contacts
2651
            if (!$error) {
2652
                $result = $this->mergeContactTask($task_origin_id, $this->id);
2653
                if ($result != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2654
                    $error++;
2655
                }
2656
            }
2657
2658
            // External modules should update their ones too
2659
            if (!$error) {
2660
                $parameters = array('task_origin' => $task_origin->id, 'task_dest' => $this->id);
2661
                $reshook = $hookmanager->executeHooks('replaceThirdparty', $parameters, $this, $action);
2662
2663
                if ($reshook < 0) {
2664
                    $this->error = $hookmanager->error;
2665
                    $this->errors = $hookmanager->errors;
2666
                    $error++;
2667
                }
2668
            }
2669
2670
2671
            if (!$error) {
2672
                $this->context = array('merge' => 1, 'mergefromid' => $task_origin->id, 'mergefromref' => $task_origin->ref);
2673
2674
                // Call trigger
2675
                $result = $this->call_trigger('TASK_MODIFY', $user);
2676
                if ($result < 0) {
2677
                    $error++;
2678
                }
2679
                // End call triggers
2680
            }
2681
2682
            if (!$error) {
2683
                // We finally remove the old task
2684
                if ($task_origin->delete($user) < 1) {
2685
                    $this->error = $task_origin->error;
2686
                    $this->errors = $task_origin->errors;
2687
                    $error++;
2688
                }
2689
            }
2690
2691
            if (!$error) {
2692
                $this->db->commit();
2693
                return 0;
2694
            } else {
2695
                $langs->load("errors");
2696
                $this->error = $langs->trans('ErrorsTaskMerge');
2697
                $this->db->rollback();
2698
                return -1;
2699
            }
2700
        }
2701
2702
        return -1;
2703
    }
2704
}
2705