dolibarr 20.0.2
compare.php
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
4 * Copyright (C) 2021 Greg Rastklan <greg.rastklan@atm-consulting.fr>
5 * Copyright (C) 2021 Jean-Pascal BOUDET <jean-pascal.boudet@atm-consulting.fr>
6 * Copyright (C) 2021 Grégory BLEMAND <gregory.blemand@atm-consulting.fr>
7 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23
24 * \file htdocs/hrm/compare.php
25 * \ingroup hrm
26 * \brief This file compares skills of user groups
27 *
28 * Displays a table in three parts.
29 * 1- the left part displays the list of users for the selected group 1.
30 *
31 * 2- the central part displays the skills. Display of the maximum score for this group and the number of occurrences.
32 *
33 * 3- the right part displays the members of group 2 or the job to be compared
34 */
35
36
37// Load Dolibarr environment
38require_once '../main.inc.php';
39require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
40require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
41require_once DOL_DOCUMENT_ROOT . '/hrm/class/skill.class.php';
42require_once DOL_DOCUMENT_ROOT . '/hrm/class/job.class.php';
43require_once DOL_DOCUMENT_ROOT . '/hrm/class/evaluation.class.php';
44require_once DOL_DOCUMENT_ROOT . '/hrm/class/position.class.php';
45require_once DOL_DOCUMENT_ROOT . '/hrm/lib/hrm.lib.php';
46
47
48// Load translation files required by the page
49$langs->load('hrm');
50
51$job = new Job($db);
52
53// Permissions
54$permissiontoread = $user->hasRight('hrm', 'evaluation', 'read') || $user->hasRight('hrm', 'compare_advance', 'read');
55$permissiontoadd = 0;
56
57if (empty($conf->hrm->enabled)) {
59}
60if (!$permissiontoread || ($action === 'create' && !$permissiontoadd)) {
62}
63
64
65/*
66 * View
67 */
68
69$css = array('/hrm/css/style.css');
70
71llxHeader('', $langs->trans('SkillComparison'), '', '', 0, 0, '', $css);
72
73$head = array();
74
75$h = 0;
76$head[$h][0] = $_SERVER["PHP_SELF"];
77$head[$h][1] = $langs->trans("SkillComparison");
78$head[$h][2] = 'compare';
79
80print dol_get_fiche_head($head, 'compare', '', 1);
81
82?>
83 <script type="text/javascript">
84
85 $(document).ready(function () {
86
87 $("li[fk_user]").click(function () {
88
89 if ($(this).hasClass('disabled')) {
90 $(this).removeClass('disabled');
91 } else {
92 $(this).addClass('disabled');
93 }
94
95
96 var $userl = $(this).closest('ul');
97 var listname = $userl.attr('name');
98
99 var TId = [];
100
101 $userl.find('li').each(function (i, item) {
102
103 if ($(item).hasClass('disabled')) {
104 TId.push($(item).attr('fk_user'));
105 }
106
107 });
108
109 $('#' + listname + '_excluded_id').val(TId.join(','));
110
111 });
112
113 });
114
115
116 </script>
117
118
119<?php
120
121$fk_usergroup2 = 0;
122$fk_job = (int) GETPOST('fk_job');
123if ($fk_job <= 0) {
124 $fk_usergroup2 = GETPOST('fk_usergroup2');
125}
126
127$fk_usergroup1 = GETPOST('fk_usergroup1');
128
129?>
130
131
132 <div class="fichecenter">
133 <form action="<?php echo $_SERVER['PHP_SELF'] ?>">
134
135 <div class="tabBar tabBarWithBottom">
136 <div class="fichehalfleft">
137 <table class="border tableforfield" width="100%">
138 <tr>
139 <td><?php
140 print $langs->trans('group1ToCompare').'</td><td>';
141 print img_picto('', 'group', 'class="pictofixedwidth"');
142 print $form->select_dolgroups($fk_usergroup1, 'fk_usergroup1', 1);
143 ?></td>
144 </tr>
145 <tr><td>&nbsp;</td></tr>
146 <tr>
147 <td><?php
148 print $langs->trans('group2ToCompare').'</td><td>';
149 print img_picto('', 'group', 'class="pictofixedwidth"');
150 print $form->select_dolgroups($fk_usergroup2, 'fk_usergroup2', 1);
151 ?></td>
152 </tr>
153 <tr>
154 <td><STRONG><?php print $langs->trans('or'); ?></STRONG></td>
155 </tr>
156 <tr>
157 <td><?php
158 echo $langs->trans('OrJobToCompare') . '</td><td>';
159 $j = new Job($db);
160 $jobs = $j->fetchAll();
161 $TJobs = array();
162
163 foreach ($jobs as &$j) {
164 $TJobs[$j->id] = $j->label;
165 }
166
167 print img_picto('', 'jobprofile', 'class="pictofixedwidth"').$form->selectarray('fk_job', $TJobs, $fk_job, 1);
168 ?></td>
169 </tr>
170 </table>
171 </div>
172
173 <div style="background:#eee;border-radius:5px 0;margin:0px 0 10px;font-style:italic;padding:5px;" class="fichehalfright">
174 <!--<h4><?php echo $langs->trans('legend'); ?></h4>-->
175 <table class="border" width="100%">
176 <tr>
177 <td><span style="vertical-align:middle" class="toohappy diffnote little"></span>
178 <?php echo $langs->trans('CompetenceAcquiredByOneOrMore'); ?></td>
179 </tr>
180 <tr>
181 <td><span style="vertical-align:middle" class="veryhappy diffnote little"></span>
182 <?php echo $langs->trans('MaxlevelGreaterThan'); ?></td>
183 </tr>
184 <tr>
185 <td><span style="vertical-align:middle" class="happy diffnote little"></span>
186 <?php echo $langs->trans('MaxLevelEqualTo'); ?></td>
187 </tr>
188 <tr>
189 <td><span style="vertical-align:middle" class="sad diffnote little"></span>
190 <?php echo $langs->trans('MaxLevelLowerThan'); ?></td>
191 </tr>
192 <tr>
193 <td><span style="vertical-align:middle" class="toosad diffnote little"></span>
194 <?php echo $langs->trans('SkillNotAcquired'); ?></td>
195 </tr>
196 </table>
197
198 </div>
199
200 <div class="clearboth"></div>
201
202 </div>
203
204 <br><br>
205 <div class="center">
206 <input class="button" type="SUBMIT" name="bt1" VALUE="<?php print $langs->trans('Refresh'); ?>">
207 </div>
208 <br><br>
209
210 <div id="compare" width="100%" style="position:relative;">
211
212 <?php if ($fk_usergroup1 > 0 || $fk_usergroup2 > 0 || $fk_job > 0) { ?>
213 <table width="100%">
214 <tr>
215 <th></th>
216 <th><?php print $langs->trans('skill'); ?></th>
217 <th><?php print $langs->trans('rank'); ?></th>
218 <th><?php print $langs->trans('difference'); ?></th>
219 <th><?php print $langs->trans('rank'); ?></th>
220 <th></th>
221 </tr>
222
223 <?php
224 echo '<tr><td id="list-user-left" style="width:30%" valign="top">';
225
226 $TUser1 = $TUser2 = array();
227
228 $userlist1 = displayUsersListWithPicto($TUser1, $fk_usergroup1, 'list1');
229
230
231 $skill = new Skill($db);
232 $TSkill1 = getSkillForUsers($TUser1);
233
234 if ($fk_job > 0) {
235 $TSkill2 = getSkillForJob($fk_job);
236
237 $job = new Job($db);
238 $job->fetch($fk_job);
239 $userlist2 = '<ul>
240 <li>
241 <h3>' . $job->label . '</h3>
242 <p>' . $job->description . '</p>
243 </li>
244 </ul>';
245 } else {
246 $userlist2 = displayUsersListWithPicto($TUser2, $fk_usergroup2, 'list2');
247 $TSkill2 = getSkillForUsers($TUser2);
248 }
249
250 $TMergedSkills = mergeSkills($TSkill1, $TSkill2);
251
252 echo $userlist1;
253
254 echo '</td>';
255
256 echo '<td id="" style="width:20%" valign="top">' . skillList($TMergedSkills) . '</td>';
257 echo '<td id="" style="width:5%" valign="top">' . rate($TMergedSkills, 'rate1') . '</td>';
258 echo '<td id="" style="width:10%" valign="top">' . diff($TMergedSkills) . '</td>';
259 echo '<td id="" style="width:5%" valign="top">' . rate($TMergedSkills, 'rate2') . '</td>';
260
261 echo '<td id="list-user-right" style="width:30%" valign="top">';
262
263 echo $userlist2;
264
265 echo '</td></tr>';
266
267 ?>
268
269 </table>
270
271 <?php } ?>
272
273 </div>
274
275 </form>
276
277 </div>
278
279<?php
280
281print dol_get_fiche_end();
282
283llxFooter();
284$db->close();
285
286
287
295function diff(&$TMergedSkills)
296{
297 $out = '<ul class="diff">';
298
299 foreach ($TMergedSkills as $id => &$sk) {
300 $class = 'diffnote';
301
302 if (empty($sk->rate2)) {
303 $class .= ' toohappy';
304 } elseif (empty($sk->rate1)) {
305 $class .= ' toosad';
306 } elseif ($sk->rate1 == $sk->rate2) {
307 $class .= ' happy';
308 } elseif ($sk->rate2 < $sk->rate1) {
309 $class .= ' veryhappy';
310 } elseif ($sk->rate2 > $sk->rate1) {
311 $class .= ' sad';
312 }
313
314 $out .= '<li fk_skill="' . $id . '" class="' . $class . '" style="text-align:center;">
315 <span class="' . $class . '">&nbsp;</span>
316 </li>';
317 }
318
319 $out .= '</ul>';
320
321 return $out;
322}
323
330function rate(&$TMergedSkills, $field)
331{
332 global $langs, $fk_job;
333
334 $out = '<ul class="competence">';
335
336 foreach ($TMergedSkills as $id => &$sk) {
337 $class = "note";
338 $how_many = 0;
339 if (empty($sk->$field)) {
340 $note = 'x';
341 $class .= ' none';
342 } else {
343 $note = $sk->$field;
344 $how_many = ($field === 'rate1') ? $sk->how_many_max1 : $sk->how_many_max2;
345 }
346
347 if ($field === 'rate2' && $fk_job > 0) {
348 $trad = $langs->trans('RequiredRank');
349 } else {
350 $trad = $langs->trans('HighestRank');
351 }
352
353 $out .= '<li fk_skill="' . $id . '" style="text-align:center;">
354 <p><span class="' . $class . ' classfortooltip" title="' . $trad . '">' . $note . '</span>' . ($how_many > 0 ? '<span class="bubble classfortooltip" title="' . $langs->trans('HowManyUserWithThisMaxNote') . '">' . $how_many . '</span>' : '') . '</p>
355 </li>';
356 }
357
358 $out .= '</ul>';
359
360 return $out;
361}
362
369function skillList(&$TMergedSkills)
370{
371 $out = '<ul class="competence">';
372
373 foreach ($TMergedSkills as $id => &$sk) {
374 $out .= '<li fk_skill="' . $id . '">
375 <h3>' . $sk->label . '</h3>
376 <p>' . $sk->description . '</p>
377 </li>';
378 }
379
380 $out .= '</ul>';
381
382 return $out;
383}
384
392function mergeSkills($TSkill1, $TSkill2)
393{
394 $Tab = array();
395
396 foreach ($TSkill1 as &$sk) {
397 if (empty($Tab[$sk->fk_skill])) {
398 $Tab[$sk->fk_skill] = new stdClass();
399 }
400
401 $Tab[$sk->fk_skill]->rate1 = $sk->rankorder;
402 $Tab[$sk->fk_skill]->how_many_max1 = $sk->how_many_max;
403 $Tab[$sk->fk_skill]->label = $sk->label;
404 $Tab[$sk->fk_skill]->description = $sk->description;
405 }
406
407 foreach ($TSkill2 as &$sk) {
408 if (empty($Tab[$sk->fk_skill])) {
409 $Tab[$sk->fk_skill] = new stdClass();
410 }
411 $Tab[$sk->fk_skill]->rate2 = $sk->rankorder;
412 $Tab[$sk->fk_skill]->label = $sk->label;
413 $Tab[$sk->fk_skill]->description = $sk->description;
414 $Tab[$sk->fk_skill]->how_many_max2 = $sk->how_many_max;
415 }
416
417 return $Tab;
418}
419
428function displayUsersListWithPicto(&$TUser, $fk_usergroup = 0, $namelist = 'list-user')
429{
430 global $db, $langs, $conf, $form;
431
432 $out = '';
433 if ($fk_usergroup > 0) {
434 $list = $namelist . '_excluded_id';
435
436 $excludedIdsList = GETPOST($list);
437
438 $sql = "SELECT u.rowid FROM " . MAIN_DB_PREFIX . "user u
439 LEFT JOIN " . MAIN_DB_PREFIX . "usergroup_user as ugu ON (u.rowid = ugu.fk_user)
440 WHERE u.statut > 0 AND ugu.entity = ".((int) $conf->entity);
441 $sql .= " AND ugu.fk_usergroup=" . ((int) $fk_usergroup);
442
443 $res = $db->query($sql);
444 $out .= '<ul name="' . $namelist . '">';
445
446 $TExcludedId = explode(',', $excludedIdsList);
447
448 $out .= '<input id="'.$list.'" type="hidden" name="'.$list.'" value="'.$excludedIdsList.'"> ';
449
450 $job = new Job($db);
451
452 while ($obj = $db->fetch_object($res)) {
453 $class = '';
454
455 $user = new User($db);
456 $user->fetch($obj->rowid);
457
458 $name = $user->getFullName($langs);
459 if (empty($name)) {
460 $name = $user->login;
461 }
462
463 if (in_array($user->id, $TExcludedId)) {
464 $class .= ' disabled';
465 } else {
466 if (!in_array($user->id, $TUser)) {
467 $TUser[] = $user->id;
468 }
469 }
470
471 $desc = '';
472
473 $jobstring = $job->getLastJobForUser($user->id);
474 $desc .= $jobstring;
475
476 $static_eval = new Evaluation($db);
477 $evaluation = $static_eval->getLastEvaluationForUser($user->id);
478
479 if (!empty($evaluation) && !empty($evaluation->date_eval)) {
480 $desc .= $langs->trans('DateLastEval') . ' : ' . dol_print_date($evaluation->date_eval);
481 } else {
482 $desc .= $langs->trans('NoEval');
483 }
484
485 if (!empty($user->array_options['options_DDA'])) {
486 $desc .= '<br>' . $langs->trans('Anciennete') . ' : ' . dol_print_date(strtotime($user->array_options['options_DDA']));
487 }
488
489 $out .= '<li fk_user="' . $user->id . '" class="' . $class . '">
490 ' . $form->showphoto('userphoto', $user, 0, 0, 0, 'photoref', 'small', 1, 0, 1) . '
491 <h3>' . $name . '</h3>
492 <p>' . $desc . '</p>
493 </li>';
494 }
495
496 $out .= '</ul>';
497 }
498
499 return $out;
500}
501
502
510function getSkillForUsers($TUser)
511{
512 global $db;
513
514 //I go back to the user with the highest score in a given group for all the skills assessed in that group
515 if (empty($TUser)) {
516 return array();
517 }
518
519 $sql = 'SELECT sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill, ';
520 $sql .= ' MAX(sr.rankorder) as rankorder';
521 $sql .= ' FROM '.MAIN_DB_PREFIX.'hrm_skill sk';
522 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'hrm_skillrank sr ON (sk.rowid = sr.fk_skill)';
523 $sql .= " WHERE sr.objecttype = '".$db->escape(SkillRank::SKILLRANK_TYPE_USER)."'";
524 $sql .= ' AND sr.fk_object IN ('.$db->sanitize(implode(',', $TUser)).')';
525 $sql .= " GROUP BY sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill "; // group par competence
526
527 $resql = $db->query($sql);
528 $Tab = array();
529
530 if ($resql) {
531 //For each skill, we count the number of times that the max score has been reached within a given group
532 $num = 0;
533 while ($obj = $db->fetch_object($resql)) {
534 $sql1 = "SELECT COUNT(rowid) as how_many_max FROM ".MAIN_DB_PREFIX."hrm_skillrank as sr";
535 $sql1 .= " WHERE sr.rankorder = ".((int) $obj->rankorder);
536 $sql1 .= " AND sr.objecttype = '".$db->escape(SkillRank::SKILLRANK_TYPE_USER)."'";
537 $sql1 .= " AND sr.fk_skill = ".((int) $obj->fk_skill);
538 $sql1 .= " AND sr.fk_object IN (".$db->sanitize(implode(',', $TUser)).")";
539 $resql1 = $db->query($sql1);
540
541 $objMax = $db->fetch_object($resql1);
542
543 $Tab[$num] = new stdClass();
544 $Tab[$num]->fk_skill = $obj->fk_skill;
545 $Tab[$num]->label = $obj->label;
546 $Tab[$num]->description = $obj->description;
547 $Tab[$num]->skill_type = $obj->skill_type;
548 $Tab[$num]->fk_object = $obj->fk_object;
549 $Tab[$num]->objectType = SkillRank::SKILLRANK_TYPE_USER;
550 $Tab[$num]->rankorder = $obj->rankorder;
551 $Tab[$num]->how_many_max = $objMax->how_many_max;
552
553 $num++;
554 }
555 } else {
556 dol_print_error($db);
557 }
558
559 return $Tab;
560}
561
568function getSkillForJob($fk_job)
569{
570 global $db;
571
572 if (empty($fk_job)) {
573 return array();
574 }
575
576 $sql = 'SELECT sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill,';
577 $sql .= " MAX(sr.rankorder) as rankorder";
578 $sql .= ' FROM '.MAIN_DB_PREFIX.'hrm_skill as sk';
579 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'hrm_skillrank as sr ON (sk.rowid = sr.fk_skill)';
580 $sql .= " WHERE sr.objecttype = '".SkillRank::SKILLRANK_TYPE_JOB."'";
581 $sql .= ' AND sr.fk_object = '.((int) $fk_job);
582 $sql .= ' GROUP BY sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill'; // group par competence*/
583
584 $resql = $db->query($sql);
585 $Tab = array();
586
587 if ($resql) {
588 $num = 0;
589 while ($obj = $db->fetch_object($resql)) {
590 $Tab[$num] = new stdClass();
591 $Tab[$num]->fk_skill = $obj->fk_skill;
592 $Tab[$num]->label = $obj->label;
593 $Tab[$num]->description = $obj->description;
594 $Tab[$num]->skill_type = $obj->skill_type;
595 //$Tab[$num]->date_start = '';// du poste
596 //$Tab[$num]->date_end = ''; // du poste
597 $Tab[$num]->fk_object = $obj->fk_object;
598 $Tab[$num]->objectType = SkillRank::SKILLRANK_TYPE_JOB;
599 $Tab[$num]->rankorder = $obj->rankorder;
600 $Tab[$num]->how_many_max = $obj->how_many_max;
601
602 $num++;
603 }
604 } else {
605 dol_print_error($db);
606 }
607
608 return $Tab;
609}
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader()
Empty header.
Definition wrapper.php:55
llxFooter()
Empty footer.
Definition wrapper.php:69
Class for Evaluation.
Class for Job.
Definition job.class.php:38
Class for Skill.
Class to manage Dolibarr users.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
treeview li table
No Email.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:137
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:140
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.