Passed
Push — EXTRACT_CLASSES ( 9f3ede...ff35ec )
by Rafael
76:09 queued 20:57
created

Job   F

Complexity

Total Complexity 155

Size/Duplication

Total Lines 1053
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 409
dl 0
loc 1053
rs 2
c 0
b 0
f 0
wmc 155

25 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 35 13
A setDraft() 0 15 2
A update() 0 3 1
A info() 0 22 4
A fetch() 0 7 3
F getNomUrl() 0 103 34
A fetchLines() 0 6 1
A getLinesArray() 0 13 2
F createFromClone() 0 88 22
B getNextNumRef() 0 47 9
A reopen() 0 15 2
B fetchAll() 0 53 9
F validate() 0 122 22
A generateDocument() 0 26 6
A getSkillRankForJob() 0 24 4
A initAsSpecimen() 0 7 1
A getLastJobForUser() 0 11 2
A cancel() 0 15 2
A create() 0 7 1
A deleteLine() 0 8 2
A getForUser() 0 11 2
A delete() 0 3 1
B getKanbanView() 0 28 8
A getLibStatut() 0 3 1
A LibStatut() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Job 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 Job, 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\Core\Base\CommonObject;
29
use DoliDB;
30
31
/**
32
 * \file        class/job.class.php
33
 * \ingroup     hrm
34
 * \brief       This file is a CRUD class file for Job (Create/Read/Update/Delete)
35
 */
36
37
/**
38
 * Class for Job
39
 */
40
class Job extends CommonObject
41
{
42
    /**
43
     * @var string ID of module.
44
     */
45
    public $module = 'hrm';
46
47
    /**
48
     * @var string ID to identify managed object.
49
     */
50
    public $element = 'job';
51
52
    /**
53
     * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management.
54
     */
55
    public $table_element = 'hrm_job';
56
57
    /**
58
     * @var string String with name of icon for job. Must be the part after the 'object_' into object_job.png
59
     */
60
    public $picto = 'technic';
61
62
63
    const STATUS_DRAFT = 0;
64
    const STATUS_VALIDATED = 1;
65
    const STATUS_CANCELED = 9;
66
67
68
    /**
69
     *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', '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')
70
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
71
     *  'label' the translation key.
72
     *  'picto' is code of a picto to show before value in forms
73
     *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalString("MY_SETUP_PARAM")')
74
     *  'position' is the sort order of field.
75
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
76
     *  '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)
77
     *  'noteditable' says if field is not editable (1 or 0)
78
     *  '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.
79
     *  'index' if we want an index in database.
80
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
81
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
82
     *  '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).
83
     *  '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'
84
     *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
85
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
86
     *  '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.
87
     *  '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'
88
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
89
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
90
     *
91
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
92
     */
93
94
    // BEGIN MODULEBUILDER PROPERTIES
95
    /**
96
     * @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...
97
     */
98
    public $fields = array(
99
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
100
        'label' => array('type' => 'varchar(128)', 'label' => 'Label', 'enabled' => 1, 'position' => 20, 'notnull' => 1, 'visible' => 1, 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'comment' => "Label of object"),
101
        'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'position' => 21, 'notnull' => 0, 'visible' => 1,),
102
        'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'visible' => 2,),
103
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 501, 'notnull' => 0, 'visible' => 2,),
104
        'deplacement' => array('type' => 'select', 'required' => 1,'label' => 'NeedBusinessTravels', 'enabled' => 1, 'position' => 90, 'notnull' => 1, 'visible' => 1, 'arrayofkeyval' => array(0 => "No", 1 => "Yes"), 'default' => '0'),
105
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'position' => 70, 'notnull' => 0, 'visible' => 0,),
106
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 71, 'notnull' => 0, 'visible' => 0,),
107
        'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'position' => 510, 'notnull' => 1, 'visible' => -2, 'foreignkey' => 'user.rowid',),
108
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'position' => 511, 'notnull' => -1, 'visible' => -2,),
109
    );
110
    public $rowid;
111
    public $ref;
112
    public $label;
113
    public $description;
114
    public $date_creation;
115
116
    public $deplacement;
117
    public $fk_user_creat;
118
    public $fk_user_modif;
119
    // END MODULEBUILDER PROPERTIES
120
121
122
    // If this object has a subtable with lines
123
124
    // /**
125
    //  * @var string    Name of subtable line
126
    //  */
127
    // public $table_element_line = 'hrm_jobline';
128
129
    /**
130
     * @var string    Field with ID of parent key if this object has a parent
131
     */
132
    public $fk_element = 'fk_job';
