Passed
Push — main ( f1540e...02d90d )
by Rafael
45:15
created

Task::generateDocument()   A

Complexity

Conditions 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nop 5
dl 0
loc 19
rs 9.9332
c 0
b 0
f 0
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 DoliModules\Project\Model;
28
29
/**
30
 *      \file       htdocs/projet/class/task.class.php
31
 *      \ingroup    project
32
 *      \brief      This file is a CRUD class file for Task (Create/Read/Update/Delete)
33
 */
34
35
use DoliCore\Base\GenericDocumentLine;
36
use DoliCore\Model\WorkboardResponse;
37
38
/**
39
 *  Class to manage tasks
40
 */
41
class Task extends GenericDocumentLine
42
{
43
    /**
44
     * Draft status
45
     */
46
    const STATUS_DRAFT = 0;
47
    /**
48
     * Validated status (To do). Note: We also have the field progress to know the progression from 0 to 100%.
49
     */
50
    const STATUS_VALIDATED = 1;
51
    /**
52
     * Finished status
53
     */
54
    const STATUS_CLOSED = 3;
55
    /**
56
     * Transferred status
57
     */
58
    const STATUS_TRANSFERRED = 4;
59
    /**
60
     * status canceled
61
     */
62
    const STATUS_CANCELED = 9;
63
    /**
64
     * @var string ID to identify managed object
65
     */
66
    public $element = 'project_task';
67
    /**
68
     * @var string  Name of table without prefix where object is stored
69
     */
70
    public $table_element = 'projet_task';
71
    /**
72
     * @var string Field with ID of parent key if this field has a parent
73
     */
74
    public $fk_element = 'fk_element';
75
    /**
76
     * @var string String with name of icon for myobject.
77
     */
78
    public $picto = 'projecttask'; // total of time spent on this task
79
    /**
80
     * @var int ID parent task
81
     */
82
    public $fk_task_parent = 0;
83
    /**
84
     * @var string Label of task
85
     */
86
    public $label;
87
    /**
88
     * @var string description
89
     */
90
    public $description;
91
public $duration_effective;
92
    public $planned_workload;
93
    public $date_c;
94
    public $progress;
95
    /**
96
     * @deprecated Use date_start instead
97
     */
98
    public $dateo;
99
    public $date_start;
100
    /**
101
     * @deprecated Use date_end instead
102
     */
103
    public $datee;
104
    public $date_end;
105
    /**
106
     * @var int ID
107
     * @deprecated use status instead
108
     */
109
    public $fk_statut;
110
    /**
111
     * @var int ID
112
     */
113
    public $status;
114
    public $priority;
115
    /**
116
     * @var int ID
117
     */
118
    public $fk_user_creat;
119
    /**
120
     * @var int ID
121
     */
122
    public $fk_user_valid;
123
    public $rang;
124
    public $timespent_min_date;
125
    public $timespent_max_date;
126
    // For detail of lines of timespent record, there is the property ->lines in common
127
128
    // Var used to call method addTimeSpent(). Bad practice.
129
    public $timespent_total_duration;
130
    public $timespent_total_amount;
131
    public $timespent_nblinesnull;
132
    public $timespent_nblines;
133
        public $timespent_id; // More accurate start date (same than timespent_date but includes hours, minutes and seconds)
134
        public $timespent_duration; // 1 = we entered also start hours for timesheet line
135
    public $timespent_old_duration;
136
    public $timespent_date;
137
public $timespent_datehour;
138
public $timespent_withhour;
139
    public $timespent_fk_user;
140
    public $timespent_thm;
141
    public $timespent_note;
142
    public $timespent_fk_product;
143
    public $timespent_invoiceid;
144
145
    // Properties calculated from sum of llx_element_time linked to task
146
    public $timespent_invoicelineid;
147
    public $comments = [];
148
149
    // Properties to store project information
150
    /**
151
     * @var array
152
     */
153
    public $labelStatus;
154
    /**
155
     * @var array
156
     */
157
    public $labelStatusShort;
158
    public $tobill;
159
    public $billed;
160
    public $projectref;
161
    public $projectstatus;
162
    public $projectlabel;
163
    public $opp_amount;
164
    public $opp_percent;
165
166
    // Properties to store thirdparty of project information
167
    public $fk_opp_status;
168
    public $usage_bill_time;
169
    public $public;
170
    public $array_options_project;
171
172
    // store parent ref and position
173
    public $socid;
174
    public $thirdparty_id;
175
    public $thirdparty_name;
176
    public $thirdparty_email;
177
    public $task_parent_ref;
178
    public $task_parent_position;
179
    /**
180
     * @var float budget_amount
181
     */
182
    public $budget_amount;
183
    /**
184
     * @var float project_budget_amount
185
     */
186
    public $project_budget_amount;
187
    /**
188
     * @var array<string, array<string>>    List of child tables. To test if we can delete object.
189
     */
190
    protected $childtables = [
191
        'element_time' => ['name' => 'Task', 'parent' => 'projet_task', 'parentkey' => 'fk_element', 'parenttypefield' => 'elementtype', 'parenttypevalue' => 'task'],
192
    ];
193
194
    /**
195
     *  Constructor
196
     *
197
     * @param DoliDB $db Database handler
0 ignored issues
show
Bug introduced by
The type DoliModules\Project\Model\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
198
     */
199
    public function __construct($db)
200
    {
201
        $this->db = $db;
202
    }
203
204
    /**
205
     *  Return nb of time spent
206
     *
207
     * @return int     Return integer <0 if KO, 0 if no children, >0 if OK
208
     */
209
    public function hasTimeSpent()
210
    {
211
        $error = 0;
212
        $ret = 0;
213
214
        $sql = "SELECT COUNT(*) as nb";
215
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time";
216
        $sql .= " WHERE fk_element = " . ((int) $this->id);
217
        $sql .= " AND elementtype = 'task'";
218
219
        dol_syslog(get_class($this) . "::hasTimeSpent", LOG_DEBUG);
220
        $resql = $this->db->query($sql);
221
        if (!$resql) {
222
            $error++;
223
            $this->errors[] = "Error " . $this->db->lasterror();
224
        } else {
225
            $obj = $this->db->fetch_object($resql);
226
            if ($obj) {
227
                $ret = $obj->nb;
228
            }
229
            $this->db->free($resql);
230
        }
231
232
        if (!$error) {
233
            return $ret;
234
        } else {
235
            return -1;
236
        }
237
    }
238
239
    /**
240
     *  Initialise an instance with random values.
241
     *  Used to build previews or test instances.
242
     *  id must be 0 if object instance is a specimen.
243
     *
244
     * @return int
245
     */
246
    public function initAsSpecimen()
247
    {
248
        $this->id = 0;
249
250
        $this->fk_project = 0;
251
        $this->ref = 'TK01';
252
        $this->fk_task_parent = 0;
253
        $this->label = 'Specimen task TK01';
254
        $this->duration_effective = '';
255
        $this->fk_user_creat = 1;
256
        $this->progress = '25';
257
        $this->status = 0;
258
        $this->note = 'This is a specimen task not';
259
260
        return 1;
261
    }
262
263
    /**
264
     * Return list of tasks for all projects or for one particular project
265
     * Sort order is on project, then on position of task, and last on start date of first level task
266
     *
267
     * @param User           $usert                Object user to limit tasks affected to a particular user
268
     * @param User           $userp                Object user to limit projects of a particular user and public
269
     *                                             projects
270
     * @param int            $projectid            Project id
271
     * @param int            $socid                Third party id
272
     * @param int            $mode                 0=Return list of tasks and their projects, 1=Return projects and
273
     *                                             tasks if exists
274
     * @param string         $filteronproj         Filter on project ref or label
275
     * @param string         $filteronprojstatus   Filter on project status ('-1'=no filter, '0,1'=Draft+Validated
276
     *                                             only)
277
     * @param string         $morewherefilter      Add more filter into where SQL request (must start with ' AND ...')
278
     * @param int            $filteronprojuser     Filter on user that is a contact of project
279
     * @param int            $filterontaskuser     Filter on user assigned to task
280
     * @param   ?Extrafields $extrafields          Show additional column from project or task
281
     * @param int            $includebilltime      Calculate also the time to bill and billed
282
     * @param array          $search_array_options Array of search filters. Not Used yet.
283
     * @param int            $loadextras           Fetch all Extrafields on each project and task
284
     * @param int            $loadRoleMode         1= will test Roles on task;  0 used in delete project action
285
     * @param string         $sortfield            Sort field
286
     * @param string         $sortorder            Sort order
287
     *
288
     * @return  array|string                    Array of tasks
289
     */
290
    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 = [], $loadextras = 0, $loadRoleMode = 1, $sortfield = '', $sortorder = '')
291
    {
292
        global $hookmanager;
293
294
        $tasks = [];
295
296
        //print $usert.'-'.$userp.'-'.$projectid.'-'.$socid.'-'.$mode.'<br>';
297
298
        // List of tasks (does not care about permissions. Filtering will be done later)
299
        $sql = "SELECT ";
300
        if ($filteronprojuser > 0 || $filterontaskuser > 0) {
301
            $sql .= " DISTINCT"; // We may get several time the same record if user has several roles on same project/task
302
        }
303
        $sql .= " p.rowid as projectid, p.ref, p.title as plabel, p.public, p.fk_statut as projectstatus, p.usage_bill_time,";
304
        $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,";
305
        $sql .= " t.dateo as date_start, t.datee as date_end, t.planned_workload, t.rang,";
306
        $sql .= " t.description, ";
307
        $sql .= " t.budget_amount, ";
308
        $sql .= " s.rowid as thirdparty_id, s.nom as thirdparty_name, s.email as thirdparty_email,";
309
        $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount as project_budget_amount";
310
        if ($loadextras) {  // TODO Replace this with a fetch_optionnal() on the project after the fetch_object of line.
311
            if (!empty($extrafields->attributes['projet']['label'])) {
312
                foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
313
                    $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp." . $key . " as options_" . $key : '');
314
                }
315
            }
316
            if (!empty($extrafields->attributes['projet_task']['label'])) {
317
                foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
318
                    $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt." . $key . " as options_" . $key : '');
319
                }
320
            }
321
        }
322
        if ($includebilltime) {
323
            $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";
324
        }
325
326
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
327
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
328
        if ($loadextras) {
329
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_extrafields as efp ON (p.rowid = efp.fk_object)";
330
        }
331
332
        if ($mode == 0) {
333
            if ($filteronprojuser > 0) {
334
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec";
335
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc";
336
            }
337
            $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
338
            if ($loadextras) {
339
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task_extrafields as efpt ON (t.rowid = efpt.fk_object)";
340
            }
341
            if ($includebilltime) {
342
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
343
            }
344
            if ($filterontaskuser > 0) {
345
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec2";
346
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc2";
347
            }
348
            $sql .= " WHERE p.entity IN (" . getEntity('project') . ")";
349
            $sql .= " AND t.fk_projet = p.rowid";
350
        } elseif ($mode == 1) {
351
            if ($filteronprojuser > 0) {
352
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec";
353
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc";
354
            }
355
            if ($filterontaskuser > 0) {
356
                $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
357
                if ($includebilltime) {
358
                    $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype='task')";
359
                }
360
                $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec2";
361
                $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc2";
362
            } else {
363
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task as t on t.fk_projet = p.rowid";
364
                if ($includebilltime) {
365
                    $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_time as tt ON (tt.fk_element = t.rowid AND tt.elementtype = 'task')";
366
                }
367
            }
368
            $sql .= " WHERE p.entity IN (" . getEntity('project') . ")";
369
        } else {
370
            return 'BadValueForParameterMode';
371
        }
