Passed
Pull Request — dev (#8)
by Rafael
58:47
created

Evaluation   F

Complexity

Total Complexity 157

Size/Duplication

Total Lines 1009
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 435
dl 0
loc 1009
rs 2
c 0
b 0
f 0
wmc 157

23 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 35 14
B fetchAll() 0 53 9
A getLibStatut() 0 3 1
A generateDocument() 0 25 5
A update() 0 3 1
A info() 0 22 4
A setDraft() 0 8 2
A reopen() 0 8 2
A delete() 0 3 1
A create() 0 26 6
A getLinesArray() 0 13 2
A initAsSpecimen() 0 7 1
F getNomUrl() 0 103 34
A getLastEvaluationForUser() 0 21 3
A fetchLines() 0 6 1
F createFromClone() 0 88 22
A deleteLine() 0 8 2
A LibStatut() 0 21 4
A fetch() 0 7 3
B getNextNumRef() 0 47 9
F validate() 0 116 22
A cancel() 0 8 2
B getKanbanView() 0 27 7

How to fix   Complexity   

Complex Class

Complex classes like Evaluation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Evaluation, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2017       Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2021       Gauthier VERDOL             <[email protected]>
5
 * Copyright (C) 2021       Greg Rastklan               <[email protected]>
6
 * Copyright (C) 2021       Jean-Pascal BOUDET          <[email protected]>
7
 * Copyright (C) 2021       Grégory BLEMAND             <[email protected]>
8
 * Copyright (C) 2024       Frédéric France             <[email protected]>
9
 * Copyright (C) 2024		MDW							<[email protected]>
10
 * Copyright (C) 2024       Rafael San José             <[email protected]>
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 3 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24
 */
25
26
namespace Dolibarr\Code\Hrm\Classes;
27
28
use Dolibarr\Code\User\Classes\User;
29
use Dolibarr\Core\Base\CommonObject;
30
use DoliDB;
31
32
/**
33
 *    \file        htdocs/hrm/class/evaluation.class.php
34
 *    \ingroup     hrm
35
 *    \brief       This file is a CRUD class file for Evaluation (Create/Read/Update/Delete)
36
 */
37
38
/**
39
 * Class for Evaluation
40
 */
41
class Evaluation extends CommonObject
42
{
43
    /**
44
     * @var string ID of module.
45
     */
46
    public $module = 'hrm';
47
48
    /**
49
     * @var string ID to identify managed object.
50
     */
51
    public $element = 'evaluation';
52
53
    /**
54
     * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management.
55
     */
56
    public $table_element = 'hrm_evaluation';
57
58
    /**
59
     * @var string String with name of icon for evaluation. Must be the part after the 'object_' into object_evaluation.png
60
     */
61
    public $picto = 'label';
62
63
64
    const STATUS_DRAFT = 0;
65
    const STATUS_VALIDATED = 1;
66
    const STATUS_CANCELED = 9;
67
    const STATUS_CLOSED = 6;
68
69
70
    /**
71
     *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
72
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
73
     *  'label' the translation key.
74
     *  'picto' is code of a picto to show before value in forms
75
     *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalString("MY_SETUP_PARAM")')
76
     *  'position' is the sort order of field.
77
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
78
     *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
79
     *  'noteditable' says if field is not editable (1 or 0)
80
     *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
81
     *  'index' if we want an index in database.
82
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
83
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
84
     *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
85
     *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
86
     *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
87
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
88
     *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
89
     *  'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar'
90
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
91
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
92
     *
93
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
94
     */
95
96
    // BEGIN MODULEBUILDER PROPERTIES
97
    /**
98
     * @var array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}>  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string,array{type:...ring>,comment?:string}> at position 16 could not be parsed: Expected '}' at position 16, but found 'int'.
Loading history...
99
     */
100
    public $fields = array(
101
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
102
        'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 20, 'notnull' => 1, 'visible' => 4, 'noteditable' => 1, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'comment' => "Reference of object"),
103
        'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'position' => 30, 'notnull' => 0, 'visible' => 1, 'searchall' => 1, 'css' => 'minwidth300', 'cssview' => 'wordbreak', 'showoncombobox' => '2',),
104
        'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => 3,),
105
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'position' => 61, 'notnull' => 0, 'visible' => 0,),
106
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 62, 'notnull' => 0, 'visible' => 0,),
107
        'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'visible' => -2,),
108
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 501, 'notnull' => 0, 'visible' => -2,),
109
        'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php:0', 'label' => 'UserAuthor', 'enabled' => 1, 'position' => 510, 'notnull' => 1, 'visible' => -2, 'foreignkey' => 'user.rowid',),