133
134
    // /**
135
    //  * @var string    Name of subtable class that manage subtable lines
136
    //  */
137
    // public $class_element_line = 'Jobline';
138
139
    /**
140
     * @var array<string,string[]>  List of child tables. To test if we can delete object.
141
     */
142
    protected $childtables = array(
143
        'hrm_evaluation' => ['name' => 'Evaluation'],
144
        'hrm_job_user' => ['name' => 'Job'],
145
    );
146
147
    /**
148
     * @var string[]    List of child tables. To know object to delete on cascade.
149
     *               If name matches '@ClassNAme:FilePathClass:ParentFkFieldName' it will
150
     *               call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object
151
     */
152
    protected $childtablesoncascade = array("@SkillRank:hrm/class/skillrank.class.php:fk_object:(objecttype:=:'job')");
153
154
    // /**
155
    //  * @var JobLine[]     Array of subtable lines
156
    //  */
157
    // public $lines = array();
158
159
160
161
    /**
162
     * Constructor
163
     *
164
     * @param DoliDB $db Database handler
165
     */
166
    public function __construct(DoliDB $db)
167
    {
168
        global $conf, $langs;
169
170
        $this->db = $db;
0 ignored issues
show
Documentation Bug introduced by
It seems like $db of type DoliDB is incompatible with the declared type Dolibarr\Core\Base\DoliDB of property $db.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
171
172
        $this->ismultientitymanaged = 0;
173
        $this->isextrafieldmanaged = 1;
174
175
        if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
176
            $this->fields['rowid']['visible'] = 0;
177
        }
178
        if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
179
            $this->fields['entity']['enabled'] = 0;
180
        }
181
182
        // Example to show how to set values of fields definition dynamically
183
        /*if ($user->rights->hrm->job->read) {
184
            $this->fields['myfield']['visible'] = 1;
185
            $this->fields['myfield']['noteditable'] = 0;
186
        }*/
187
188
        // Unset fields that are disabled
189
        foreach ($this->fields as $key => $val) {
190
            if (isset($val['enabled']) && empty($val['enabled'])) {
191
                unset($this->fields[$key]);
192
            }
193
        }
194
195
        // Translate some data of arrayofkeyval
196
        if (is_object($langs)) {
197
            foreach ($this->fields as $key => $val) {
198
                if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
199
                    foreach ($val['arrayofkeyval'] as $key2 => $val2) {
200
                        $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
201
                    }
202
                }
203
            }
204
        }
205
    }
206
207
    /**
208
     * Create object into database
209
     *
210
     * @param  User $user      User that creates
211
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
212
     * @return int             Return integer <0 if KO, Id of created object if OK
213
     */
214
    public function create(User $user, $notrigger = 0)
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Hrm\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
215
    {
216
        $resultcreate = $this->createCommon($user, $notrigger);
217
218
        //$resultvalidate = $this->validate($user, $notrigger);
219
220
        return $resultcreate;
221
    }
222
223
    /**
224
     * Clone an object into another one
225
     *
226
     * @param   User    $user       User that creates
227
     * @param   int     $fromid     Id of object to clone
228
     * @return  mixed               New object created, <0 if KO
229
     */
230
    public function createFromClone(User $user, $fromid)
231
    {
232
        global $langs, $extrafields;
233
        $error = 0;
234
235
        dol_syslog(__METHOD__, LOG_DEBUG);
236
237
        $object = new self($this->db);
238
239
        $this->db->begin();
240
241
        // Load source object
242
        $result = $object->fetchCommon($fromid);
243
        if ($result > 0 && !empty($object->table_element_line)) {
244
            $object->fetchLines();
245
        }
246
247
        // get lines so they will be clone
248
        //foreach($this->lines as $line)
249
        //  $line->fetch_optionals();
250
251
        // Reset some properties
252
        unset($object->id);
253
        unset($object->fk_user_creat);
254
        unset($object->import_key);
255
256
        // Clear fields
257
        if (property_exists($object, 'ref')) {
258
            $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_" . $object->ref : $this->fields['ref']['default'];
259
        }
260
        if (property_exists($object, 'label')) {
261
            $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf") . " " . $object->label : $this->fields['label']['default'];
262
        }
263
        if (property_exists($object, 'status')) {
264
            $object->status = self::STATUS_DRAFT;
265
        }
266
        if (property_exists($object, 'date_creation')) {
267
            $object->date_creation = dol_now();
268
        }
269
        if (property_exists($object, 'date_modification')) {
270
            $object->date_modification = null;
271
        }
272
        // ...
273
        // Clear extrafields that are unique
274
        if (is_array($object->array_options) && count($object->array_options) > 0) {
275
            $extrafields->fetch_name_optionals_label($this->table_element);
276
            foreach ($object->array_options as $key => $option) {
277
                $shortkey = preg_replace('/options_/', '', $key);
278
                if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
279
                    //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
280
                    unset($object->array_options[$key]);
281
                }
282
            }
283
        }