372
373
        if ($filteronprojuser > 0) {
374
            $sql .= " AND p.rowid = ec.element_id";
375
            $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
376
            $sql .= " AND ctc.element = 'project'";
377
            $sql .= " AND ec.fk_socpeople = " . ((int) $filteronprojuser);
378
            $sql .= " AND ec.statut = 4";
379
            $sql .= " AND ctc.source = 'internal'";
380
        }
381
        if ($filterontaskuser > 0) {
382
            $sql .= " AND t.fk_projet = p.rowid";
383
            $sql .= " AND p.rowid = ec2.element_id";
384
            $sql .= " AND ctc2.rowid = ec2.fk_c_type_contact";
385
            $sql .= " AND ctc2.element = 'project_task'";
386
            $sql .= " AND ec2.fk_socpeople = " . ((int) $filterontaskuser);
387
            $sql .= " AND ec2.statut = 4";
388
            $sql .= " AND ctc2.source = 'internal'";
389
        }
390
        if ($socid) {
391
            $sql .= " AND p.fk_soc = " . ((int) $socid);
392
        }
393
        if ($projectid) {
394
            $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectid) . ")";
395
        }
396
        if ($filteronproj) {
397
            $sql .= natural_search(["p.ref", "p.title"], $filteronproj);
398
        }
399
        if ($filteronprojstatus && (int) $filteronprojstatus != '-1') {
400
            $sql .= " AND p.fk_statut IN (" . $this->db->sanitize($filteronprojstatus) . ")";
401
        }
402
        if ($morewherefilter) {
403
            $sql .= $morewherefilter;
404
        }
405
406
        // Add where from extra fields
407
        $extrafieldsobjectkey = 'projet_task';
408
        $extrafieldsobjectprefix = 'efpt.';
409
        global $db, $conf; // needed for extrafields_list_search_sql.tpl
410
        include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_sql.tpl.php';
411
412
        // Add where from hooks
413
        $parameters = [];
414
        $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
415
        $sql .= $hookmanager->resPrint;
416
        if ($includebilltime) {
417
            $sql .= " GROUP BY p.rowid, p.ref, p.title, p.public, p.fk_statut, p.usage_bill_time,";
418
            $sql .= " t.datec, t.dateo, t.datee, t.tms,";
419
            $sql .= " t.rowid, t.ref, t.label, t.description, t.fk_task_parent, t.duration_effective, t.progress, t.fk_statut,";
420
            $sql .= " t.dateo, t.datee, t.planned_workload, t.rang,";
421
            $sql .= " t.description, ";
422
            $sql .= " t.budget_amount, ";
423
            $sql .= " s.rowid, s.nom, s.email,";
424
            $sql .= " p.fk_opp_status, p.opp_amount, p.opp_percent, p.budget_amount";
425
            if ($loadextras) {
426
                if (!empty($extrafields->attributes['projet']['label'])) {
427
                    foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
428
                        $sql .= ($extrafields->attributes['projet']['type'][$key] != 'separate' ? ",efp." . $key : '');
429
                    }
430
                }
431
                if (!empty($extrafields->attributes['projet_task']['label'])) {
432
                    foreach ($extrafields->attributes['projet_task']['label'] as $key => $val) {
433
                        $sql .= ($extrafields->attributes['projet_task']['type'][$key] != 'separate' ? ",efpt." . $key : '');
434
                    }
435
                }
436
            }
437
        }
438
439
        if ($sortfield && $sortorder) {
440
            $sql .= $this->db->order($sortfield, $sortorder);
441
        } else {
442
            $sql .= " ORDER BY p.ref, t.rang, t.dateo";
443
        }
444
445
        //print $sql;exit;
446
        dol_syslog(get_class($this) . "::getTasksArray", LOG_DEBUG);
447
        $resql = $this->db->query($sql);
448
        if ($resql) {
449
            $num = $this->db->num_rows($resql);
450
            $i = 0;
451
            // Loop on each record found, so each couple (project id, task id)
452
            while ($i < $num) {
453
                $error = 0;
454
455
                $obj = $this->db->fetch_object($resql);
456
457
                if ($loadRoleMode) {
458
                    if ((!$obj->public) && (is_object($userp))) {    // If not public project and we ask a filter on project owned by a user
459
                        if (!$this->getUserRolesForProjectsOrTasks($userp, null, $obj->projectid, 0)) {
460
                            $error++;
461
                        }
462
                    }
463
                    if (is_object($usert)) {                            // If we ask a filter on a user affected to a task
464
                        if (!$this->getUserRolesForProjectsOrTasks(null, $usert, $obj->projectid, $obj->taskid)) {
465
                            $error++;
466
                        }
467
                    }
468
                }
469
470
                if (!$error) {
471
                    $tasks[$i] = new Task($this->db);
472
                    $tasks[$i]->id = $obj->taskid;
473
                    $tasks[$i]->ref = $obj->taskref;
474
                    $tasks[$i]->fk_project = $obj->projectid;
475
476
                    // Data from project
477
                    $tasks[$i]->projectref = $obj->ref;
478
                    $tasks[$i]->projectlabel = $obj->plabel;
479
                    $tasks[$i]->projectstatus = $obj->projectstatus;
480
                    $tasks[$i]->fk_opp_status = $obj->fk_opp_status;
481
                    $tasks[$i]->opp_amount = $obj->opp_amount;
482
                    $tasks[$i]->opp_percent = $obj->opp_percent;
483
                    $tasks[$i]->budget_amount = $obj->budget_amount;
484
                    $tasks[$i]->project_budget_amount = $obj->project_budget_amount;
485
                    $tasks[$i]->usage_bill_time = $obj->usage_bill_time;
486
487
                    $tasks[$i]->label = $obj->label;
488
                    $tasks[$i]->description = $obj->description;
489
                    $tasks[$i]->fk_task_parent = $obj->fk_task_parent;
490
                    $tasks[$i]->duration_effective = $obj->duration_effective;
491
                    $tasks[$i]->planned_workload = $obj->planned_workload;
492
493
                    if ($includebilltime) {
494
                        // Data summed from element_time linked to task
495
                        $tasks[$i]->tobill = $obj->tobill;
496
                        $tasks[$i]->billed = $obj->billed;
497
                    }
498
499
                    $tasks[$i]->progress = $obj->progress;
500
                    $tasks[$i]->fk_statut = $obj->status;
501
                    $tasks[$i]->status = $obj->status;
502
                    $tasks[$i]->public = $obj->public;
503
                    $tasks[$i]->date_start = $this->db->jdate($obj->date_start);
504
                    $tasks[$i]->date_end = $this->db->jdate($obj->date_end);
505
                    $tasks[$i]->rang = $obj->rang;
506
507
                    $tasks[$i]->socid = $obj->thirdparty_id; // For backward compatibility
508
                    $tasks[$i]->thirdparty_id = $obj->thirdparty_id;
509
                    $tasks[$i]->thirdparty_name = $obj->thirdparty_name;
510
                    $tasks[$i]->thirdparty_email = $obj->thirdparty_email;
511
512
                    if ($loadextras) {
513
                        if (!empty($extrafields->attributes['projet']['label'])) {
514
                            foreach ($extrafields->attributes['projet']['label'] as $key => $val) {
515
                                if ($extrafields->attributes['projet']['type'][$key] != 'separate') {
516
                                    $tmpvar = 'options_' . $key;
517
                                    $tasks[$i]->array_options_project['options_' . $key] = $obj->$tmpvar;
518
                                }
519
                            }
520
                        }
521
                    }
522
523
                    if ($loadextras) {
524
                        $tasks[$i]->fetch_optionals();
525
                    }
526
                }
527
528
                $i++;
529
            }
530
            $this->db->free($resql);
531
        } else {
532
            dol_print_error($this->db);
533
        }
534
535
        return $tasks;
536
    }
537
538
    /**
539
     * Return list of roles for a user for each projects or each tasks (or a particular project or a particular task).
540
     *
541
     * @param User|null $userp              Return roles on project for this internal user. If set, usert and taskid
542
     *                                      must not be defined.
543
     * @param User|null $usert              Return roles on task for this internal user. If set userp must NOT be
544
     *                                      defined. -1 means no filter.
545
     * @param string    $projectid          Project id list separated with , to filter on project
546
     * @param int       $taskid             Task id to filter on a task
547
     * @param integer   $filteronprojstatus Filter on project status if userp is set. Not used if userp not defined.
548
     *
549
     * @return  array|int                         Array (projectid => 'list of roles for project' or taskid => 'list of
550
     *                                            roles for task')
551
     */
552
    public function getUserRolesForProjectsOrTasks($userp, $usert, $projectid = '', $taskid = 0, $filteronprojstatus = -1)
553
    {
554
        $arrayroles = [];
555
556
        dol_syslog(get_class($this) . "::getUserRolesForProjectsOrTasks userp=" . is_object($userp) . " usert=" . is_object($usert) . " projectid=" . $projectid . " taskid=" . $taskid);
557
558
        // We want role of user for a projet or role of user for a task. Both are not possible.
559
        if (empty($userp) && empty($usert)) {
560
            $this->error = "CallWithWrongParameters";
561
            return -1;
562
        }
563
        if (!empty($userp) && !empty($usert)) {
564
            $this->error = "CallWithWrongParameters";
565
            return -1;
566
        }
567
568
        /* Liste des taches et role sur les projects ou taches */
569
        $sql = "SELECT ";
570
        if ($userp) {
571
            $sql .= " p.rowid as pid,";
572
        } else {
573
            $sql .= " pt.rowid as pid,";
574
        }
575
        $sql .= " ec.element_id, ctc.code, ctc.source";
576
        if ($userp) {
577
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
578
        }
579
        if ($usert && $filteronprojstatus > -1) {
580
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p, " . MAIN_DB_PREFIX . "projet_task as pt";
581
        }
582
        if ($usert && $filteronprojstatus <= -1) {
583
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet_task as pt";
584
        }
585
        $sql .= ", " . MAIN_DB_PREFIX . "element_contact as ec";
586
        $sql .= ", " . MAIN_DB_PREFIX . "c_type_contact as ctc";
587
        if ($userp) {
588
            $sql .= " WHERE p.rowid = ec.element_id";
589
        } else {
590
            $sql .= " WHERE pt.rowid = ec.element_id";
591
        }
592
        if ($userp && $filteronprojstatus > -1) {
593
            $sql .= " AND p.fk_statut = " . ((int) $filteronprojstatus);
594
        }
595
        if ($usert && $filteronprojstatus > -1) {
596
            $sql .= " AND pt.fk_projet = p.rowid AND p.fk_statut = " . ((int) $filteronprojstatus);
597
        }
598
        if ($userp) {
599
            $sql .= " AND ctc.element = 'project'";
600
        }
601
        if ($usert) {
602
            $sql .= " AND ctc.element = 'project_task'";
603
        }
604
        $sql .= " AND ctc.rowid = ec.fk_c_type_contact";
605
        if ($userp) {
606
            $sql .= " AND ec.fk_socpeople = " . ((int) $userp->id);
607
        }
608
        if ($usert) {
609
            $sql .= " AND ec.fk_socpeople = " . ((int) $usert->id);
610
        }
611
        $sql .= " AND ec.statut = 4";
612
        $sql .= " AND ctc.source = 'internal'";
613
        if ($projectid) {
614
            if ($userp) {
615
                $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectid) . ")";
616
            }
617
            if ($usert) {
618
                $sql .= " AND pt.fk_projet IN (" . $this->db->sanitize($projectid) . ")";
619
            }
620
        }
621
        if ($taskid) {
622
            if ($userp) {
623
                $sql .= " ERROR SHOULD NOT HAPPENS";
624
            }
625
            if ($usert) {
626
                $sql .= " AND pt.rowid = " . ((int) $taskid);
627
            }
628
        }
629
        //print $sql;
630
631
        dol_syslog(get_class($this) . "::getUserRolesForProjectsOrTasks execute request", LOG_DEBUG);
632
        $resql = $this->db->query($sql);