110
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php:0', 'label' => 'UserModif', 'enabled' => 1, 'position' => 511, 'notnull' => -1, 'visible' => -2,),
111
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 1000, 'notnull' => -1, 'visible' => -2,),
112
        'status' => array('type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => '0', 'visible' => 5, 'index' => 1, 'arrayofkeyval' => array('0' => 'Draft', '1' => 'Validated', '6' => 'Closed'),),
113
        'date_eval' => array('type' => 'date', 'label' => 'DateEval', 'enabled' => 1, 'position' => 502, 'notnull' => 1, 'visible' => 1,),
114
        'fk_user' => array('type' => 'integer:User:user/class/user.class.php:0', 'label' => 'Employee', 'enabled' => 1, 'position' => 504, 'notnull' => 1, 'visible' => 1, 'picto' => 'user', 'css' => 'maxwidth300 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150'),
115
        'fk_job' => array('type' => 'integer:Job:/hrm/class/job.class.php', 'label' => 'JobProfile', 'enabled' => 1, 'position' => 505, 'notnull' => 1, 'visible' => 1, 'picto' => 'jobprofile', 'css' => 'maxwidth300 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150'),
116
    );
117
    public $rowid;
118
    public $ref;
119
    public $label;
120
    public $description;
121
    public $note_public;
122
    public $note_private;
123
    public $date_creation;
124
    public $fk_user_creat;
125
    public $fk_user_modif;
126
    public $import_key;
127
    public $status;
128
    public $date_eval;
129
    public $fk_user;
130
    public $fk_job;
131
    // END MODULEBUILDER PROPERTIES
132
133
134
    // If this object has a subtable with lines
135
136
    /**
137
     * @var string    Name of subtable line
138
     */
139
    public $table_element_line = 'hrm_evaluationdet';
140
141
    /**
142
     * @var string    Field with ID of parent key if this object has a parent
143
     */
144
    public $fk_element = 'fk_evaluation';
145
146
    /**
147
     * @var string    Name of subtable class that manage subtable lines
148
     */
149
    public $class_element_line = 'EvaluationLine';
150
151
    // /**
152
    //  * @var array    List of child tables. To test if we can delete object.
153
    //  */
154
    // protected $childtables = array();
155
156
    /**
157
     * @var string[] List of child tables. To know object to delete on cascade.
158
     *               If name matches '@ClassNAme:FilePathClass;ParentFkFieldName' it will
159
     *               call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object
160
     */
161
    protected $childtablesoncascade = array('@EvaluationLine:hrm/class/evaluationdet.class.php:fk_evaluation');
162
163
    /**
164
     * @var EvaluationLine[]     Array of subtable lines
165
     */
166
    public $lines = array();
167
168
169
170
    /**
171
     * Constructor
172
     *
173
     * @param DoliDB $db Database handler
174
     */
175
    public function __construct(DoliDB $db)
176
    {
177
        global $conf, $langs, $user;
178
179
        $this->db = $db;
180
181
        $this->ismultientitymanaged = 0;
182
        $this->isextrafieldmanaged = 1;
183
184
        if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
185
            $this->fields['rowid']['visible'] = 0;
186
        }
187
        if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
188
            $this->fields['entity']['enabled'] = 0;
189
        }
190
191
        if (!$user->hasRight('hrm', 'evaluation', 'readall')) {
192
            $this->fields['fk_user']['type'] .= ':rowid IN(' . $this->db->sanitize(implode(", ", $user->getAllChildIds(1))) . ')';
193
        }
194
195
        $this->date_eval = dol_now();
196
197
        // Unset fields that are disabled
198
        foreach ($this->fields as $key => $val) {
199
            if (isset($val['enabled']) && empty($val['enabled'])) {
200
                unset($this->fields[$key]);
201
            }
202
        }
203
204
        // Translate some data of arrayofkeyval
205
        if (is_object($langs)) {
206
            foreach ($this->fields as $key => $val) {
207
                if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
208
                    foreach ($val['arrayofkeyval'] as $key2 => $val2) {
209
                        $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
210
                    }
211
                }
212
            }