284
285
        // Create clone
286
        $object->context['createfromclone'] = 'createfromclone';
287
        $result = $object->createCommon($user);
288
        if ($result < 0) {
289
            $error++;
290
            $this->setErrorsFromObject($object);
291
        }
292
293
        if (!$error) {
294
            // copy internal contacts
295
            if ($this->copy_linked_contact($object, 'internal') < 0) {
296
                $error++;
297
            }
298
        }
299
300
        if (!$error) {
301
            // copy external contacts if same company
302
            if (property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) {
303
                if ($this->copy_linked_contact($object, 'external') < 0) {
304
                    $error++;
305
                }
306
            }
307
        }
308
309
        unset($object->context['createfromclone']);
310
311
        // End
312
        if (!$error) {
313
            $this->db->commit();
314
            return $object;
315
        } else {
316
            $this->db->rollback();
317
            return -1;
318
        }
319
    }
320
321
    /**
322
     * Load object in memory from the database
323
     *
324
     * @param int    $id   Id object
325
     * @param string $ref  Ref
326
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
327
     */
328
    public function fetch($id, $ref = null)
329
    {
330
        $result = $this->fetchCommon($id, $ref);
331
        if ($result > 0 && !empty($this->table_element_line)) {
332
            $this->fetchLines();
333
        }
334
        return $result;
335
    }
336
337
    /**
338
     * Load object lines in memory from the database
339
     *
340
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
341
     */
342
    public function fetchLines()
343
    {
344
        $this->lines = array();
345
346
        $result = $this->fetchLinesCommon();
347
        return $result;
348
    }
349
350
351
    /**
352
     * Load list of objects in memory from the database.
353
     *
354
     * @param  string       $sortorder      Sort Order
355
     * @param  string       $sortfield      Sort field
356
     * @param  int          $limit          limit
357
     * @param  int          $offset         Offset
358
     * @param  string       $filter         Filter as an Universal Search string.
359
     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')'
360
     * @param  string       $filtermode     No more used
361
     * @return array|int                    int <0 if KO, array of pages if OK
362
     */
363
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
364
    {
365
        dol_syslog(__METHOD__, LOG_DEBUG);
366
367
        $records = array();
368
369
        $sql = 'SELECT ';
370
        $sql .= $this->getFieldList('t');
371
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
372
        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
373
            $sql .= ' WHERE t.entity IN (' . getEntity($this->element) . ')';
374
        } else {
375
            $sql .= ' WHERE 1 = 1';
376
        }
377
378
        // Manage filter
379
        $errormessage = '';
380
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
381
        if ($errormessage) {
382
            $this->errors[] = $errormessage;
383
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
384
            return -1;
385
        }
386
387
        if (!empty($sortfield)) {
388
            $sql .= $this->db->order($sortfield, $sortorder);
389
        }
390
        if (!empty($limit)) {
391
            $sql .= ' ' . $this->db->plimit($limit, $offset);
392
        }
393
394
        $resql = $this->db->query($sql);
395
        if ($resql) {
396
            $num = $this->db->num_rows($resql);
397
            $i = 0;
398
            while ($i < ($limit ? min($limit, $num) : $num)) {
399
                $obj = $this->db->fetch_object($resql);
400
401
                $record = new self($this->db);
402
                $record->setVarsFromFetchObj($obj);
403
404
                $records[$record->id] = $record;
405
406
                $i++;
407
            }
408
            $this->db->free($resql);
409
410
            return $records;
411
        } else {
412
            $this->errors[] = 'Error ' . $this->db->lasterror();
413
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
414
415
            return -1;
416
        }
417
    }
418
419
    /**
420
     * Update object into database
421
     *
422
     * @param  User $user      User that modifies
423
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
424
     * @return int             Return integer <0 if KO, >0 if OK
425
     */
426
    public function update(User $user, $notrigger = 0)
427
    {
428
        return $this->updateCommon($user, $notrigger);
429
    }
430
431
    /**
432
     * Delete object in database
433
     *
434
     * @param User  $user       User that deletes
435
     * @param int   $notrigger  0=launch triggers after, 1=disable triggers
436
     * @return int              Return integer <0 if KO, >0 if OK
437
     */
438
    public function delete(User $user, $notrigger = 0)