633
        if ($resql) {
634
            $num = $this->db->num_rows($resql);
635
            $i = 0;
636
            while ($i < $num) {
637
                $obj = $this->db->fetch_object($resql);
638
                if (empty($arrayroles[$obj->pid])) {
639
                    $arrayroles[$obj->pid] = $obj->code;
640
                } else {
641
                    $arrayroles[$obj->pid] .= ',' . $obj->code;
642
                }
643
                $i++;
644
            }
645
            $this->db->free($resql);
646
        } else {
647
            dol_print_error($this->db);
648
        }
649
650
        return $arrayroles;
651
    }
652
653
    /**
654
     *  Return list of id of contacts of task
655
     *
656
     * @param string $source Source
657
     *
658
     * @return array               Array of id of contacts
659
     */
660
    public function getListContactId($source = 'internal')
661
    {
662
        $contactAlreadySelected = [];
663
        $tab = $this->liste_contact(-1, $source);
664
        //var_dump($tab);
665
        $num = count($tab);
666
        $i = 0;
667
        while ($i < $num) {
668
            if ($source == 'thirdparty') {
669
                $contactAlreadySelected[$i] = $tab[$i]['socid'];
670
            } else {
671
                $contactAlreadySelected[$i] = $tab[$i]['id'];
672
            }
673
            $i++;
674
        }
675
        return $contactAlreadySelected;
676
    }
677
678
    /**
679
     *  Add time spent
680
     *
681
     * @param User $user      User object
682
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
683
     *
684
     * @return int                     Return integer <=0 if KO, >0 if OK
685
     */
686
    public function addTimeSpent($user, $notrigger = 0)
687
    {
688
        global $conf, $langs;
689
690
        dol_syslog(get_class($this) . "::addTimeSpent", LOG_DEBUG);
691
692
        $ret = 0;
693
        $now = dol_now();
694
695
        // Check parameters
696
        if (!is_object($user)) {
697
            dol_print_error(null, "Method addTimeSpent was called with wrong parameter user");
698
            return -1;
699
        }
700
701
        // Clean parameters
702
        if (isset($this->timespent_note)) {
703
            $this->timespent_note = trim($this->timespent_note);
704
        }
705
        if (empty($this->timespent_datehour) || ($this->timespent_date != $this->timespent_datehour)) {
706
            $this->timespent_datehour = $this->timespent_date;
707
        }
708
709
        if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
710
            require_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
711
            $restrictBefore = dol_time_plus_duree(dol_now(), -$conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
712
713
            if ($this->timespent_date < $restrictBefore) {
714
                $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
715
                $this->errors[] = $this->error;
716
                return -1;
717
            }
718
        }
719
720
721
        $this->db->begin();
722
723
        $timespent = new TimeSpent($this->db);
0 ignored issues
show
Bug introduced by
The type DoliModules\Project\Model\TimeSpent was not found. Did you mean TimeSpent? If so, make sure to prefix the type with \.
Loading history...
724
        $timespent->fk_element = $this->id;
725
        $timespent->elementtype = 'task';
726
        $timespent->element_date = $this->timespent_date;
727
        $timespent->element_datehour = $this->timespent_datehour;
728
        $timespent->element_date_withhour = $this->timespent_withhour;
729
        $timespent->element_duration = $this->timespent_duration;
730
        $timespent->fk_user = $this->timespent_fk_user;
731
        $timespent->fk_product = $this->timespent_fk_product;
732
        $timespent->note = $this->timespent_note;
733
        $timespent->datec = $this->db->idate($now);
734
735
        $result = $timespent->create($user);
736
737
        if ($result > 0) {
738
            $ret = $result;
739
            $this->timespent_id = $result;
740
741
            if (!$notrigger) {
742
                // Call trigger
743
                $result = $this->call_trigger('TASK_TIMESPENT_CREATE', $user);
744
                if ($result < 0) {
745
                    $ret = -1;
746
                }
747
                // End call triggers
748
            }
749
        } else {
750
            $this->error = $this->db->lasterror();
751
            $ret = -1;
752
        }
753
754
        if ($ret > 0) {
755
            // Recalculate amount of time spent for task and update denormalized field
756
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
757
            $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) . ")";
758
            if (isset($this->progress)) {
759
                $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
760
            }
761
            $sql .= " WHERE rowid = " . ((int) $this->id);
762
763
            dol_syslog(get_class($this) . "::addTimeSpent", LOG_DEBUG);
764
            if (!$this->db->query($sql)) {
765
                $this->error = $this->db->lasterror();
766
                $ret = -2;
767
            }
768
769
            // Update hourly rate of this time spent entry
770
            $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
771
            if (!empty($resql_thm_user)) {
772
                $obj_thm_user = $this->db->fetch_object($resql_thm_user);
773
                $timespent->thm = $obj_thm_user->thm;
774
            }
775
            $res_update = $timespent->update($user);
776
777
            dol_syslog(get_class($this) . "::addTimeSpent", LOG_DEBUG);
778
            if ($res_update <= 0) {
779
                $this->error = $this->db->lasterror();
780
                $ret = -2;
781
            }
782
        }
783
784
        if ($ret > 0) {
785
            $this->db->commit();
786
        } else {
787
            $this->db->rollback();
788
        }
789
        return $ret;
790
    }
791
792
    /**
793
     *  Create into database
794
     *
795
     * @param User $user      User that create
796
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
797
     *
798
     * @return int                     Return integer <0 if KO, Id of created object if OK
799
     */
800
    public function create($user, $notrigger = 0)
801
    {
802
        global $conf, $langs;
803
804
        //For the date
805
        $now = dol_now();
806
807
        $error = 0;
808
809
        // Clean parameters
810
        $this->label = trim($this->label);
811
        $this->description = trim($this->description);
812
813
        if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
814
            $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
815
            return -1;
816
        }
817
818
        // Insert request
819
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet_task (";
820
        $sql .= "entity";
821
        $sql .= ", fk_projet";
822
        $sql .= ", ref";
823
        $sql .= ", fk_task_parent";
824
        $sql .= ", label";
825
        $sql .= ", description";
826
        $sql .= ", datec";
827
        $sql .= ", fk_user_creat";
828
        $sql .= ", dateo";
829
        $sql .= ", datee";
830
        $sql .= ", planned_workload";
831
        $sql .= ", progress";
832
        $sql .= ", budget_amount";
833
        $sql .= ") VALUES (";
834
        $sql .= (!empty($this->entity) ? (int) $this->entity : (int) $conf->entity);
835
        $sql .= ", " . ((int) $this->fk_project);
836
        $sql .= ", " . (!empty($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : 'null');
837
        $sql .= ", " . ((int) $this->fk_task_parent);
838
        $sql .= ", '" . $this->db->escape($this->label) . "'";
839
        $sql .= ", '" . $this->db->escape($this->description) . "'";
840
        $sql .= ", '" . $this->db->idate($now) . "'";
841
        $sql .= ", " . ((int) $user->id);
842
        $sql .= ", " . ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : 'null');
843
        $sql .= ", " . ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : 'null');
844
        $sql .= ", " . (($this->planned_workload != '' && $this->planned_workload >= 0) ? ((int) $this->planned_workload) : 'null');
845
        $sql .= ", " . (($this->progress != '' && $this->progress >= 0) ? ((int) $this->progress) : 'null');
846
        $sql .= ", " . (($this->budget_amount != '' && $this->budget_amount >= 0) ? ((int) $this->budget_amount) : 'null');
847
        $sql .= ")";
848
849
        $this->db->begin();
850
851
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
852
        $resql = $this->db->query($sql);
853
        if (!$resql) {
854
            $error++;
855
            $this->errors[] = "Error " . $this->db->lasterror();
856
        }
857
858
        if (!$error) {
859
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "projet_task");
860
861
            if (!$notrigger) {
862
                // Call trigger
863
                $result = $this->call_trigger('TASK_CREATE', $user);
864
                if ($result < 0) {
865
                    $error++;
866
                }
867
                // End call triggers
868
            }
869
        }
870
871
        // Update extrafield
872
        if (!$error) {
873
            if (!$error) {
874
                $result = $this->insertExtraFields();
875
                if ($result < 0) {
876
                    $error++;
877
                }
878
            }
879
        }
880
881
        // Commit or rollback
882
        if ($error) {
883
            foreach ($this->errors as $errmsg) {
884
                dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR);
885
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
886
            }
887
            $this->db->rollback();
888
            return -1 * $error;
889
        } else {
890
            $this->db->commit();
891
            return $this->id;
892
        }
893
    }
894
895
    /**
896
     *  Update database
897
     *
898
     * @param User $user      User that modify
899
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
900
     *
901
     * @return int                     Return integer <=0 if KO, >0 if OK
902
     */
903
    public function update($user = null, $notrigger = 0)
904
    {
905
        global $conf, $langs;
906
        $error = 0;
907
908
        // Clean parameters
909
        if (isset($this->fk_project)) {
910
            $this->fk_project = (int) $this->fk_project;
911
        }
912
        if (isset($this->ref)) {
913
            $this->ref = trim($this->ref);
914
        }
915
        if (isset($this->fk_task_parent)) {
916
            $this->fk_task_parent = (int) $this->fk_task_parent;
917
        }
918
        if (isset($this->label)) {
919
            $this->label = trim($this->label);
920
        }
921
        if (isset($this->description)) {
922
            $this->description = trim($this->description);
923
        }
924
        if (isset($this->duration_effective)) {
925
            $this->duration_effective = trim($this->duration_effective);
926
        }
927
        if (isset($this->planned_workload)) {
928
            $this->planned_workload = trim($this->planned_workload);
929
        }
930
        if (isset($this->budget_amount)) {
931
            $this->budget_amount = (float) $this->budget_amount;
932
        }
933
934
        if (!empty($this->date_start) && !empty($this->date_end) && $this->date_start > $this->date_end) {
935
            $this->errors[] = $langs->trans('StartDateCannotBeAfterEndDate');
936
            return -1;
937
        }
938
939
        // Check parameters
940
        // Put here code to add control on parameters values
941
942
        // Update request
943
        $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task SET";
944
        $sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ",";
945
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "'" . $this->db->escape($this->id) . "'") . ",";
946
        $sql .= " fk_task_parent=" . (isset($this->fk_task_parent) ? $this->fk_task_parent : "null") . ",";