213
        }
214
    }
215
216
    /**
217
     * Create object into database
218
     *
219
     * @param  User $user      User that creates
220
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
221
     * @return int             Return integer <0 if KO, Id of created object if OK
222
     */
223
    public function create(User $user, $notrigger = 0)
224
    {
225
        $resultcreate = $this->createCommon($user, $notrigger);
226
227
        if ($resultcreate > 0) {
228
            $skillRank = new SkillRank($this->db);
229
            $TRequiredRanks = $skillRank->fetchAll('ASC', 't.rowid', 0, 0, '(fk_object:=:' . ((int) $this->fk_job) . ") AND (objecttype:=:'job')");
230
231
            if (is_array($TRequiredRanks) && !empty($TRequiredRanks)) {
232
                $this->lines = array();
233
                foreach ($TRequiredRanks as $required) {
234
                    $line = new EvaluationLine($this->db);
235
                    $line->fk_evaluation = $resultcreate;
236
                    $line->fk_skill = $required->fk_skill;
237
                    $line->required_rank = $required->rankorder;
238
                    $line->fk_rank = 0;
239
240
                    $res = $line->create($user, $notrigger);
241
                    if ($res > 0) {
242
                        $this->lines[] = $line;
243
                    }
244
                }
245
            }
246
        }
247
248
        return $resultcreate;
249
    }
250
251
    /**
252
     * Clone an object into another one
253
     *
254
     * @param   User    $user       User that creates
255
     * @param   int     $fromid     Id of object to clone
256
     * @return  mixed               New object created, <0 if KO
257
     */
258
    public function createFromClone(User $user, $fromid)
259
    {
260
        global $langs, $extrafields;
261
        $error = 0;
262
263
        dol_syslog(__METHOD__, LOG_DEBUG);
264
265
        $object = new self($this->db);
266
267
        $this->db->begin();
268
269
        // Load source object
270
        $result = $object->fetchCommon($fromid);
271
        if ($result > 0 && !empty($object->table_element_line)) {
272
            $object->fetchLines();
273
        }
274
275
        // get lines so they will be clone
276
        //foreach($this->lines as $line)
277
        //  $line->fetch_optionals();
278
279
        // Reset some properties
280
        unset($object->id);
281
        unset($object->fk_user_creat);
282
        unset($object->import_key);
283
284
        // Clear fields
285
        if (property_exists($object, 'ref')) {
286
            $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_" . $object->ref : $this->fields['ref']['default'];
287
        }
288
        if (property_exists($object, 'label')) {
289
            $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf") . " " . $object->label : $this->fields['label']['default'];
290
        }
291
        if (property_exists($object, 'status')) {
292
            $object->status = self::STATUS_DRAFT;
293
        }
294
        if (property_exists($object, 'date_creation')) {
295
            $object->date_creation = dol_now();
296
        }
297
        if (property_exists($object, 'date_modification')) {
298
            $object->date_modification = null;
299
        }
300
        // ...
301
        // Clear extrafields that are unique
302
        if (is_array($object->array_options) && count($object->array_options) > 0) {
303
            $extrafields->fetch_name_optionals_label($this->table_element);
304
            foreach ($object->array_options as $key => $option) {
305
                $shortkey = preg_replace('/options_/', '', $key);
306
                if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
307
                    //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
308
                    unset($object->array_options[$key]);
309
                }
310
            }
311
        }
312
313
        // Create clone
314
        $object->context['createfromclone'] = 'createfromclone';
315
        $result = $object->createCommon($user);
316
        if ($result < 0) {
317
            $error++;
318
            $this->setErrorsFromObject($object);
319
        }
320
321
        if (!$error) {
322
            // copy internal contacts
323
            if ($this->copy_linked_contact($object, 'internal') < 0) {
324
                $error++;
325
            }
326
        }
327
328
        if (!$error) {
329
            // copy external contacts if same company
330
            if (property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) {
331
                if ($this->copy_linked_contact($object, 'external') < 0) {
332
                    $error++;
333
                }
334
            }
335
        }
336
337
        unset($object->context['createfromclone']);
338
339
        // End
340
        if (!$error) {
341
            $this->db->commit();
342
            return $object;
343
        } else {
344
            $this->db->rollback();
345
            return -1;
346
        }
347
    }