439
    {
440
        return $this->deleteCommon($user, $notrigger);
441
        //return $this->deleteCommon($user, $notrigger, 1);
442
    }
443
444
    /**
445
     *  Delete a line of object in database
446
     *
447
     *  @param  User    $user       User that delete
448
     *  @param  int     $idline     Id of line to delete
449
     *  @param  int     $notrigger  0=launch triggers after, 1=disable triggers
450
     *  @return int                 >0 if OK, <0 if KO
451
     */
452
    public function deleteLine(User $user, $idline, $notrigger = 0)
453
    {
454
        if ($this->status < 0) {
455
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
456
            return -2;
457
        }
458
459
        return $this->deleteLineCommon($user, $idline, $notrigger);
460
    }
461
462
463
    /**
464
     *  Validate object
465
     *
466
     *  @param      User    $user           User making status change
467
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
468
     *  @return     int                     Return integer <=0 if OK, 0=Nothing done, >0 if KO
469
     */
470
    public function validate($user, $notrigger = 0)
471
    {
472
        global $conf, $langs;
473
474
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
475
476
        $error = 0;
477
478
        // Protection
479
        if ($this->status == self::STATUS_VALIDATED) {
480
            dol_syslog(get_class($this) . "::validate action abandoned: already validated", LOG_WARNING);
481
            return 0;
482
        }
483
484
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->hrm->job->write))
485
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->hrm->job->job_advance->validate))))
486
         {
487
         $this->error='NotEnoughPermissions';
488
         dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
489
         return -1;
490
         }*/
491
492
        $now = dol_now();
493
494
        $this->db->begin();
495
496
        // Define new ref
497
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
498
            $num = $this->getNextNumRef();
499
        } else {
500
            $num = $this->ref;
501
        }
502
        $this->newref = $num;
503
504
        if (!empty($num)) {
505
            // Validate
506
            $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
507
            $sql .= " SET ref = '" . $this->db->escape($num) . "',";
508
            $sql .= " status = " . self::STATUS_VALIDATED;
509
            if (!empty($this->fields['date_validation'])) {
510
                $sql .= ", date_validation = '" . $this->db->idate($now) . "'";
511
            }
512
            if (!empty($this->fields['fk_user_valid'])) {
513
                $sql .= ", fk_user_valid = " . ((int) $user->id);
514
            }
515
            $sql .= " WHERE rowid = " . ((int) $this->id);
516
517
            dol_syslog(get_class($this) . "::validate()", LOG_DEBUG);
518
            $resql = $this->db->query($sql);
519
            if (!$resql) {
520
                dol_print_error($this->db);
521
                $this->error = $this->db->lasterror();
522
                $error++;
523
            }
524
525
            if (!$error && !$notrigger) {
526
                // Call trigger
527
                $result = $this->call_trigger('HRM_JOB_VALIDATE', $user);
528
                if ($result < 0) {
529
                    $error++;
530
                }
531
                // End call triggers
532
            }
533
        }
534
535
        if (!$error) {
536
            $this->oldref = $this->ref;
537
538
            // Rename directory if dir was a temporary ref
539
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
540
                // Now we rename also files into index
541
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'job/" . $this->db->escape($this->newref) . "'";
542
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'job/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
543
                $resql = $this->db->query($sql);
544
                if (!$resql) {
545
                    $error++;
546
                    $this->error = $this->db->lasterror();
547
                }
548
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'job/" . $this->db->escape($this->newref) . "'";
549
                $sql .= " WHERE filepath = 'job/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
550
                $resql = $this->db->query($sql);
551
                if (!$resql) {
552
                    $error++;
553
                    $this->error = $this->db->lasterror();
554
                }
555
556
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
557
                $oldref = dol_sanitizeFileName($this->ref);
558
                $newref = dol_sanitizeFileName($num);
559
                $dirsource = $conf->hrm->dir_output . '/job/' . $oldref;
560
                $dirdest = $conf->hrm->dir_output . '/job/' . $newref;
561
                if (!$error && file_exists($dirsource)) {
562
                    dol_syslog(get_class($this) . "::validate() rename dir " . $dirsource . " into " . $dirdest);
563
564
                    if (@rename($dirsource, $dirdest)) {
565
                        dol_syslog("Rename ok");
566
                        // Rename docs starting with $oldref with $newref
567
                        $listoffiles = dol_dir_list($conf->hrm->dir_output . '/job/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
568
                        foreach ($listoffiles as $fileentry) {
569
                            $dirsource = $fileentry['name'];
570
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
571
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
572
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
573
                            @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

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