947
        $sql .= " label=" . (isset($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
948
        $sql .= " description=" . (isset($this->description) ? "'" . $this->db->escape($this->description) . "'" : "null") . ",";
949
        $sql .= " duration_effective=" . (isset($this->duration_effective) ? $this->duration_effective : "null") . ",";
950
        $sql .= " planned_workload=" . ((isset($this->planned_workload) && $this->planned_workload != '') ? $this->planned_workload : "null") . ",";
951
        $sql .= " dateo=" . ($this->date_start != '' ? "'" . $this->db->idate($this->date_start) . "'" : 'null') . ",";
952
        $sql .= " datee=" . ($this->date_end != '' ? "'" . $this->db->idate($this->date_end) . "'" : 'null') . ",";
953
        $sql .= " progress=" . (($this->progress != '' && $this->progress >= 0) ? $this->progress : 'null') . ",";
954
        $sql .= " budget_amount=" . (($this->budget_amount != '' && $this->budget_amount >= 0) ? $this->budget_amount : 'null') . ",";
955
        $sql .= " rang=" . ((!empty($this->rang)) ? $this->rang : "0");
956
        $sql .= " WHERE rowid=" . ((int) $this->id);
957
958
        $this->db->begin();
959
960
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
961
        $resql = $this->db->query($sql);
962
        if (!$resql) {
963
            $error++;
964
            $this->errors[] = "Error " . $this->db->lasterror();
965
        }
966
967
        // Update extrafield
968
        if (!$error) {
969
            $result = $this->insertExtraFields();
970
            if ($result < 0) {
971
                $error++;
972
            }
973
        }
974
975
        if (!$error && getDolGlobalString('PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE')) {
976
            // Close the parent project if it is open (validated) and its tasks are 100% completed
977
            $project = new Project($this->db);
978
            if ($project->fetch($this->fk_project) > 0) {
979
                if ($project->statut == Project::STATUS_VALIDATED) {
980
                    $project->getLinesArray(null); // this method does not return <= 0 if fails
981
                    $projectCompleted = array_reduce(
982
                        $project->lines,
983
                        /**
984
                         * @param bool $allTasksCompleted
985
                         * @param Task $task
986
                         *
987
                         * @return bool
988
                         */
989
                        static function ($allTasksCompleted, $task) {
990
                            return $allTasksCompleted && $task->progress >= 100;
991
                        },
992
                        1
993
                    );
994
                    if ($projectCompleted) {
995
                        if ($project->setClose($user) <= 0) {
996
                            $error++;
997
                        }
998
                    }
999
                }
1000
            } else {
1001
                $error++;
1002
            }
1003
            if ($error) {
1004
                $this->errors[] = $project->error;
1005
            }
1006
        }
1007
1008
        if (!$error) {
1009
            if (!$notrigger) {
1010
                // Call trigger
1011
                $result = $this->call_trigger('TASK_MODIFY', $user);
1012
                if ($result < 0) {
1013
                    $error++;
1014
                }
1015
                // End call triggers
1016
            }
1017
        }
1018
1019
        if (!$error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) {
1020
            // We remove directory
1021
            if ($conf->project->dir_output) {
1022
                $project = new Project($this->db);
1023
                $project->fetch($this->fk_project);
1024
1025
                $olddir = $conf->project->dir_output . '/' . dol_sanitizeFileName($project->ref) . '/' . dol_sanitizeFileName($this->oldcopy->ref);
1026
                $newdir = $conf->project->dir_output . '/' . dol_sanitizeFileName($project->ref) . '/' . dol_sanitizeFileName($this->ref);
1027
                if (file_exists($olddir)) {
1028
                    include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1029
                    $res = dol_move_dir($olddir, $newdir);
1030
                    if (!$res) {
1031
                        $langs->load("errors");
1032
                        $this->error = $langs->trans('ErrorFailToRenameDir', $olddir, $newdir);
1033
                        $error++;
1034
                    }
1035
                }
1036
            }
1037
        }
1038
1039
        // Commit or rollback
1040
        if ($error) {
1041
            foreach ($this->errors as $errmsg) {
1042
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1043
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1044
            }
1045
            $this->db->rollback();
1046
            return -1 * $error;
1047
        } else {
1048
            $this->db->commit();
1049
            return 1;
1050
        }
1051
    }
1052
1053
    /**
1054
     *  Load object in memory from database
1055
     *
1056
     * @param int    $id             Id object
1057
     * @param string $ref            ref object
1058
     * @param int    $loadparentdata Also load parent data
1059
     *
1060
     * @return int                         Return integer <0 if KO, 0 if not found, >0 if OK
1061
     */
1062
    public function fetch($id, $ref = '', $loadparentdata = 0)
1063
    {
1064
        $sql = "SELECT";
1065
        $sql .= " t.rowid,";
1066
        $sql .= " t.ref,";
1067
        $sql .= " t.entity,";
1068
        $sql .= " t.fk_projet as fk_project,";
1069
        $sql .= " t.fk_task_parent,";
1070
        $sql .= " t.label,";
1071
        $sql .= " t.description,";
1072
        $sql .= " t.duration_effective,";
1073
        $sql .= " t.planned_workload,";
1074
        $sql .= " t.datec,";
1075
        $sql .= " t.dateo as date_start,";
1076
        $sql .= " t.datee as date_end,";
1077
        $sql .= " t.fk_user_creat,";
1078
        $sql .= " t.fk_user_valid,";
1079
        $sql .= " t.fk_statut as status,";
1080
        $sql .= " t.progress,";
1081
        $sql .= " t.budget_amount,";
1082
        $sql .= " t.priority,";
1083
        $sql .= " t.note_private,";
1084
        $sql .= " t.note_public,";
1085
        $sql .= " t.rang";
1086
        if (!empty($loadparentdata)) {
1087
            $sql .= ", t2.ref as task_parent_ref";
1088
            $sql .= ", t2.rang as task_parent_position";
1089
        }
1090
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet_task as t";
1091
        if (!empty($loadparentdata)) {
1092
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "projet_task as t2 ON t.fk_task_parent = t2.rowid";
1093
        }
1094
        $sql .= " WHERE ";
1095
        if (!empty($ref)) {
1096
            $sql .= "entity IN (" . getEntity('project') . ")";
1097
            $sql .= " AND t.ref = '" . $this->db->escape($ref) . "'";
1098
        } else {
1099
            $sql .= "t.rowid = " . ((int) $id);
1100
        }
1101
1102
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
1103
        $resql = $this->db->query($sql);
1104
        if ($resql) {
1105
            $num_rows = $this->db->num_rows($resql);
1106
1107
            if ($num_rows) {
1108
                $obj = $this->db->fetch_object($resql);
1109
1110
                $this->id = $obj->rowid;
1111
                $this->ref = $obj->ref;
1112
                $this->entity = $obj->entity;
1113
                $this->fk_project = $obj->fk_project;
1114
                $this->fk_task_parent = $obj->fk_task_parent;
1115
                $this->label = $obj->label;
1116
                $this->description = $obj->description;
1117
                $this->duration_effective = $obj->duration_effective;
1118
                $this->planned_workload = $obj->planned_workload;
1119
                $this->date_c = $this->db->jdate($obj->datec);
1120
                $this->date_start = $this->db->jdate($obj->date_start);
1121
                $this->date_end = $this->db->jdate($obj->date_end);
1122
                $this->fk_user_creat = $obj->fk_user_creat;
1123
                $this->fk_user_valid = $obj->fk_user_valid;
1124
                $this->fk_statut = $obj->status;
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Project\Model\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

1124
                /** @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...
1125
                $this->status = $obj->status;
1126
                $this->progress = $obj->progress;
1127
                $this->budget_amount = $obj->budget_amount;
1128
                $this->priority = $obj->priority;
1129
                $this->note_private = $obj->note_private;
1130
                $this->note_public = $obj->note_public;
1131
                $this->rang = $obj->rang;
1132
1133
                if (!empty($loadparentdata)) {
1134
                    $this->task_parent_ref = $obj->task_parent_ref;
1135
                    $this->task_parent_position = $obj->task_parent_position;
1136
                }
1137
1138
                // Retrieve all extrafield
1139
                $this->fetch_optionals();
1140
            }
1141
1142
            $this->db->free($resql);
1143
1144
            if ($num_rows) {
1145
                return 1;
1146
            } else {
1147
                return 0;
1148
            }
1149
        } else {
1150
            $this->error = "Error " . $this->db->lasterror();
1151
            return -1;
1152
        }
1153
    }
1154
1155
    /**
1156
     *  Fetch records of time spent of this task
1157
     *
1158
     * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
1159
     *
1160
     * @return int                         Return integer <0 if KO, array of time spent if OK
1161
     */
1162
    public function fetchTimeSpentOnTask($morewherefilter = '')
1163
    {
1164
        $arrayres = [];
1165
1166
        $sql = "SELECT";
1167
        $sql .= " s.rowid as socid,";
1168
        $sql .= " s.nom as thirdparty_name,";
1169
        $sql .= " s.email as thirdparty_email,";
1170
        $sql .= " ptt.rowid,";
1171
        $sql .= " ptt.ref_ext,";
1172
        $sql .= " ptt.fk_element as fk_task,";
1173
        $sql .= " ptt.element_date as task_date,";
1174
        $sql .= " ptt.element_datehour as task_datehour,";
1175
        $sql .= " ptt.element_date_withhour as task_date_withhour,";
1176
        $sql .= " ptt.element_duration as task_duration,";
1177
        $sql .= " ptt.fk_user,";
1178
        $sql .= " ptt.note,";
1179
        $sql .= " ptt.thm,";
1180
        $sql .= " pt.rowid as task_id,";
1181
        $sql .= " pt.ref as task_ref,";
1182
        $sql .= " pt.label as task_label,";
1183
        $sql .= " p.rowid as project_id,";
1184
        $sql .= " p.ref as project_ref,";
1185
        $sql .= " p.title as project_label,";
1186
        $sql .= " p.public as public";
1187
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet as p";
1188
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
1189
        $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1190
        $sql .= " AND ptt.elementtype = 'task'";
1191
        $sql .= " AND pt.rowid = " . ((int) $this->id);
1192
        $sql .= " AND pt.entity IN (" . getEntity('project') . ")";
1193
        if ($morewherefilter) {
1194
            $sql .= $morewherefilter;
1195
        }
1196
1197
        dol_syslog(get_class($this) . "::fetchAllTimeSpent", LOG_DEBUG);
1198
        $resql = $this->db->query($sql);
1199
        if ($resql) {
1200
            $num = $this->db->num_rows($resql);
1201
1202
            $i = 0;
1203
            while ($i < $num) {
1204
                $obj = $this->db->fetch_object($resql);
1205
1206
                $newobj = new stdClass();
0 ignored issues
show
Bug introduced by
The type DoliModules\Project\Model\stdClass was not found. Did you mean stdClass? If so, make sure to prefix the type with \.
Loading history...
1207
1208
                $newobj->socid = $obj->socid;
1209
                $newobj->thirdparty_name = $obj->thirdparty_name;
1210
                $newobj->thirdparty_email = $obj->thirdparty_email;
1211
1212
                $newobj->fk_project = $obj->project_id;
1213
                $newobj->project_ref = $obj->project_ref;
1214
                $newobj->project_label = $obj->project_label;
1215
                $newobj->public = $obj->project_public;
1216
1217
                $newobj->fk_task = $obj->task_id;
1218
                $newobj->task_ref = $obj->task_ref;
1219
                $newobj->task_label = $obj->task_label;
1220
1221
                $newobj->timespent_line_id = $obj->rowid;
1222
                $newobj->timespent_line_ref_ext = $obj->ref_ext;
1223
                $newobj->timespent_line_date = $this->db->jdate($obj->task_date);
1224
                $newobj->timespent_line_datehour = $this->db->jdate($obj->task_datehour);
1225
                $newobj->timespent_line_withhour = $obj->task_date_withhour;
1226
                $newobj->timespent_line_duration = $obj->task_duration;
1227
                $newobj->timespent_line_fk_user = $obj->fk_user;
1228
                $newobj->timespent_line_thm = $obj->thm;    // hourly rate
1229
                $newobj->timespent_line_note = $obj->note;
1230
1231
                $arrayres[] = $newobj;
1232
1233
                $i++;
1234
            }
1235
1236
            $this->db->free($resql);
1237
1238
            $this->lines = $arrayres;
1239
            return 1;
1240
        } else {
1241
            dol_print_error($this->db);
1242
            $this->error = "Error " . $this->db->lasterror();
1243
            return -1;
1244
        }
1245
    }
1246
1247
    /**
1248
     *  Calculate total of time spent for task
1249
     *
1250
     * @param User|int $userobj         Filter on user. null or 0=No filter
1251
     * @param string   $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
1252
     *
1253
     * @return array|int                       Array of info for task array('min_date', 'max_date', 'total_duration',
1254
     *                                         'total_amount', 'nblines', 'nblinesnull')
1255
     */
1256
    public function getSummaryOfTimeSpent($userobj = null, $morewherefilter = '')
1257
    {
1258
        if (is_object($userobj)) {
1259
            $userid = $userobj->id;
1260
        } else {
1261
            $userid = $userobj; // old method
1262
        }
1263
1264
        $id = $this->id;
1265
        if (empty($id) && empty($userid)) {
1266
            dol_syslog("getSummaryOfTimeSpent called on a not loaded task without user param defined", LOG_ERR);
1267
            return -1;
1268
        }
1269
1270
        $result = [];
1271
1272
        $sql = "SELECT";
1273
        $sql .= " MIN(t.element_datehour) as min_date,";
1274
        $sql .= " MAX(t.element_datehour) as max_date,";
1275
        $sql .= " SUM(t.element_duration) as total_duration,";
1276
        $sql .= " SUM(t.element_duration / 3600 * " . $this->db->ifsql("t.thm IS NULL", 0, "t.thm") . ") as total_amount,";
1277
        $sql .= " COUNT(t.rowid) as nblines,";
1278
        $sql .= " SUM(" . $this->db->ifsql("t.thm IS NULL", 1, 0) . ") as nblinesnull";
1279
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as t";
1280
        $sql .= " WHERE t.elementtype='task'";
1281
        if ($morewherefilter) {
1282
            $sql .= $morewherefilter;
1283
        }
1284
        if ($id > 0) {
1285
            $sql .= " AND t.fk_element = " . ((int) $id);
1286
        }
1287
        if ($userid > 0) {
1288
            $sql .= " AND t.fk_user = " . ((int) $userid);
1289
        }
1290
1291
        dol_syslog(get_class($this) . "::getSummaryOfTimeSpent", LOG_DEBUG);
1292
        $resql = $this->db->query($sql);
1293
        if ($resql) {
1294
            $obj = $this->db->fetch_object($resql);
1295
1296
            $result['min_date'] = $obj->min_date; // deprecated. use the ->timespent_xxx instead
1297
            $result['max_date'] = $obj->max_date; // deprecated. use the ->timespent_xxx instead
1298
            $result['total_duration'] = $obj->total_duration; // deprecated. use the ->timespent_xxx instead
1299
1300
            $this->timespent_min_date = $this->db->jdate($obj->min_date);
1301
            $this->timespent_max_date = $this->db->jdate($obj->max_date);
1302
            $this->timespent_total_duration = $obj->total_duration;
1303
            $this->timespent_total_amount = $obj->total_amount;
1304
            $this->timespent_nblinesnull = ($obj->nblinesnull ? $obj->nblinesnull : 0);
1305
            $this->timespent_nblines = ($obj->nblines ? $obj->nblines : 0);
1306
1307
            $this->db->free($resql);
1308
        } else {
1309
            dol_print_error($this->db);
1310
        }
1311
        return $result;
1312
    }
1313
1314
    /**
1315
     *  Calculate quantity and value of time consumed using the thm (hourly amount value of work for user entering time)
1316
     *
1317
     * @param User|string $fuser Filter on a dedicated user
1318
     * @param string      $dates Start date (ex 00:00:00)
1319
     * @param string      $datee End date (ex 23:59:59)
1320
     *
1321
     * @return     array                   Array of info for task array('amount','nbseconds','nblinesnull')
1322
     */
1323
    public function getSumOfAmount($fuser = '', $dates = '', $datee = '')
1324
    {
1325
        $id = $this->id;
1326
1327
        $result = [];
1328
1329
        $sql = "SELECT";
1330
        $sql .= " SUM(t.element_duration) as nbseconds,";
1331
        $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";
1332
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as t";
1333
        $sql .= " WHERE t.elementtype='task' AND t.fk_element = " . ((int) $id);
1334
        if (is_object($fuser) && $fuser->id > 0) {
1335
            $sql .= " AND fk_user = " . ((int) $fuser->id);
1336
        }
1337
        if ($dates > 0) {
1338
            $datefieldname = "element_datehour";
1339
            $sql .= " AND (" . $datefieldname . " >= '" . $this->db->idate($dates) . "' OR " . $datefieldname . " IS NULL)";
1340
        }
1341
        if ($datee > 0) {
1342
            $datefieldname = "element_datehour";
1343
            $sql .= " AND (" . $datefieldname . " <= '" . $this->db->idate($datee) . "' OR " . $datefieldname . " IS NULL)";
1344
        }
1345
        //print $sql;
1346
1347
        dol_syslog(get_class($this) . "::getSumOfAmount", LOG_DEBUG);
1348
        $resql = $this->db->query($sql);
1349
        if ($resql) {
1350
            $obj = $this->db->fetch_object($resql);
1351
1352
            $result['amount'] = $obj->amount;
1353
            $result['nbseconds'] = $obj->nbseconds;
1354
            $result['nblinesnull'] = $obj->nblinesnull;
1355
1356
            $this->db->free($resql);
1357
            return $result;
1358
        } else {
1359
            dol_print_error($this->db);
1360
            return $result;
1361
        }
1362
    }
1363
1364
    /**
1365
     *  Load properties of timespent of a task from the time spent ID.
1366
     *
1367
     * @param int $id Id in time spent table
1368
     *
1369
     * @return int             Return integer <0 if KO, >0 if OK
1370
     */
1371
    public function fetchTimeSpent($id)
1372
    {
1373
        $timespent = new TimeSpent($this->db);
1374
        $timespent->fetch($id);
1375
1376
        dol_syslog(get_class($this) . "::fetchTimeSpent", LOG_DEBUG);
1377
1378
        if ($timespent->id > 0) {
1379
            $this->timespent_id = $timespent->id;
1380
            $this->id = $timespent->fk_element;
1381
            $this->timespent_date = $timespent->element_date;
1382
            $this->timespent_datehour = $timespent->element_datehour;
1383
            $this->timespent_withhour = $timespent->element_date_withhour;
1384
            $this->timespent_duration = $timespent->element_duration;
1385
            $this->timespent_fk_user = $timespent->fk_user;
1386
            $this->timespent_fk_product = $timespent->fk_product;
1387
            $this->timespent_thm = $timespent->thm; // hourly rate
1388
            $this->timespent_note = $timespent->note;
1389
1390
            return 1;
1391
        }
1392
1393
        return 0;
1394
    }
1395
1396
    /**
1397
     *  Load all records of time spent
1398
     *
1399
     * @param User   $userobj         User object
1400
     * @param string $morewherefilter Add more filter into where SQL request (must start with ' AND ...')
1401
     *
1402
     * @return array|int                       Return integer <0 if KO, array of time spent if OK
1403
     */
1404
    public function fetchAllTimeSpent(User $userobj, $morewherefilter = '')
1405
    {
1406
        $arrayres = [];
1407
1408
        $sql = "SELECT";
1409
        $sql .= " s.rowid as socid,";
1410
        $sql .= " s.nom as thirdparty_name,";
1411
        $sql .= " s.email as thirdparty_email,";
1412
        $sql .= " ptt.rowid,";
1413
        $sql .= " ptt.fk_element as fk_task,";
1414
        $sql .= " ptt.element_date as task_date,";
1415
        $sql .= " ptt.element_datehour as task_datehour,";
1416
        $sql .= " ptt.element_date_withhour as task_date_withhour,";
1417
        $sql .= " ptt.element_duration as task_duration,";
1418
        $sql .= " ptt.fk_user,";
1419
        $sql .= " ptt.note,";
1420
        $sql .= " ptt.thm,";
1421
        $sql .= " pt.rowid as task_id,";
1422
        $sql .= " pt.ref as task_ref,";
1423
        $sql .= " pt.label as task_label,";
1424
        $sql .= " p.rowid as project_id,";
1425
        $sql .= " p.ref as project_ref,";
1426
        $sql .= " p.title as project_label,";
1427
        $sql .= " p.public as public";
1428
        $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet as p";
1429
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
1430
        $sql .= " WHERE ptt.fk_element = pt.rowid AND pt.fk_projet = p.rowid";
1431
        $sql .= " AND ptt.elementtype = 'task'";
1432
        $sql .= " AND ptt.fk_user = " . ((int) $userobj->id);
1433
        $sql .= " AND pt.entity IN (" . getEntity('project') . ")";
1434
        if ($morewherefilter) {
1435
            $sql .= $morewherefilter;
1436
        }
1437
1438
        dol_syslog(get_class($this) . "::fetchAllTimeSpent", LOG_DEBUG);
1439
        $resql = $this->db->query($sql);
1440
        if ($resql) {
1441
            $num = $this->db->num_rows($resql);
1442
1443
            $i = 0;
1444
            while ($i < $num) {
1445
                $obj = $this->db->fetch_object($resql);
1446
1447
                $newobj = new stdClass();
1448
1449
                $newobj->socid = $obj->socid;
1450
                $newobj->thirdparty_name = $obj->thirdparty_name;
1451
                $newobj->thirdparty_email = $obj->thirdparty_email;
1452
1453
                $newobj->fk_project = $obj->project_id;
1454
                $newobj->project_ref = $obj->project_ref;
1455
                $newobj->project_label = $obj->project_label;
1456
                $newobj->public = $obj->project_public;
1457
1458
                $newobj->fk_task = $obj->task_id;
1459
                $newobj->task_ref = $obj->task_ref;
1460
                $newobj->task_label = $obj->task_label;
1461
1462
                $newobj->timespent_id = $obj->rowid;
1463
                $newobj->timespent_date = $this->db->jdate($obj->task_date);
1464
                $newobj->timespent_datehour = $this->db->jdate($obj->task_datehour);
1465
                $newobj->timespent_withhour = $obj->task_date_withhour;
1466
                $newobj->timespent_duration = $obj->task_duration;
1467
                $newobj->timespent_fk_user = $obj->fk_user;
1468
                $newobj->timespent_thm = $obj->thm; // hourly rate
1469
                $newobj->timespent_note = $obj->note;
1470
1471
                $arrayres[] = $newobj;
1472
1473
                $i++;
1474
            }
1475
1476
            $this->db->free($resql);
1477
        } else {
1478
            dol_print_error($this->db);
1479
            $this->error = "Error " . $this->db->lasterror();
1480
            return -1;
1481
        }
1482
1483
        return $arrayres;
1484
    }
1485
1486
    /**
1487
     *  Update time spent
1488
     *
1489
     * @param User $user      User id
1490
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
1491
     *
1492
     * @return int                     Return integer <0 if KO, >0 if OK
1493
     */
1494
    public function updateTimeSpent($user, $notrigger = 0)
1495
    {
1496
        global $conf, $langs;
1497
1498
        $ret = 0;
1499
1500
        // Check parameters
1501
        if ($this->timespent_date == '') {
1502
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Date"));
1503
            return -1;
1504
        }
1505
        if (!($this->timespent_fk_user > 0)) {
1506
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentities("User"));
1507
            return -1;
1508
        }
1509
1510
        // Clean parameters
1511
        if (empty($this->timespent_datehour)) {
1512
            $this->timespent_datehour = $this->timespent_date;
1513
        }
1514
        if (isset($this->timespent_note)) {
1515
            $this->timespent_note = trim($this->timespent_note);
1516
        }
1517
1518
        if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1519
            require_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
1520
            $restrictBefore = dol_time_plus_duree(dol_now(), -$conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1521
1522
            if ($this->timespent_date < $restrictBefore) {
1523
                $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1524
                $this->errors[] = $this->error;
1525
                return -1;
1526
            }
1527
        }
1528
1529
        $this->db->begin();
1530
1531
        $timespent = new TimeSpent($this->db);
1532
        $timespent->fetch($this->timespent_id);
1533
        $timespent->element_date = $this->timespent_date;
1534
        $timespent->element_datehour = $this->timespent_datehour;
1535
        $timespent->element_date_withhour = $this->timespent_withhour;
1536
        $timespent->element_duration = $this->timespent_duration;
1537
        $timespent->fk_user = $this->timespent_fk_user;
1538
        $timespent->fk_product = $this->timespent_fk_product;
1539
        $timespent->note = $this->timespent_note;
1540
        $timespent->invoice_id = $this->timespent_invoiceid;
1541
        $timespent->invoice_line_id = $this->timespent_invoicelineid;
1542
1543
        dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1544
        if ($timespent->update($user) > 0) {
1545
            if (!$notrigger) {
1546
                // Call trigger
1547
                $result = $this->call_trigger('TASK_TIMESPENT_MODIFY', $user);
1548
                if ($result < 0) {
1549
                    $this->db->rollback();
1550
                    $ret = -1;
1551
                } else {
1552
                    $ret = 1;
1553
                }
1554
                // End call triggers
1555
            } else {
1556
                $ret = 1;
1557
            }
1558
        } else {
1559
            $this->error = $this->db->lasterror();
1560
            $this->db->rollback();
1561
            $ret = -1;
1562
        }
1563
1564
        if ($ret == 1 && (($this->timespent_old_duration != $this->timespent_duration) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM'))) {
1565
            if ($this->timespent_old_duration != $this->timespent_duration) {
1566
                // Recalculate amount of time spent for task and update denormalized field
1567
                $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1568
                $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) . ")";
1569
                if (isset($this->progress)) {
1570
                    $sql .= ", progress = " . ((float) $this->progress); // Do not overwrite value if not provided
1571
                }
1572
                $sql .= " WHERE rowid = " . ((int) $this->id);
1573
1574
                dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1575
                if (!$this->db->query($sql)) {
1576
                    $this->error = $this->db->lasterror();
1577
                    $this->db->rollback();
1578
                    $ret = -2;
1579
                }
1580
            }
1581
1582
            // Update hourly rate of this time spent entry, but only if it was not set initially
1583
            $res_update = 1;
1584
            if (empty($timespent->thm) || getDolGlobalString('TIMESPENT_ALWAYS_UPDATE_THM')) {
1585
                $resql_thm_user = $this->db->query("SELECT thm FROM " . MAIN_DB_PREFIX . "user WHERE rowid = " . ((int) $timespent->fk_user));
1586
                if (!empty($resql_thm_user)) {
1587
                    $obj_thm_user = $this->db->fetch_object($resql_thm_user);
1588
                    $timespent->thm = $obj_thm_user->thm;
1589
                }
1590
                $res_update = $timespent->update($user);
1591
            }
1592
1593
            dol_syslog(get_class($this) . "::updateTimeSpent", LOG_DEBUG);
1594
            if ($res_update <= 0) {
1595
                $this->error = $this->db->lasterror();
1596
                $ret = -2;
1597
            }
1598
        }
1599
1600
        if ($ret >= 0) {
1601
            $this->db->commit();
1602
        }
1603
        return $ret;
1604
    }
1605
1606
    /**
1607
     *  Delete time spent
1608
     *
1609
     * @param User $user      User that delete
1610
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
1611
     *
1612
     * @return int                     Return integer <0 if KO, >0 if OK
1613
     */
1614
    public function delTimeSpent($user, $notrigger = 0)
1615
    {
1616
        global $conf, $langs;
1617
1618
        $error = 0;
1619
1620
        if (getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS')) {
1621
            require_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
1622
            $restrictBefore = dol_time_plus_duree(dol_now(), -$conf->global->PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS, 'm');
1623
1624
            if ($this->timespent_date < $restrictBefore) {
1625
                $this->error = $langs->trans('TimeRecordingRestrictedToNMonthsBack', getDolGlobalString('PROJECT_TIMESHEET_PREVENT_AFTER_MONTHS'));
1626
                $this->errors[] = $this->error;
1627
                return -1;
1628
            }
1629
        }
1630
1631
        $this->db->begin();
1632
1633
        if (!$notrigger) {
1634
            // Call trigger
1635
            $result = $this->call_trigger('TASK_TIMESPENT_DELETE', $user);
1636
            if ($result < 0) {
1637
                $error++;
1638
            }
1639
            // End call triggers
1640
        }
1641
1642
        if (!$error) {
1643
            $timespent = new TimeSpent($this->db);
1644
            $timespent->fetch($this->timespent_id);
1645
1646
            $res_del = $timespent->delete($user);
1647
1648
            if ($res_del < 0) {
1649
                $error++;
1650
                $this->errors[] = "Error " . $this->db->lasterror();
1651
            }
1652
        }
1653
1654
        if (!$error) {
1655
            $sql = "UPDATE " . MAIN_DB_PREFIX . "projet_task";
1656
            $sql .= " SET duration_effective = duration_effective - " . $this->db->escape($this->timespent_duration ? $this->timespent_duration : 0);
1657
            $sql .= " WHERE rowid = " . ((int) $this->id);
1658
1659
            dol_syslog(get_class($this) . "::delTimeSpent", LOG_DEBUG);
1660
            if ($this->db->query($sql)) {
1661
                $result = 0;
1662
            } else {
1663
                $this->error = $this->db->lasterror();
1664
                $result = -2;
1665
            }
1666
        }
1667
1668
        // Commit or rollback
1669
        if ($error) {
1670
            foreach ($this->errors as $errmsg) {
1671
                dol_syslog(get_class($this) . "::delTimeSpent " . $errmsg, LOG_ERR);
1672
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1673
            }
1674
            $this->db->rollback();
1675
            return -1 * $error;
1676
        } else {
1677
            $this->db->commit();
1678
            return 1;
1679
        }
1680
    }
1681
1682
    /**
1683
     *  Delete task from database
1684
     *
1685
     * @param User $user      User that delete
1686
     * @param int  $notrigger 0=launch triggers after, 1=disable triggers
1687
     *
1688
     * @return int                     Return integer <0 if KO, >0 if OK
1689
     */
1690
    public function delete($user, $notrigger = 0)
1691
    {
1692
        global $conf;
1693
        require_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1694
1695
        $error = 0;
1696
1697
        $this->db->begin();
1698
1699
        if ($this->hasChildren() > 0) {
1700
            dol_syslog(get_class($this) . "::delete Can't delete record as it has some sub tasks", LOG_WARNING);
1701
            $this->error = 'ErrorRecordHasSubTasks';
1702
            $this->db->rollback();
1703
            return 0;
1704
        }
1705
1706
        $objectisused = $this->isObjectUsed($this->id);
1707
        if (!empty($objectisused)) {
1708
            dol_syslog(get_class($this) . "::delete Can't delete record as it has some child", LOG_WARNING);
1709
            $this->error = 'ErrorRecordHasChildren';
1710
            $this->db->rollback();
1711
            return 0;
1712
        }
1713
1714
        if (!$error) {
1715
            // Delete linked contacts
1716
            $res = $this->delete_linked_contact();
1717
            if ($res < 0) {
1718
                $this->error = 'ErrorFailToDeleteLinkedContact';
1719
                //$error++;
1720
                $this->db->rollback();
1721
                return 0;
1722
            }
1723
        }
1724
1725
        if (!$error) {
1726
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "element_time";
1727
            $sql .= " WHERE fk_element = " . ((int) $this->id) . " AND elementtype = 'task'";
1728
1729
            $resql = $this->db->query($sql);
1730
            if (!$resql) {
1731
                $error++;
1732
                $this->errors[] = "Error " . $this->db->lasterror();
1733
            }
1734
        }
1735
1736
        if (!$error) {
1737
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task_extrafields";
1738
            $sql .= " WHERE fk_object = " . ((int) $this->id);
1739
1740
            $resql = $this->db->query($sql);
1741
            if (!$resql) {
1742
                $error++;
1743
                $this->errors[] = "Error " . $this->db->lasterror();
1744
            }
1745
        }
1746
1747
        if (!$error) {
1748
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task";
1749
            $sql .= " WHERE rowid=" . ((int) $this->id);
1750
1751
            $resql = $this->db->query($sql);
1752
            if (!$resql) {
1753
                $error++;
1754
                $this->errors[] = "Error " . $this->db->lasterror();
1755
            }
1756
        }
1757
1758
        if (!$error) {
1759
            if (!$notrigger) {
1760
                // Call trigger
1761
                $result = $this->call_trigger('TASK_DELETE', $user);
1762
                if ($result < 0) {
1763
                    $error++;
1764
                }
1765
                // End call triggers
1766
            }
1767
        }
1768
1769
        // Commit or rollback
1770
        if ($error) {
1771
            foreach ($this->errors as $errmsg) {
1772
                dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR);
1773
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1774
            }
1775
            $this->db->rollback();
1776
            return -1 * $error;
1777
        } else {
1778
            //Delete associated link file
1779
            if ($conf->project->dir_output) {
1780
                $projectstatic = new Project($this->db);
1781
                $projectstatic->fetch($this->fk_project);
1782
1783
                $dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($projectstatic->ref) . '/' . dol_sanitizeFileName($this->id);
1784
                dol_syslog(get_class($this) . "::delete dir=" . $dir, LOG_DEBUG);
1785
                if (file_exists($dir)) {
1786
                    require_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1787
                    $res = @dol_delete_dir_recursive($dir);
1788
                    if (!$res) {
1789
                        $this->error = 'ErrorFailToDeleteDir';
1790
                        $this->db->rollback();
1791
                        return 0;
1792
                    }
1793
                }
1794
            }
1795
1796
            $this->db->commit();
1797
1798
            return 1;
1799
        }
1800
    }
1801
1802
    /**
1803
     *  Return nb of children
1804
     *
1805
     * @return int     Return integer <0 if KO, 0 if no children, >0 if OK
1806
     */
1807
    public function hasChildren()
1808
    {
1809
        $error = 0;
1810
        $ret = 0;
1811
1812
        $sql = "SELECT COUNT(*) as nb";
1813
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet_task";
1814
        $sql .= " WHERE fk_task_parent = " . ((int) $this->id);
1815
1816
        dol_syslog(get_class($this) . "::hasChildren", LOG_DEBUG);
1817
        $resql = $this->db->query($sql);
1818
        if (!$resql) {
1819
            $error++;
1820
            $this->errors[] = "Error " . $this->db->lasterror();
1821
        } else {
1822
            $obj = $this->db->fetch_object($resql);
1823
            if ($obj) {
1824
                $ret = $obj->nb;
1825
            }
1826
            $this->db->free($resql);
1827
        }
1828
1829
        if (!$error) {
1830
            return $ret;
1831
        } else {
1832
            return -1;
1833
        }
1834
    }
1835
1836
    /** Load an object from its id and create a new one in database
1837
     *
1838
     * @param User $user              User making the clone
1839
     * @param int  $fromid            Id of object to clone
1840
     * @param int  $project_id        Id of project to attach clone task
1841
     * @param int  $parent_task_id    Id of task to attach clone task
1842
     * @param bool $clone_change_dt   recalculate date of task regarding new project start date
1843
     * @param bool $clone_affectation clone affectation of project
1844
     * @param bool $clone_time        clone time of project
1845
     * @param bool $clone_file        clone file of project
1846
     * @param bool $clone_note        clone note of project
1847
     * @param bool $clone_prog        clone progress of project
1848
     *
1849
     * @return int                             New id of clone
1850
     */
1851
    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)
1852
    {
1853
        global $langs, $conf;
1854
1855
        $error = 0;
1856
1857
        //Use 00:00 of today if time is use on task.
1858
        $now = dol_mktime(0, 0, 0, dol_print_date(dol_now(), '%m'), dol_print_date(dol_now(), '%d'), dol_print_date(dol_now(), '%Y'));
1859
1860
        $datec = $now;
1861
1862
        $clone_task = new Task($this->db);
1863
        $origin_task = new Task($this->db);
1864
1865
        $clone_task->context['createfromclone'] = 'createfromclone';
1866
1867
        $this->db->begin();
1868
1869
        // Load source object
1870
        $clone_task->fetch($fromid);
1871
        $clone_task->fetch_optionals();
1872
        //var_dump($clone_task->array_options);exit;
1873
1874
        $origin_task->fetch($fromid);
1875
1876
        $defaultref = '';
1877
        $obj = !getDolGlobalString('PROJECT_TASK_ADDON') ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
1878
        if (getDolGlobalString('PROJECT_TASK_ADDON') && is_readable(DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON') . ".php")) {
1879
            require_once DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . getDolGlobalString('PROJECT_TASK_ADDON') . '.php';
1880
            $modTask = new $obj();
1881
            $defaultref = $modTask->getNextValue(0, $clone_task);
1882
        }
1883
1884
        $ori_project_id = $clone_task->fk_project;
1885
1886
        $clone_task->id = 0;
1887
        $clone_task->ref = $defaultref;
1888
        $clone_task->fk_project = $project_id;
1889
        $clone_task->fk_task_parent = $parent_task_id;
1890
        $clone_task->date_c = $datec;
1891
        $clone_task->planned_workload = $origin_task->planned_workload;
1892
        $clone_task->rang = $origin_task->rang;
1893
1894
        //Manage Task Date
1895
        if ($clone_change_dt) {
1896
            $projectstatic = new Project($this->db);
1897
            $projectstatic->fetch($ori_project_id);
1898
1899
            //Origin project start date
1900
            $orign_project_dt_start = $projectstatic->date_start;
1901
1902
            //Calculate new task start date with difference between origin proj start date and origin task start date
1903
            if (!empty($clone_task->date_start)) {
1904
                $clone_task->date_start = $now + $clone_task->date_start - $orign_project_dt_start;
1905
            }
1906
1907
            //Calculate new task end date with difference between origin proj end date and origin task end date
1908
            if (!empty($clone_task->date_end)) {
1909
                $clone_task->date_end = $now + $clone_task->date_end - $orign_project_dt_start;
1910
            }
1911
        }
1912
1913
        if (!$clone_prog) {
1914
            $clone_task->progress = 0;
1915
        }
1916
1917
        // Create clone
1918
        $result = $clone_task->create($user);
1919
1920
        // Other options
1921
        if ($result < 0) {
1922
            $this->error = $clone_task->error;
1923
            $error++;
1924
        }
1925
1926
        // End
1927
        if (!$error) {
1928
            $clone_task_id = $clone_task->id;
1929
            $clone_task_ref = $clone_task->ref;
1930
1931
            //Note Update
1932
            if (!$clone_note) {
1933
                $clone_task->note_private = '';
1934
                $clone_task->note_public = '';
1935
            } else {
1936
                $this->db->begin();
1937
                $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_public, ENT_QUOTES | ENT_HTML5), '_public');
1938
                if ($res < 0) {
1939
                    $this->error .= $clone_task->error;
1940
                    $error++;
1941
                    $this->db->rollback();
1942
                } else {
1943
                    $this->db->commit();
1944
                }
1945
1946
                $this->db->begin();
1947
                $res = $clone_task->update_note(dol_html_entity_decode($clone_task->note_private, ENT_QUOTES | ENT_HTML5), '_private');
1948
                if ($res < 0) {
1949
                    $this->error .= $clone_task->error;
1950
                    $error++;
1951
                    $this->db->rollback();
1952
                } else {
1953
                    $this->db->commit();
1954
                }
1955
            }
1956
1957
            //Duplicate file
1958
            if ($clone_file) {
1959
                require_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1960
1961
                //retrieve project origin ref to know folder to copy
1962
                $projectstatic = new Project($this->db);
1963
                $projectstatic->fetch($ori_project_id);
1964
                $ori_project_ref = $projectstatic->ref;
1965
1966
                if ($ori_project_id != $project_id) {
1967
                    $projectstatic->fetch($project_id);
1968
                    $clone_project_ref = $projectstatic->ref;
1969
                } else {
1970
                    $clone_project_ref = $ori_project_ref;
1971
                }
1972
1973
                $clone_task_dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($clone_project_ref) . "/" . dol_sanitizeFileName($clone_task_ref);
1974
                $ori_task_dir = $conf->project->dir_output . "/" . dol_sanitizeFileName($ori_project_ref) . "/" . dol_sanitizeFileName($fromid);
1975
1976
                $filearray = dol_dir_list($ori_task_dir, "files", 0, '', '(\.meta|_preview.*\.png)$', '', SORT_ASC, 1);
1977
                foreach ($filearray as $key => $file) {
1978
                    if (!file_exists($clone_task_dir)) {
1979
                        if (dol_mkdir($clone_task_dir) < 0) {
1980
                            $this->error .= $langs->trans('ErrorInternalErrorDetected') . ':dol_mkdir';
1981
                            $error++;
1982
                        }
1983
                    }
1984
1985
                    $rescopy = dol_copy($ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name'], 0, 1);
1986
                    if (is_numeric($rescopy) && $rescopy < 0) {
1987
                        $this->error .= $langs->trans("ErrorFailToCopyFile", $ori_task_dir . '/' . $file['name'], $clone_task_dir . '/' . $file['name']);
1988
                        $error++;
1989
                    }
1990
                }
1991
            }
1992
1993
            // clone affectation
1994
            if ($clone_affectation) {
1995
                $origin_task = new Task($this->db);
1996
                $origin_task->fetch($fromid);
1997
1998
                foreach (['internal', 'external'] as $source) {
1999
                    $tab = $origin_task->liste_contact(-1, $source);
2000
                    $num = count($tab);
2001
                    $i = 0;
2002
                    while ($i < $num) {
2003
                        $clone_task->add_contact($tab[$i]['id'], $tab[$i]['code'], $tab[$i]['source']);
2004
                        if ($clone_task->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2005
                            $langs->load("errors");
2006
                            $this->error .= $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType");
2007
                            $error++;
2008
                        } else {
2009
                            if ($clone_task->error != '') {
2010
                                $this->error .= $clone_task->error;
2011
                                $error++;
2012
                            }
2013
                        }
2014
                        $i++;
2015
                    }
2016
                }
2017
            }
2018
2019
            if ($clone_time) {
2020
                //TODO clone time of affectation
2021
            }
2022
        }
2023
2024
        unset($clone_task->context['createfromclone']);
2025
2026
        if (!$error) {
2027
            $this->db->commit();
2028
            return $clone_task_id;
2029
        } else {
2030
            $this->db->rollback();
2031
            dol_syslog(get_class($this) . "::createFromClone nbError: " . $error . " error : " . $this->error, LOG_ERR);
2032
            return -1;
2033
        }
2034
    }
2035
2036
    /**
2037
     *  Return status label of object
2038
     *
2039
     * @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
2040
     *                      label + Picto
2041
     *
2042
     * @return string              Label
2043
     */
2044
    public function getLibStatut($mode = 0)
2045
    {
2046
        return $this->LibStatut($this->status, $mode);
2047
    }
2048
2049
    /**
2050
     *  Return status label for an object
2051
     *
2052
     * @param int     $status Id status
2053
     * @param integer $mode   0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label,
2054
     *                        5=Short label + Picto
2055
     *
2056
     * @return string                  Label
2057
     */
2058
    public function LibStatut($status, $mode = 0)
2059
    {
2060
        // phpcs:enable
2061
        global $langs;
2062
2063
        // list of Statut of the task
2064
        $this->labelStatus[0] = 'Draft';
2065
        $this->labelStatus[1] = 'ToDo';
2066
        $this->labelStatus[2] = 'Running';
2067
        $this->labelStatus[3] = 'Finish';
2068
        $this->labelStatus[4] = 'Transfered';
2069
        $this->labelStatusShort[0] = 'Draft';
2070
        $this->labelStatusShort[1] = 'ToDo';
2071
        $this->labelStatusShort[2] = 'Running';
2072
        $this->labelStatusShort[3] = 'Completed';
2073
        $this->labelStatusShort[4] = 'Transfered';
2074
2075
        if ($mode == 0) {
2076
            return $langs->trans($this->labelStatus[$status]);
2077
        } elseif ($mode == 1) {
2078
            return $langs->trans($this->labelStatusShort[$status]);
2079
        } elseif ($mode == 2) {
2080
            if ($status == 0) {
2081
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2082
            } elseif ($status == 1) {
2083
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2084
            } elseif ($status == 2) {
2085
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2086
            } elseif ($status == 3) {
2087
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2088
            } elseif ($status == 4) {
2089
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2090
            } elseif ($status == 5) {
2091
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5') . ' ' . $langs->trans($this->labelStatusShort[$status]);
2092
            }
2093
        } elseif ($mode == 3) {
2094
            if ($status == 0) {
2095
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0');
2096
            } elseif ($status == 1) {
2097
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1');
2098
            } elseif ($status == 2) {
2099
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3');
2100
            } elseif ($status == 3) {
2101
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2102
            } elseif ($status == 4) {
2103
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6');
2104
            } elseif ($status == 5) {
2105
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5');
2106
            }
2107
        } elseif ($mode == 4) {
2108
            if ($status == 0) {
2109
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut0') . ' ' . $langs->trans($this->labelStatus[$status]);
2110
            } elseif ($status == 1) {
2111
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut1') . ' ' . $langs->trans($this->labelStatus[$status]);
2112
            } elseif ($status == 2) {
2113
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut3') . ' ' . $langs->trans($this->labelStatus[$status]);
2114
            } elseif ($status == 3) {
2115
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatus[$status]);
2116
            } elseif ($status == 4) {
2117
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut6') . ' ' . $langs->trans($this->labelStatus[$status]);
2118
            } elseif ($status == 5) {
2119
                return img_picto($langs->trans($this->labelStatusShort[$status]), 'statut5') . ' ' . $langs->trans($this->labelStatus[$status]);
2120
            }
2121
        } elseif ($mode == 5) {
2122
            /*if ($status==0) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2123
            elseif ($status==1) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2124
            elseif ($status==2) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2125
            elseif ($status==3) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2126
            elseif ($status==4) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2127
            elseif ($status==5) return $langs->trans($this->labelStatusShort[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2128
            */
2129
            //else return $this->progress.' %';
2130
            return '&nbsp;';
2131
        } elseif ($mode == 6) {
2132
            /*if ($status==0) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut0');
2133
            elseif ($status==1) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut1');
2134
            elseif ($status==2) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut3');
2135
            elseif ($status==3) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2136
            elseif ($status==4) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut6');
2137
            elseif ($status==5) return $langs->trans($this->labelStatus[$status]).' '.img_picto($langs->trans($this->labelStatusShort[$status]),'statut5');
2138
            */
2139
            //else return $this->progress.' %';
2140
            return '&nbsp;';
2141
        }
2142
        return "";
2143
    }