348
349
    /**
350
     * Load object in memory from the database
351
     *
352
     * @param int    $id   Id object
353
     * @param string $ref  Ref
354
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
355
     */
356
    public function fetch($id, $ref = null)
357
    {
358
        $result = $this->fetchCommon($id, $ref);
359
        if ($result > 0 && !empty($this->table_element_line)) {
360
            $this->fetchLines();
361
        }
362
        return $result;
363
    }
364
365
    /**
366
     * Load object lines in memory from the database
367
     *
368
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
369
     */
370
    public function fetchLines()
371
    {
372
        $this->lines = array();
373
374
        $result = $this->fetchLinesCommon();
375
        return $result;
376
    }
377
378
379
    /**
380
     * Load list of objects in memory from the database.
381
     *
382
     * @param  string       $sortorder      Sort Order
383
     * @param  string       $sortfield      Sort field
384
     * @param  int          $limit          limit
385
     * @param  int          $offset         Offset
386
     * @param  string       $filter         Filter as an Universal Search string.
387
     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')'
388
     * @param  string       $filtermode     No more used
389
     * @return array|int                    int <0 if KO, array of pages if OK
390
     */
391
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
392
    {
393
        dol_syslog(__METHOD__, LOG_DEBUG);
394
395
        $records = array();
396
397
        $sql = 'SELECT ';
398
        $sql .= $this->getFieldList('t');
399
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
400
        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
401
            $sql .= ' WHERE t.entity IN (' . getEntity($this->element) . ')';
402
        } else {
403
            $sql .= ' WHERE 1 = 1';
404
        }
405
406
        // Manage filter
407
        $errormessage = '';
408
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
409
        if ($errormessage) {
410
            $this->errors[] = $errormessage;
411
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
412
            return -1;
413
        }
414
415
        if (!empty($sortfield)) {
416
            $sql .= $this->db->order($sortfield, $sortorder);
417
        }
418
        if (!empty($limit)) {
419
            $sql .= ' ' . $this->db->plimit($limit, $offset);
420
        }
421
422
        $resql = $this->db->query($sql);
423
        if ($resql) {
424
            $num = $this->db->num_rows($resql);
425
            $i = 0;
426
            while ($i < ($limit ? min($limit, $num) : $num)) {
427
                $obj = $this->db->fetch_object($resql);
428
429
                $record = new self($this->db);
430
                $record->setVarsFromFetchObj($obj);
431
432
                $records[$record->id] = $record;
433
434
                $i++;
435
            }
436
            $this->db->free($resql);
437
438
            return $records;
439
        } else {
440
            $this->errors[] = 'Error ' . $this->db->lasterror();
441
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
442
443
            return -1;
444
        }
445
    }
446
447
    /**
448
     * Update object into database
449
     *
450
     * @param  User $user      User that modifies
451
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
452
     * @return int             Return integer <0 if KO, >0 if OK
453
     */
454
    public function update(User $user, $notrigger = 0)
455
    {
456
        return $this->updateCommon($user, $notrigger);
457
    }
458
459
    /**
460
     * Delete object in database
461
     *
462
     * @param User  $user       User that deletes
463
     * @param int   $notrigger  0=launch triggers after, 1=disable triggers
464
     * @return int              Return integer <0 if KO, >0 if OK
465
     */
466
    public function delete(User $user, $notrigger = 0)
467
    {
468
        return $this->deleteCommon($user, $notrigger);
469
        //return $this->deleteCommon($user, $notrigger, 1);
470
    }
471
472
    /**
473
     *  Delete a line of object in database
474
     *
475
     *  @param  User    $user       User that delete
476
     *  @param  int     $idline     Id of line to delete
477
     *  @param  int     $notrigger  0=launch triggers after, 1=disable triggers
478
     *  @return int                 >0 if OK, <0 if KO
479
     */
480
    public function deleteLine(User $user, $idline, $notrigger = 0)
481
    {
482
        if ($this->status < 0) {
483
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
484
            return -2;
485
        }
486
487
        return $this->deleteLineCommon($user, $idline, $notrigger);
488
    }
489
490
491
    /**
492
     *  Validate object
493
     *
494
     *  @param      User    $user           User making status change
495
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
496
     *  @return     int                     Return integer <=0 if OK, 0=Nothing done, >0 if KO
497
     */
498
    public function validate($user, $notrigger = 0)
