compare.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. * \file htdocs/hrm/compare.php
  21. * \ingroup hrm
  22. * \brief This file compares skills of user groups
  23. *
  24. * Displays a table in three parts.
  25. * 1- the left part displays the list of users of the selected group 1.
  26. *
  27. * 2- the central part displays the skills. display of the maximum score for this group and the number of occurrences.
  28. *
  29. * 3- the right part displays the members of group 2 or the job to be compared
  30. */
  31. // Load Dolibarr environment
  32. require_once '../main.inc.php';
  33. require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
  34. require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
  35. require_once DOL_DOCUMENT_ROOT . '/hrm/class/skill.class.php';
  36. require_once DOL_DOCUMENT_ROOT . '/hrm/class/job.class.php';
  37. require_once DOL_DOCUMENT_ROOT . '/hrm/class/evaluation.class.php';
  38. require_once DOL_DOCUMENT_ROOT . '/hrm/class/position.class.php';
  39. require_once DOL_DOCUMENT_ROOT . '/hrm/lib/hrm.lib.php';
  40. // Load translation files required by the page
  41. $langs->load('hrm');
  42. $job = new Job($db);
  43. // Permissions
  44. $permissiontoread = $user->rights->hrm->evaluation->read || $user->rights->hrm->compare_advance->read;
  45. $permissiontoadd = 0;
  46. if (empty($conf->hrm->enabled)) accessforbidden();
  47. if (!$permissiontoread || ($action === 'create' && !$permissiontoadd)) accessforbidden();
  48. /*
  49. * View
  50. */
  51. $css = array('/hrm/css/style.css');
  52. llxHeader('', $langs->trans('SkillComparison'), '', '', 0, 0, '', $css);
  53. $head = array();
  54. $h = 0;
  55. $head[$h][0] = $_SERVER["PHP_SELF"];
  56. $head[$h][1] = $langs->trans("SkillComparison");
  57. $head[$h][2] = 'compare';
  58. print dol_get_fiche_head($head, 'compare', '', 1);
  59. ?>
  60. <script type="text/javascript">
  61. $(document).ready(function () {
  62. $("li[fk_user]").click(function () {
  63. if ($(this).hasClass('disabled')) {
  64. $(this).removeClass('disabled');
  65. } else {
  66. $(this).addClass('disabled');
  67. }
  68. var $userl = $(this).closest('ul');
  69. var listname = $userl.attr('name');
  70. var TId = [];
  71. $userl.find('li').each(function (i, item) {
  72. if ($(item).hasClass('disabled')) {
  73. TId.push($(item).attr('fk_user'));
  74. }
  75. });
  76. $('#' + listname + '_excluded_id').val(TId.join(','));
  77. });
  78. });
  79. </script>
  80. <?php
  81. $fk_usergroup2 = 0;
  82. $fk_job = (int) GETPOST('fk_job');
  83. if ($fk_job <= 0) $fk_usergroup2 = GETPOST('fk_usergroup2');
  84. $fk_usergroup1 = GETPOST('fk_usergroup1');
  85. ?>
  86. <div class="fichecenter">
  87. <form action="<?php echo $_SERVER['PHP_SELF'] ?>">
  88. <div class="tabBar tabBarWithBottom">
  89. <div class="fichehalfleft">
  90. <table class="border tableforfield" width="100%">
  91. <tr>
  92. <td><?php
  93. print $langs->trans('group1ToCompare').'</td><td>';
  94. print img_picto('', 'group', 'class="pictofixedwidth"');
  95. print $form->select_dolgroups($fk_usergroup1, 'fk_usergroup1', 1);
  96. ?></td>
  97. </tr>
  98. <tr><td>&nbsp;</td></tr>
  99. <tr>
  100. <td><?php
  101. print $langs->trans('group2ToCompare').'</td><td>';
  102. print img_picto('', 'group', 'class="pictofixedwidth"');
  103. print $form->select_dolgroups($fk_usergroup2, 'fk_usergroup2', 1);
  104. ?></td>
  105. </tr>
  106. <tr>
  107. <td><STRONG><?php print $langs->trans('or'); ?></STRONG></td>
  108. </tr>
  109. <tr>
  110. <td><?php
  111. echo $langs->trans('OrJobToCompare') . '</td><td>';
  112. $j = new Job($db);
  113. $jobs = $j->fetchAll();
  114. $TJobs = array();
  115. foreach ($jobs as &$j) {
  116. $TJobs[$j->id] = $j->label;
  117. }
  118. print img_picto('', 'jobprofile', 'class="pictofixedwidth"').$form->selectarray('fk_job', $TJobs, $fk_job, 1);
  119. ?></td>
  120. </tr>
  121. </table>
  122. </div>
  123. <div style="background:#eee;border-radius:5px 0;margin:0px 0 10px;font-style:italic;padding:5px;" class="fichehalfright">
  124. <!--<h4><?php echo $langs->trans('legend'); ?></h4>-->
  125. <table class="border" width="100%">
  126. <tr>
  127. <td><span style="vertical-align:middle" class="toohappy diffnote little"></span>
  128. <?php echo $langs->trans('CompetenceAcquiredByOneOrMore'); ?></td>
  129. </tr>
  130. <tr>
  131. <td><span style="vertical-align:middle" class="veryhappy diffnote little"></span>
  132. <?php echo $langs->trans('MaxlevelGreaterThan'); ?></td>
  133. </tr>
  134. <tr>
  135. <td><span style="vertical-align:middle" class="happy diffnote little"></span>
  136. <?php echo $langs->trans('MaxLevelEqualTo'); ?></td>
  137. </tr>
  138. <tr>
  139. <td><span style="vertical-align:middle" class="sad diffnote little"></span>
  140. <?php echo $langs->trans('MaxLevelLowerThan'); ?></td>
  141. </tr>
  142. <tr>
  143. <td><span style="vertical-align:middle" class="toosad diffnote little"></span>
  144. <?php echo $langs->trans('SkillNotAcquired'); ?></td>
  145. </tr>
  146. </table>
  147. </div>
  148. <div class="clearboth"></div>
  149. </div>
  150. <br><br>
  151. <div class="center">
  152. <input class="button" type="SUBMIT" name="bt1" VALUE="<?php print $langs->trans('Refresh'); ?>">
  153. </div>
  154. <br><br>
  155. <div id="compare" width="100%" style="position:relative;">
  156. <?php if ($fk_usergroup1 > 0 || $fk_usergroup2 > 0 || $fk_job > 0) { ?>
  157. <table width="100%">
  158. <tr>
  159. <th></th>
  160. <th><?php print $langs->trans('skill'); ?></th>
  161. <th><?php print $langs->trans('rank'); ?></th>
  162. <th><?php print $langs->trans('difference'); ?></th>
  163. <th><?php print $langs->trans('rank'); ?></th>
  164. <th></th>
  165. </tr>
  166. <?php
  167. echo '<tr><td id="list-user-left" style="width:30%" valign="top">';
  168. $TUser1 = $TUser2 = array();
  169. $userlist1 = displayUsersListWithPicto($TUser1, $fk_usergroup1, 'list1');
  170. $skill = new Skill($db);
  171. $TSkill1 = getSkillForUsers($TUser1);
  172. if ($fk_job > 0) {
  173. $TSkill2 = getSkillForJob($fk_job);
  174. $job = new Job($db);
  175. $job->fetch($fk_job);
  176. $userlist2 = '<ul>
  177. <li>
  178. <h3>' . $job->label . '</h3>
  179. <p>' . $job->description . '</p>
  180. </li>
  181. </ul>';
  182. } else {
  183. $userlist2 = displayUsersListWithPicto($TUser2, $fk_usergroup2, 'list2');
  184. $TSkill2 = getSkillForUsers($TUser2);
  185. }
  186. $TMergedSkills = mergeSkills($TSkill1, $TSkill2);
  187. echo $userlist1;
  188. echo '</td>';
  189. echo '<td id="" style="width:20%" valign="top">' . skillList($TMergedSkills) . '</td>';
  190. echo '<td id="" style="width:5%" valign="top">' . rate($TMergedSkills, 'rate1') . '</td>';
  191. echo '<td id="" style="width:10%" valign="top">' . diff($TMergedSkills) . '</td>';
  192. echo '<td id="" style="width:5%" valign="top">' . rate($TMergedSkills, 'rate2') . '</td>';
  193. echo '<td id="list-user-right" style="width:30%" valign="top">';
  194. echo $userlist2;
  195. echo '</td></tr>';
  196. ?>
  197. </table>
  198. <?php } ?>
  199. </div>
  200. </form>
  201. </div>
  202. <?php
  203. print dol_get_fiche_end();
  204. llxFooter();
  205. /**
  206. *
  207. * Return a html list element with diff between required rank and user rank
  208. *
  209. * @param array $TMergedSkills skill list with all rate to add good picto
  210. * @return string
  211. */
  212. function diff(&$TMergedSkills)
  213. {
  214. $out = '<ul class="diff">';
  215. foreach ($TMergedSkills as $id => &$sk) {
  216. $class = 'diffnote';
  217. if (empty($sk->rate2)) $class .= ' toohappy';
  218. elseif (empty($sk->rate1)) $class .= ' toosad';
  219. elseif ($sk->rate1 == $sk->rate2) $class .= ' happy';
  220. elseif ($sk->rate2 < $sk->rate1) $class .= ' veryhappy';
  221. elseif ($sk->rate2 > $sk->rate1) $class .= ' sad';
  222. $out .= '<li fk_skill="' . $id . '" class="' . $class . '" style="text-align:center;">
  223. <span class="' . $class . '">&nbsp;</span>
  224. </li>';
  225. }
  226. $out .= '</ul>';
  227. return $out;
  228. }
  229. /**
  230. * Return a html list with rank informations
  231. * @param array $TMergedSkills skill list for display
  232. * @param string $field which column of comparison we are working with
  233. * @return string
  234. */
  235. function rate(&$TMergedSkills, $field)
  236. {
  237. global $langs, $fk_job;
  238. $out = '<ul class="competence">';
  239. foreach ($TMergedSkills as $id => &$sk) {
  240. $class = "note";
  241. $how_many = 0;
  242. if (empty($sk->$field)) {
  243. $note = 'x';
  244. $class .= ' none';
  245. } else {
  246. $note = $sk->$field;
  247. $how_many = ($field === 'rate1') ? $sk->how_many_max1 : $sk->how_many_max2;
  248. }
  249. if ($field === 'rate2' && $fk_job > 0) $trad = $langs->trans('RequiredRank');
  250. else $trad = $langs->trans('HighestRank');
  251. $out .= '<li fk_skill="' . $id . '" style="text-align:center;">
  252. <p><span class="' . $class . ' classfortooltip" title="' . $trad . '">' . $note . '</span>' . ($how_many > 0 ? '<span class="bubble classfortooltip" title="' . $langs->trans('HowManyUserWithThisMaxNote') . '">' . $how_many . '</span>' : '') . '</p>
  253. </li>';
  254. }
  255. $out .= '</ul>';
  256. return $out;
  257. }
  258. /**
  259. * return a html ul list of skills
  260. *
  261. * @param array $TMergedSkills skill list for display
  262. * @return string (ul list in html )
  263. */
  264. function skillList(&$TMergedSkills)
  265. {
  266. $out = '<ul class="competence">';
  267. foreach ($TMergedSkills as $id => &$sk) {
  268. $out .= '<li fk_skill="' . $id . '">
  269. <h3>' . $sk->label . '</h3>
  270. <p>' . $sk->description . '</p>
  271. </li>';
  272. }
  273. $out .= '</ul>';
  274. return $out;
  275. }
  276. /**
  277. * create an array of lines [ skillLabel,dscription, maxrank on group1 , minrank needed for this skill ]
  278. *
  279. * @param array $TSkill1 skill list of first column
  280. * @param array $TSkill2 skill list of second column
  281. * @return array
  282. */
  283. function mergeSkills($TSkill1, $TSkill2)
  284. {
  285. $Tab = array();
  286. foreach ($TSkill1 as &$sk) {
  287. if (empty($Tab[$sk->fk_skill])) $Tab[$sk->fk_skill] = new stdClass;
  288. $Tab[$sk->fk_skill]->rate1 = $sk->rankorder;
  289. $Tab[$sk->fk_skill]->how_many_max1 = $sk->how_many_max;
  290. $Tab[$sk->fk_skill]->label = $sk->label;
  291. $Tab[$sk->fk_skill]->description = $sk->description;
  292. }
  293. foreach ($TSkill2 as &$sk) {
  294. if (empty($Tab[$sk->fk_skill])) $Tab[$sk->fk_skill] = new stdClass;
  295. $Tab[$sk->fk_skill]->rate2 = $sk->rankorder;
  296. $Tab[$sk->fk_skill]->label = $sk->label;
  297. $Tab[$sk->fk_skill]->description = $sk->description;
  298. $Tab[$sk->fk_skill]->how_many_max2 = $sk->how_many_max;
  299. }
  300. return $Tab;
  301. }
  302. /**
  303. * Display a list of User with picto
  304. *
  305. * @param array $TUser list of users (employees) in selected usergroup of a column
  306. * @param int $fk_usergroup selected usergroup id
  307. * @param string $namelist html name
  308. * @return string
  309. */
  310. function displayUsersListWithPicto(&$TUser, $fk_usergroup = 0, $namelist = 'list-user')
  311. {
  312. global $db, $langs, $conf, $form;
  313. $out = '';
  314. if ($fk_usergroup > 0) {
  315. $list = $namelist . '_excluded_id';
  316. $excludedIdsList = GETPOST($list);
  317. $sql = "SELECT u.rowid FROM " . MAIN_DB_PREFIX . "user u
  318. LEFT JOIN " . MAIN_DB_PREFIX . "usergroup_user as ugu ON (u.rowid = ugu.fk_user)
  319. WHERE u.statut > 0 AND ugu.entity = ".((int) $conf->entity);
  320. $sql .= " AND ugu.fk_usergroup=" . ((int) $fk_usergroup);
  321. $res = $db->query($sql);
  322. $out .= '<ul name="' . $namelist . '">';
  323. $TExcludedId = explode(',', $excludedIdsList);
  324. $out .= '<input id="'.$list.'" type="hidden" name="'.$list.'" value="'.$excludedIdsList.'"> ';
  325. $job = new Job($db);
  326. while ($obj = $db->fetch_object($res)) {
  327. $class = '';
  328. $user = new User($db);
  329. $user->fetch($obj->rowid);
  330. $name = $user->getFullName($langs);
  331. if (empty($name)) $name = $user->login;
  332. if (in_array($user->id, $TExcludedId)) {
  333. $class .= ' disabled';
  334. } else {
  335. if (!in_array($user->id, $TUser)) $TUser[] = $user->id;
  336. }
  337. $desc = '';
  338. $jobstring = $job->getLastJobForUser($user->id);
  339. $desc .= $jobstring;
  340. $static_eval = new Evaluation($db);
  341. $evaluation = $static_eval->getLastEvaluationForUser($user->id);
  342. if (!empty($evaluation) && !empty($evaluation->date_eval)) {
  343. $desc .= $langs->trans('DateLastEval') . ' : ' . dol_print_date($evaluation->date_eval);
  344. } else {
  345. $desc .= $langs->trans('NoEval');
  346. }
  347. if (!empty($user->array_options['options_DDA'])) $desc .= '<br>' . $langs->trans('Anciennete') . ' : ' . dol_print_date(strtotime($user->array_options['options_DDA']));
  348. $out .= '<li fk_user="' . $user->id . '" class="' . $class . '">
  349. ' . $form->showphoto('userphoto', $user, 0, 0, 0, 'photoref', 'small', 1, 0, 1) . '
  350. <h3>' . $name . '</h3>
  351. <p>' . $desc . '</p>
  352. </li>';
  353. }
  354. $out .= '</ul>';
  355. }
  356. return $out;
  357. }
  358. /**
  359. *
  360. * Allow to get skill(s) of a user
  361. *
  362. * @param array $TUser array of employees we need to get skills
  363. * @return array|int
  364. */
  365. function getSkillForUsers($TUser)
  366. {
  367. global $db;
  368. //I go back to the user with the highest score in a given group for all the skills assessed in that group
  369. if (empty($TUser)) return array();
  370. $sql = 'SELECT sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill, ';
  371. $sql.= ' MAX(sr.rankorder) as rankorder';
  372. $sql.= ' FROM '.MAIN_DB_PREFIX.'hrm_skill sk';
  373. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'hrm_skillrank sr ON (sk.rowid = sr.fk_skill)';
  374. $sql.= " WHERE sr.objecttype = '".$db->escape(SkillRank::SKILLRANK_TYPE_USER)."'";
  375. $sql.= ' AND sr.fk_object IN ('.$db->sanitize(implode(',', $TUser)).')';
  376. $sql.= " GROUP BY sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill "; // group par competence
  377. $resql = $db->query($sql);
  378. $Tab = array();
  379. if ($resql) {
  380. //For each skill, we count the number of times that the max score has been reached within a given group
  381. $num = 0;
  382. while ($obj = $db->fetch_object($resql) ) {
  383. $sql1 = "SELECT COUNT(rowid) as how_many_max FROM ".MAIN_DB_PREFIX."hrm_skillrank as sr";
  384. $sql1.=" WHERE sr.rankorder = ".((int) $obj->rankorder);
  385. $sql1.=" AND sr.objecttype = '".$db->escape(SkillRank::SKILLRANK_TYPE_USER)."'";
  386. $sql1.=" AND sr.fk_skill = ".((int) $obj->fk_skill);
  387. $sql1.=" AND sr.fk_object IN (".$db->sanitize(implode(',', $TUser)).")";
  388. $resql1 = $db->query($sql1);
  389. $objMax = $db->fetch_object($resql1);
  390. $Tab[$num] = new stdClass();
  391. $Tab[$num]->fk_skill = $obj->fk_skill;
  392. $Tab[$num]->label = $obj->label;
  393. $Tab[$num]->description = $obj->description;
  394. $Tab[$num]->skill_type = $obj->skill_type;
  395. $Tab[$num]->fk_object = $obj->fk_object;
  396. $Tab[$num]->objectType = SkillRank::SKILLRANK_TYPE_USER;
  397. $Tab[$num]->rankorder = $obj->rankorder;
  398. $Tab[$num]->how_many_max = $objMax->how_many_max;
  399. $num++;
  400. }
  401. } else {
  402. dol_print_error($db);
  403. }
  404. return $Tab;
  405. }
  406. /**
  407. * Allow to get skill(s) of a job
  408. *
  409. * @param int $fk_job job we need to get required skills
  410. * @return array|int
  411. */
  412. function getSkillForJob($fk_job)
  413. {
  414. global $db;
  415. if (empty($fk_job)) return array();
  416. $sql = 'SELECT sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill,';
  417. $sql.= " MAX(sr.rankorder) as rankorder";
  418. $sql.=' FROM '.MAIN_DB_PREFIX.'hrm_skill as sk';
  419. $sql.=' LEFT JOIN '.MAIN_DB_PREFIX.'hrm_skillrank as sr ON (sk.rowid = sr.fk_skill)';
  420. $sql.=" WHERE sr.objecttype = '".SkillRank::SKILLRANK_TYPE_JOB."'";
  421. $sql.=' AND sr.fk_object = '.((int) $fk_job);
  422. $sql.=' GROUP BY sk.rowid, sk.label, sk.description, sk.skill_type, sr.fk_object, sr.objecttype, sr.fk_skill'; // group par competence*/
  423. $resql = $db->query($sql);
  424. $Tab = array();
  425. if ($resql) {
  426. $num = 0;
  427. while ($obj = $db->fetch_object($resql) ) {
  428. $Tab[$num] = new stdClass();
  429. $Tab[$num]->fk_skill = $obj->fk_skill;
  430. $Tab[$num]->label = $obj->label;
  431. $Tab[$num]->description = $obj->description;
  432. $Tab[$num]->skill_type = $obj->skill_type;
  433. //$Tab[$num]->date_start = '';// du poste
  434. //$Tab[$num]->date_end = ''; // du poste
  435. $Tab[$num]->fk_object = $obj->fk_object;
  436. $Tab[$num]->objectType = SkillRank::SKILLRANK_TYPE_JOB;
  437. $Tab[$num]->rankorder = $obj->rankorder;
  438. $Tab[$num]->how_many_max = $obj->how_many_max;
  439. $num++;
  440. }
  441. } else {
  442. dol_print_error($db);
  443. }
  444. return $Tab;
  445. }