2144
2145
    /**
2146
     *  Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF
2147
     *
2148
     * @param string    $modele      force le modele a utiliser ('' par default)
2149
     * @param Translate $outputlangs object lang a utiliser pour traduction
0 ignored issues
show
Bug introduced by
The type DoliModules\Project\Model\Translate was not found. Did you mean Translate? If so, make sure to prefix the type with \.
Loading history...
2150
     * @param int       $hidedetails Hide details of lines
2151
     * @param int       $hidedesc    Hide description
2152
     * @param int       $hideref     Hide ref
2153
     *
2154
     * @return int                         0 if KO, 1 if OK
2155
     */
2156
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0)
2157
    {
2158
        global $conf;
2159
2160
        $outputlangs->load("projects");
2161
2162
        if (!dol_strlen($modele)) {
2163
            $modele = 'nodefault';
2164
2165
            if (!empty($this->model_pdf)) {
2166
                $modele = $this->model_pdf;
2167
            } elseif (getDolGlobalString('PROJECT_TASK_ADDON_PDF')) {
2168
                $modele = getDolGlobalString('PROJECT_TASK_ADDON_PDF');
2169
            }
2170
        }
2171
2172
        $modelpath = "core/modules/project/task/doc/";
2173
2174
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
2175
    }
2176
2177
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2178
2179
    /**
2180
     * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2181
     *
2182
     * @param User $user Object user
2183
     *
2184
     * @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK
2185
     */