499
    {
500
        global $conf, $langs;
501
502
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
503
504
        $error = 0;
505
506
        // Protection
507
        if ($this->status == self::STATUS_VALIDATED) {
508
            dol_syslog(get_class($this) . "::validate action abandoned: already validated", LOG_WARNING);
509
            return 0;
510
        }
511
512
513
514
        $now = dol_now();
515
516
        $this->db->begin();
517
518
        // Define new ref
519
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
520
            $num = $this->getNextNumRef();
521
        } else {
522
            $num = $this->ref;
523
        }
524
        $this->newref = $num;
525
526
        if (!empty($num)) {
527
            // Validate
528
            $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
529
            $sql .= " SET ref = '" . $this->db->escape($num) . "',";
530
            $sql .= " status = " . self::STATUS_VALIDATED;
531
            if (!empty($this->fields['date_validation'])) {
532
                $sql .= ", date_validation = '" . $this->db->idate($now) . "'";
533
            }
534
            if (!empty($this->fields['fk_user_valid'])) {
535
                $sql .= ", fk_user_valid = " . ((int) $user->id);
536
            }
537
            $sql .= " WHERE rowid = " . ((int) $this->id);
538
539
            dol_syslog(get_class($this) . "::validate()", LOG_DEBUG);
540
            $resql = $this->db->query($sql);
541
            if (!$resql) {
542
                dol_print_error($this->db);
543
                $this->error = $this->db->lasterror();
544
                $error++;
545
            }
546
547
            if (!$error && !$notrigger) {
548
                // Call trigger
549
                $result = $this->call_trigger('HRM_EVALUATION_VALIDATE', $user);
550
                if ($result < 0) {
551
                    $error++;
552
                }
553
                // End call triggers
554
            }
555
        }
