compare.php 16 KB

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