2186
    public function load_board($user)
2187
    {
2188
        // phpcs:enable
2189
        global $conf, $langs;
2190
2191
        // For external user, no check is done on company because readability is managed by public status of project and assignment.
2192
        //$socid = $user->socid;
2193
        $socid = 0;
2194
2195
        $projectstatic = new Project($this->db);
2196
        $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, 0, 1, $socid);
2197
2198
        // List of tasks (does not care about permissions. Filtering will be done later)
2199
        $sql = "SELECT p.rowid as projectid, p.fk_statut as projectstatus,";
2200
        $sql .= " t.rowid as taskid, t.progress as progress, t.fk_statut as status,";
2201
        $sql .= " t.dateo as date_start, t.datee as date_end";
2202
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
2203
        //$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
2204
        //if (! $user->rights->societe->client->voir && ! $socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2205
        $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
2206
        $sql .= " WHERE p.entity IN (" . getEntity('project', 0) . ')';
2207
        $sql .= " AND p.fk_statut = 1";
2208
        $sql .= " AND t.fk_projet = p.rowid";
2209
        $sql .= " AND (t.progress IS NULL OR t.progress < 100)"; // tasks to do
2210
        if (!$user->hasRight('projet', 'all', 'lire')) {
2211
            $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectsListId) . ")";