556
557
        if (!$error) {
558
            $this->oldref = $this->ref;
559
560
            // Rename directory if dir was a temporary ref
561
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
562
                // Now we rename also files into index
563
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'evaluation/" . $this->db->escape($this->newref) . "'";
564
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'evaluation/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
565
                $resql = $this->db->query($sql);
566
                if (!$resql) {
567
                    $error++;
568
                    $this->error = $this->db->lasterror();
569
                }
570
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'evaluation/" . $this->db->escape($this->newref) . "'";
571
                $sql .= " WHERE filepath = 'evaluation/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
572
                $resql = $this->db->query($sql);
573
                if (!$resql) {
574
                    $error++;
575
                    $this->error = $this->db->lasterror();
576
                }
577
578
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
579
                $oldref = dol_sanitizeFileName($this->ref);
580
                $newref = dol_sanitizeFileName($num);
581
                $dirsource = $conf->hrm->dir_output . '/evaluation/' . $oldref;
582
                $dirdest = $conf->hrm->dir_output . '/evaluation/' . $newref;
583
                if (!$error && file_exists($dirsource)) {
584
                    dol_syslog(get_class($this) . "::validate() rename dir " . $dirsource . " into " . $dirdest);
585
586
                    if (@rename($dirsource, $dirdest)) {
587
                        dol_syslog("Rename ok");
588
                        // Rename docs starting with $oldref with $newref
589
                        $listoffiles = dol_dir_list($conf->hrm->dir_output . '/evaluation/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
590
                        foreach ($listoffiles as $fileentry) {
591
                            $dirsource = $fileentry['name'];
592
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
593
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
594
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
595
                            @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

595
                            /** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
596
                        }
597
                    }
598
                }
599
            }
600
        }
601
602
        // Set new ref and current status
603
        if (!$error) {
604
            $this->ref = $num;
605
            $this->status = self::STATUS_VALIDATED;
606
        }
607
608
        if (!$error) {
609
            $this->db->commit();
610
            return 1;
611
        } else {
612
            $this->db->rollback();
613
            return -1;
614
        }
615
    }
616
617
    /**
618
     *      Get the last evaluation by date for the user assigned
619
     *
620
     *       @param int $fk_user ID of user we need to get last eval
621
     *       @return Evaluation|null
622
     */
623
    public function getLastEvaluationForUser($fk_user)
624
    {
625
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "hrm_evaluation ";
626
        $sql .= "WHERE fk_user=" . ((int) $fk_user) . " ";
627
        $sql .= "ORDER BY date_eval DESC ";
628
        $sql .= "LIMIT 1 ";
629
630
        $res = $this->db->query($sql);
631
        if (!$res) {
632
            dol_print_error($this->db);
633
        }
634
635
        $Tab = $this->db->fetch_object($res);
636
637
        if (empty($Tab)) {
638
            return null;
639
        } else {
640
            $evaluation = new Evaluation($this->db);
641
            $evaluation->fetch($Tab->rowid);
642
643
            return $evaluation;
644
        }
645
    }
646
647
648
    /**
649
     *  Set draft status
650
     *
651
     *  @param  User    $user           Object user that modify
652
     *  @param  int     $notrigger      1=Does not execute triggers, 0=Execute triggers
653
     *  @return int                     Return integer <0 if KO, >0 if OK
654
     */
655
    public function setDraft($user, $notrigger = 0)
656
    {
657
        // Protection
658
        if ($this->status <= self::STATUS_DRAFT) {
659
            return 0;
660
        }
661
662
        return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'HRM_EVALUATION_UNVALIDATE');
663
    }
664
665
    /**
666
     *  Set cancel status
667
     *
668
     *  @param  User    $user           Object user that modify
669
     *  @param  int     $notrigger      1=Does not execute triggers, 0=Execute triggers
670
     *  @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
671
     */
672
    public function cancel($user, $notrigger = 0)
673
    {
674
        // Protection
675
        if ($this->status != self::STATUS_VALIDATED) {
676
            return 0;
677
        }
678
679
        return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'HRM_EVALUATION_CANCEL');
680
    }
681
682
    /**
683
     *  Set back to validated status
684
     *
685
     *  @param  User    $user           Object user that modify
686
     *  @param  int     $notrigger      1=Does not execute triggers, 0=Execute triggers
687
     *  @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
688
     */
689
    public function reopen($user, $notrigger = 0)
690
    {
691
        // Protection
692
        if ($this->status != self::STATUS_CANCELED) {
693
            return 0;
694
        }
695
696
        return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'HRM_EVALUATION_REOPEN');
697
    }
698
699
    /**
700
     *  Return a link to the object card (with optionally the picto)
701
     *
702
     *  @param  int     $withpicto                  Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
703
     *  @param  string  $option                     On what the link point to ('nolink', ...)
704
     *  @param  int     $notooltip                  1=Disable tooltip
705
     *  @param  string  $morecss                    Add more css on link
706
     *  @param  int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
707
     *  @return string                              String with URL
708
     */
709
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
710
    {
711
        global $conf, $langs, $hookmanager;
712
713
        if (!empty($conf->dol_no_mouse_hover)) {
714
            $notooltip = 1; // Force disable tooltips
715
        }
716
717
        $result = '';
718
719
        $label = img_picto('', $this->picto) . ' <u>' . $langs->trans("Evaluation") . '</u>';
720
        if (isset($this->status)) {
721
            $label .= ' ' . $this->getLibStatut(5);
722
        }
723
        $label .= '<br>';
724
        $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
725
726
        $url = dol_buildpath('/hrm/evaluation_card.php', 1) . '?id=' . $this->id;
727
728
        if ($option != 'nolink') {
729
            // Add param to save lastsearch_values or not
730
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
731
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
732
                $add_save_lastsearch_values = 1;
733
            }
734
            if ($add_save_lastsearch_values) {
735
                $url .= '&save_lastsearch_values=1';
736
            }
737
        }
738
739
        $linkclose = '';
740
        if (empty($notooltip)) {
741
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
742
                $label = $langs->trans("ShowEvaluation");
743
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
744
            }
745
            $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
746
            $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
747
        } else {
748
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
749
        }
750
751
        if ($option == 'nolink') {
752
            $linkstart = '<span';
753
        } else {
754
            $linkstart = '<a href="' . $url . '"';
755
        }
756
        $linkstart .= $linkclose . '>';
757
        if ($option == 'nolink') {
758
            $linkend = '</span>';
759
        } else {
760
            $linkend = '</a>';
761
        }
762
763
        $result .= $linkstart;
764
765
        if (empty($this->showphoto_on_popup)) {
766
            if ($withpicto) {
767
                $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
768
            }
769
        } else {
770
            if ($withpicto) {
771
                require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
772
773
                list($class, $module) = explode('@', $this->picto);
774
                $upload_dir = $conf->$module->multidir_output[$conf->entity] . "/$class/" . dol_sanitizeFileName($this->ref);
775
                $filearray = dol_dir_list($upload_dir, "files");
776
                $filename = $filearray[0]['name'];
777
                if (!empty($filename)) {
778
                    $pospoint = strpos($filearray[0]['name'], '.');
779
780
                    $pathtophoto = $class . '/' . $this->ref . '/thumbs/' . substr($filename, 0, $pospoint) . '_mini' . substr($filename, $pospoint);
781
                    if (!getDolGlobalString(strtoupper($module . '_' . $class) . '_FORMATLISTPHOTOSASUSERS')) {
782
                        $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo' . $module . '" alt="No photo" border="0" src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div></div>';
783
                    } else {
784
                        $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div>';
785
                    }
786
787
                    $result .= '</div>';
788
                } else {
789
                    $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
790
                }
791
            }
792
        }
793
794
        if ($withpicto != 2) {
795
            $result .= $this->ref;
796
        }
797
798
        $result .= $linkend;
799
        //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
800
801
        global $action, $hookmanager;
802
        $hookmanager->initHooks(array('evaluationdao'));
803
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
804
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
805
        if ($reshook > 0) {
806
            $result = $hookmanager->resPrint;
807
        } else {
808
            $result .= $hookmanager->resPrint;
809
        }
810
811
        return $result;
812
    }