2212
        }
2213
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2214
        //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).")";
2215
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2216
        // 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))";
2217
2218
        //print $sql;
2219
        $resql = $this->db->query($sql);
2220
        if ($resql) {
2221
            $task_static = new Task($this->db);
2222
2223
            $response = new WorkboardResponse();
2224
            $response->warning_delay = $conf->project->task->warning_delay / 60 / 60 / 24;
2225
            $response->label = $langs->trans("OpenedTasks");
2226
            if ($user->hasRight("projet", "all", "lire")) {
2227
                $response->url = DOL_URL_ROOT . '/projet/tasks/list.php?mainmenu=project';
2228
            } else {
2229
                $response->url = DOL_URL_ROOT . '/projet/tasks/list.php?mode=mine&amp;mainmenu=project';
2230
            }
2231
            $response->img = img_object('', "task");
2232
2233
            // This assignment in condition is not a bug. It allows walking the results.
2234
            while ($obj = $this->db->fetch_object($resql)) {
2235
                $response->nbtodo++;
2236
2237
                $task_static->projectstatus = $obj->projectstatus;
2238
                $task_static->progress = $obj->progress;
2239
                $task_static->fk_statut = $obj->status;
0 ignored issues
show
Deprecated Code introduced by
The property DoliModules\Project\Model\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

2239
                /** @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...
2240
                $task_static->status = $obj->status;
2241
                $task_static->date_start = $this->db->jdate($obj->date_start);
2242
                $task_static->date_end = $this->db->jdate($obj->date_end);
2243
2244
                if ($task_static->hasDelay()) {
2245
                    $response->nbtodolate++;
2246
                }
2247
            }
2248
2249
            return $response;
2250
        } else {
2251
            $this->error = $this->db->error();
2252
            return -1;
2253
        }
2254
    }
2255
2256
    /**
2257
     * Is the task delayed?
2258
     *
2259
     * @return bool
2260
     */
2261
    public function hasDelay()
2262
    {
2263
        global $conf;
2264
2265
        if (!($this->progress >= 0 && $this->progress < 100)) {
2266
            return false;
2267
        }
2268
2269
        $now = dol_now();
2270
2271
        $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 DoliModules\Project\Model\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

2271
        $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...
2272
2273
        return ($datetouse > 0 && ($datetouse < ($now - $conf->project->task->warning_delay)));
2274
    }
2275
2276
2277
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2278
2279
    /**
2280
     *      Load indicators this->nb for state board
2281
     *
2282
     * @return     int         Return integer <0 if ko, >0 if ok
2283
     */
2284
    public function loadStateBoard()
2285
    {
2286
        global $user;
2287
2288
        $mine = 0;
2289
        $socid = $user->socid;
2290
2291
        $projectstatic = new Project($this->db);
2292
        $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $mine, 1, $socid);
2293
2294
        // List of tasks (does not care about permissions. Filtering will be done later)
2295
        $sql = "SELECT count(p.rowid) as nb";
2296
        $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
2297
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s on p.fk_soc = s.rowid";
2298
        if (!$user->hasRight('societe', 'client', 'voir')) {
2299
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON sc.fk_soc = s.rowid";
2300
        }
2301
        $sql .= ", " . MAIN_DB_PREFIX . "projet_task as t";
2302
        $sql .= " WHERE p.entity IN (" . getEntity('project', 0) . ')';
2303
        $sql .= " AND t.fk_projet = p.rowid"; // tasks to do
2304
        if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
2305
            $sql .= " AND p.rowid IN (" . $this->db->sanitize($projectsListId) . ")";
2306
        }
2307
        // No need to check company, as filtering of projects must be done by getProjectsAuthorizedForUser
2308
        //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).")";
2309
        if ($socid) {
2310
            $sql .= "  AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = " . ((int) $socid) . ")";
2311
        }
2312
        if (!$user->hasRight('societe', 'client', 'voir')) {
2313
            $sql .= " AND ((s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id) . ") OR (s.rowid IS NULL))";
2314
        }
2315
2316
        $resql = $this->db->query($sql);
2317
        if ($resql) {
2318
            // This assignment in condition is not a bug. It allows walking the results.
2319
            while ($obj = $this->db->fetch_object($resql)) {
2320
                $this->nb["tasks"] = $obj->nb;
2321
            }
2322
            $this->db->free($resql);
2323
            return 1;
2324
        } else {
2325
            dol_print_error($this->db);
2326
            $this->error = $this->db->error();
2327
            return -1;
2328
        }
2329
    }
2330
2331
    /**
2332
     *  Return clicable link of object (with eventually picto)
2333
     *
2334
     * @param string $option    Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
2335
     * @param array  $arraydata Array of data
2336
     *
2337
     * @return     string                              HTML Code for Kanban thumb.
2338
     */
2339
    public function getKanbanView($option = '', $arraydata = null)
2340
    {
2341
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2342
2343
        $return = '<div class="box-flex-item box-flex-grow-zero">';
2344
        $return .= '<div class="info-box info-box-sm info-box-kanban">';
2345
        $return .= '<span class="info-box-icon bg-infobox-action">';
2346
        $return .= img_picto('', $this->picto);
2347
        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2348
        $return .= '</span>';
2349
        $return .= '<div class="info-box-content">';
2350
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>';
2351
        if ($selected >= 0) {
2352
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
2353
        }
2354
        if (!empty($arraydata['projectlink'])) {
2355
            //$tmpproject = $arraydata['project'];
2356
            //$return .= '<br><span class="info-box-status ">'.$tmpproject->getNomProject().'</span>';
2357
            $return .= '<br><span class="info-box-status ">' . $arraydata['projectlink'] . '</span>';
2358
        }
2359
        if (property_exists($this, 'budget_amount')) {
2360
            //$return .= '<br><span class="info-box-label amount">'.$langs->trans("Budget").' : '.price($this->budget_amount, 0, $langs, 1, 0, 0, $conf->currency).'</span>';
2361
        }
2362
        if (property_exists($this, 'duration_effective')) {
2363
            $return .= '<br><br><div class="info-box-label progressinkanban">' . getTaskProgressView($this, false, true) . '</div>';
2364
        }
2365
        $return .= '</div>';
2366
        $return .= '</div>';
2367
        $return .= '</div>';
2368
        return $return;
2369
    }
2370
2371
    /**
2372
     *  Return clicable name (with picto eventually)
2373
     *
2374
     * @param int    $withpicto             0=No picto, 1=Include picto into link, 2=Only picto
2375
     * @param string $option                'withproject' or ''
2376
     * @param string $mode                  Mode 'task', 'time', 'contact', 'note', document' define page to link to.
2377
     * @param int    $addlabel              0=Default, 1=Add label into string, >1=Add first chars into string
2378
     * @param string $sep                   Separator between ref and label if option addlabel is set
2379
     * @param int    $notooltip             1=Disable tooltip
2380
     * @param int    $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save
2381
     *                                      lastsearch_values whenclicking
2382
     *
2383
     * @return string                  Chaine avec URL
2384
     */
2385
    public function getNomUrl($withpicto = 0, $option = '', $mode = 'task', $addlabel = 0, $sep = ' - ', $notooltip = 0, $save_lastsearch_value = -1)
2386
    {
2387
        global $action, $conf, $hookmanager, $langs;
2388
2389
        if (!empty($conf->dol_no_mouse_hover)) {
2390
            $notooltip = 1; // Force disable tooltips
2391
        }
2392
2393
        $result = '';
2394
        $params = [
2395
            'id' => $this->id,
2396
            'objecttype' => $this->element,
2397
        ];
2398
        $classfortooltip = 'classfortooltip';
2399
        $dataparams = '';
2400
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2401
            $classfortooltip = 'classforajaxtooltip';
2402
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
2403
            $label = '';
2404
        } else {
2405
            $label = implode($this->getTooltipContentArray($params));
2406
        }
2407
2408
        $url = DOL_URL_ROOT . '/projet/tasks/' . $mode . '.php?id=' . $this->id . ($option == 'withproject' ? '&withproject=1' : '');
2409
        // Add param to save lastsearch_values or not
2410
        $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2411
        if ($save_lastsearch_value == -1 && isset($_SERVER['PHP_SELF']) && preg_match('/list\.php/', $_SERVER['PHP_SELF'])) {
2412
            $add_save_lastsearch_values = 1;
2413
        }
2414
        if ($add_save_lastsearch_values) {
2415
            $url .= '&save_lastsearch_values=1';
2416
        }
2417
2418
        $linkclose = '';
2419
        if (empty($notooltip)) {
2420
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2421
                $label = $langs->trans("ShowTask");
2422
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
2423
            }
2424
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
2425
            $linkclose .= $dataparams . ' class="' . $classfortooltip . ' nowraponall"';
2426
        } else {
2427
            $linkclose .= ' class="nowraponall"';
2428
        }
2429
2430
        $linkstart = '<a href="' . $url . '"';
2431
        $linkstart .= $linkclose . '>';
2432
        $linkend = '</a>';
2433
2434
        $picto = 'projecttask';
2435
2436
        $result .= $linkstart;
2437
        if ($withpicto) {
2438
            $result .= img_object(($notooltip ? '' : $label), $picto, 'class="paddingright"', 0, 0, $notooltip ? 0 : 1);
2439
        }
2440
        if ($withpicto != 2) {
2441
            $result .= $this->ref;
2442
        }
2443
        $result .= $linkend;
2444
        if ($withpicto != 2) {
2445
            $result .= (($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
2446
        }
2447
2448
        $parameters = ['id' => $this->id, 'getnomurl' => &$result];
2449
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2450
        if ($reshook > 0) {
2451
            $result = $hookmanager->resPrint;
2452
        } else {
2453
            $result .= $hookmanager->resPrint;
2454
        }
2455
2456
        return $result;
2457
    }
2458
2459
    /**
2460
     * getTooltipContentArray
2461
     *
2462
     * @param array $params ex option, infologin
2463
     *
2464
     * @return array
2465
     * @since v18
2466
     */
2467
    public function getTooltipContentArray($params)
2468
    {
2469
        global $langs;
2470
2471
        $langs->load('projects');
2472
2473
        $datas = [];
2474
        $datas['picto'] = img_picto('', $this->picto) . ' <u>' . $langs->trans("Task") . '</u>';
2475
        if (!empty($this->ref)) {
2476
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
2477
        }
2478
        if (!empty($this->label)) {
2479
            $datas['label'] = '<br><b>' . $langs->trans('LabelTask') . ':</b> ' . $this->label;
2480
        }
2481
        if ($this->date_start || $this->date_end) {
2482
            $datas['range'] = "<br>" . get_date_range($this->date_start, $this->date_end, '', $langs, 0);
2483
        }
2484
2485
        return $datas;
2486
    }
2487
}
2488