813
814
    /**
815
     *  Return the label of the status
816
     *
817
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
818
     *  @return string                 Label of status
819
     */
820
    public function getLibStatut($mode = 0)
821
    {
822
        return $this->LibStatut($this->status, $mode);
823
    }
824
825
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
826
    /**
827
     *  Return the status
828
     *
829
     *  @param  int     $status        Id status
830
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
831
     *  @return string                 Label of status
832
     */
833
    public function LibStatut($status, $mode = 0)
834
    {
835
		// phpcs:enable
836
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
837
            global $langs;
838
            //$langs->load("hrm");
839
            $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
840
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
841
            $this->labelStatus[self::STATUS_CLOSED] = $langs->transnoentitiesnoconv('Closed');
842
            $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
843
            $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
844
            $this->labelStatusShort[self::STATUS_CLOSED] = $langs->transnoentitiesnoconv('Closed');
845
        }
846
847
        $statusType = 'status' . $status;
848
        //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
849
        if ($status == self::STATUS_CANCELED) {
850
            $statusType = 'status6';
851
        }
852
853
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
854
    }
855
856
    /**
857
     *  Load the info information in the object
858
     *
859
     *  @param  int     $id       Id of object
860
     *  @return void
861
     */
862
    public function info($id)
863
    {
864
        $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
865
        $sql .= ' fk_user_creat, fk_user_modif';
866
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
867
        $sql .= ' WHERE t.rowid = ' . ((int) $id);
868
        $result = $this->db->query($sql);
869
        if ($result) {
870
            if ($this->db->num_rows($result)) {
871
                $obj = $this->db->fetch_object($result);
872
873
                $this->id = $obj->rowid;
874
875
                $this->user_creation_id = $obj->fk_user_creat;
876
                $this->user_modification_id = $obj->fk_user_modif;
877
                $this->date_creation     = $this->db->jdate($obj->datec);
878
                $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
879
            }
880
881
            $this->db->free($result);
882
        } else {
883
            dol_print_error($this->db);
884
        }
885
    }
886
887
    /**
888
     * Initialise object with example values
889
     * Id must be 0 if object instance is a specimen
890
     *
891
     * @return int
892
     */
893
    public function initAsSpecimen()
894
    {
895
        // Set here init that are not commonf fields
896
        // $this->property1 = ...
897
        // $this->property2 = ...
898
899
        return $this->initAsSpecimenCommon();
900
    }
901
902
    /**
903
     *  Create an array of lines
904
     *
905
     *  @return array|int       array of lines if OK, <0 if KO
906
     */
907
    public function getLinesArray()
908
    {
909
        $this->lines = array();
910
911
        $objectline = new EvaluationLine($this->db);
912
        $result = $objectline->fetchAll('ASC', '', 0, 0, '(fk_evaluation:=:' . ((int) $this->id) . ')');
913
914
        if (is_numeric($result)) {
915
            $this->setErrorsFromObject($objectline);
916
            return $result;
917
        } else {
918
            $this->lines = $result;
919
            return $this->lines;
920
        }
921
    }
922
923
    /**
924
     *  Returns the reference to the following non used object depending on the active numbering module.
925
     *
926
     *  @return string              Object free reference
927
     */
928
    public function getNextNumRef()
929
    {
930
        global $langs, $conf;
931
        $langs->load("hrm");
932
933
        if (!getDolGlobalString('HRMTEST_EVALUATION_ADDON')) {
934
            $conf->global->HRMTEST_EVALUATION_ADDON = 'mod_evaluation_standard';
935
        }
936
937
        if (getDolGlobalString('HRMTEST_EVALUATION_ADDON')) {
938
            $mybool = false;
939
940
            $file = getDolGlobalString('HRMTEST_EVALUATION_ADDON') . ".php";
941
            $classname = getDolGlobalString('HRMTEST_EVALUATION_ADDON');
942
943
            // Include file with class
944
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
945
            foreach ($dirmodels as $reldir) {
946
                $dir = dol_buildpath($reldir . "core/modules/hrm/");
947
948
                // Load file with numbering class (if found)
949
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
950
            }
951
952
            if ($mybool === false) {
953
                dol_print_error(null, "Failed to include file " . $file);
954
                return '';
955
            }
956
957
            if (class_exists($classname)) {
958
                $obj = new $classname();
959
                $numref = $obj->getNextValue($this);
960
961
                if ($numref != '' && $numref != '-1') {
962
                    return $numref;
963
                } else {
964
                    $this->error = $obj->error;
965
                    //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
966
                    return "";
967
                }
968
            } else {
969
                print $langs->trans("Error") . " " . $langs->trans("ClassNotFound") . ' ' . $classname;
970
                return "";
971
            }
972
        } else {
973
            print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
974
            return "";
975
        }
976
    }
977
978
    /**
979
     *  Create a document onto disk according to template module.
980
     *
981
     *  @param      string      $modele         Force template to use ('' to not force)
982
     *  @param      Translate   $outputlangs    object lang a utiliser pour traduction
983
     *  @param      int         $hidedetails    Hide details of lines
984
     *  @param      int         $hidedesc       Hide description
985
     *  @param      int         $hideref        Hide ref
986
     *  @param      null|array  $moreparams     Array to provide more information
987
     *  @return     int                         0 if KO, 1 if OK
988
     */
989
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
990
    {
991
        global $conf, $langs;
992
993
        $result = 0;
994
995
        $langs->load("hrm");
996
997
        if (!dol_strlen($modele)) {
998
            $modele = 'standard';
999
1000
            if (!empty($this->model_pdf)) {
1001
                $modele = $this->model_pdf;
1002
            } elseif (getDolGlobalString('EVALUATION_ADDON_PDF')) {
1003
                $modele = getDolGlobalString('EVALUATION_ADDON_PDF');
1004
            }
1005
        }
1006
1007
        $modelpath = "core/modules/hrm/doc/";
1008
1009
        if (!empty($modele)) {
1010
            $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1011
        }
1012
1013
        return $result;
1014
    }
1015
1016
    /**
1017
     *  Return clicable link of object (with eventually picto)
1018
     *
1019
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
1020
     *  @param      array       $arraydata              Array of data
1021
     *  @return     string                              HTML Code for Kanban thumb.
1022
     */
1023
    public function getKanbanView($option = '', $arraydata = null)
1024
    {
1025
        global $selected, $langs;
1026
1027
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1028
1029
        $return = '<div class="box-flex-item box-flex-grow-zero">';
1030
        $return .= '<div class="info-box info-box-sm">';
1031
        $return .= '<span class="info-box-icon bg-infobox-action">';
1032
        $return .= img_picto('', $this->picto);
1033
        $return .= '</span>';
1034
        $return .= '<div class="info-box-content">';
1035
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>';
1036
        $return .= '<input class="fright" id="cb' . $this->id . '" class="flat checkforselect" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
1037
        if (!empty($arraydata['user'])) {
1038
            $return .= '<br><span class="info-box-label ">' . $arraydata['user'] . '</span>';
1039
        }
1040
        if (!empty($arraydata['job'])) {
1041
            $return .= '<br><span class="info-box-label ">' . $arraydata['job'] . '</span>';
1042
        }
1043
        if (method_exists($this, 'getLibStatut')) {
1044
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
1045
        }
1046
        $return .= '</div>';
1047
        $return .= '</div>';
1048
        $return .= '</div>';
1049
        return $return;
1050
    }
1051
}
1052