person.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Система учёта рабочего времени (CPS) НИЯУ МИФИ: Приветствие</title>
  5. <meta name="robots" content="noindex,nofollow">
  6. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  7. <meta http-equiv="Content-Language" content="ru">
  8. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  9. <meta name="keywords" content="cps,нияу мифи,мифи,система учёта рабочего времени,сурв">
  10. <link rel="shortcut icon" type="image/png" href="/img/favicon.ico">
  11. <script type='application/ld+json'>{"@context":"http://schema.org","@type":"CollegeOrUniversity","address":"115409, Moscow, Kashirskoe shosse, 31","email":"rector@mephi.ru","name":"National research nuclear university \"MEPhI\"","sameAs":"https://en.wikipedia.org/wiki/National_Research_Nuclear_University_MEPhI","telephone":"+7 (495) 788-5699"}</script>
  12. <style>
  13. #employee-details {
  14. padding: 20px;
  15. }
  16. #employee-details li {
  17. width: 50%;
  18. }
  19. #employee-details ul {
  20. width:100%;
  21. max-width: 1200px;
  22. margin:0px;
  23. overflow:hidden;
  24. }
  25. #employee-details li {
  26. min-width: 400px;
  27. line-height:1.5em;
  28. float:left;
  29. display:inline;
  30. }
  31. #employee-details h1 {
  32. padding: 0;
  33. margin: 0;
  34. font-size: 24px;
  35. border-bottom:1px solid #ccc;
  36. }
  37. #employee-details table td {
  38. line-height:1em;
  39. padding: 5px;
  40. }
  41. #person-info td:nth-child(1) {
  42. display: none;
  43. }
  44. #statistics td:nth-child(1) {
  45. text-align: right;
  46. }
  47. #statistics td:nth-child(2) {
  48. font-weight: bold;
  49. }
  50. .event-falseExit, .event-falseEntry {
  51. color: #888;
  52. }
  53. #events tr:nth-child(even) {
  54. background: #eee;
  55. }
  56. #events tr:nth-child(odd) {
  57. background: #ddd;
  58. }
  59. #events tr:nth-child(even):hover {
  60. background: #bbf;
  61. }
  62. #events tr:nth-child(odd):hover {
  63. background: #ccF;
  64. }
  65. .holiday {
  66. color: #888;
  67. }
  68. .fakeRow {
  69. visibility: hidden;
  70. }
  71. .startEndDatesPanel {
  72. padding: 10px 0 0 10px;
  73. display: block;
  74. position: fixed;
  75. width: 100%;
  76. background: #222c3c;
  77. }
  78. </style>
  79. </head>
  80. <body>
  81. <div class='startEndDatesPanel'>
  82. <!--input type='date' name='startDate'>
  83. <input type='date' name='endDate'-->
  84. <input type='number' name='year' min=2016 max=2099 maxlength=4 size=4 onchange='datesChanged(); return false'>
  85. <select name='month' onchange='datesChanged(); return false'>
  86. <option value="" selected>весь год</option>
  87. <option value="1">январь</option>
  88. <option value="2">февраль</option>
  89. <option value="3">март</option>
  90. <option value="4">апрель</option>
  91. <option value="5">май</option>
  92. <option value="6">июнь</option>
  93. <option value="7">июль</option>
  94. <option value="8">август</option>
  95. <option value="9">сентябрь</option>
  96. <option value="10">октябрь</option>
  97. <option value="11">ноябрь</option>
  98. <option value="12">декабрь</option>
  99. </select>
  100. </div>
  101. <br>
  102. <div id='employee-details'>
  103. <span id='details' style='display:none'>
  104. <h1>Детализация: (<a href='#' style='color:#0088cc' onclick='exportToXls()'>экспортировать в XLS</a>)</h1>
  105. <table id='events'>
  106. <tr id='eventsHeader'><th>Дата</th><th>Вход</th><th>Вых.</th><th>Отработано</th><th>СТУ</th><th>Переработка</th></tr>
  107. <tr class='fakeRow'><td>&nbsp;</td></tr>
  108. </table>
  109. </span>
  110. <span id='loading'>
  111. Загрузка…
  112. </span>
  113. <ul>
  114. <li>
  115. <h1>Физ. лицо:</h1>
  116. <table id='person'>
  117. <tr>
  118. <td>
  119. <a id='photoLink' href='#'><img id='photo' src='https://voip.mephi.ru/public/photos/no_photo.png' style='min-width:100px' width='100px' height='133px' alt='photo'></a>
  120. </td>
  121. <td>
  122. <table id='person-info'>
  123. <tr><td>ФИО:</td> <td id='fullname'></td></tr>
  124. <tr><td>Должность:</td> <td id='appointment'></td></tr>
  125. <tr><td title='Суммарная ставка'>Ставка:</td> <td id='stake'></td></tr>
  126. <tr><td>Обязанности:</td> <td id='appointmentDescription'></td></tr>
  127. <tr id='controls' style='display:none'><td></td><td><a style='cursor:pointer' id='voipLink' href='#' target="_blank">найти в справочной книге</a></td></tr>
  128. </table>
  129. </td>
  130. </tr>
  131. <tr id='personnelNumberRow' style='display:none'><td>Таб. номер (шифр):</td><td id="personnelNumber" style='font-weight:bold'></td></tr>
  132. </table>
  133. </li>
  134. <li id='statisticsLi' style='display:none'>
  135. <h1>Статистика:</h2>
  136. <table id='statistics'>
  137. <!--tr><td>Рабочих дней:</td><td id='workdays'></td></tr>
  138. <tr><td>Вых. и праздн. дней:</td><td id='holidays'></td></tr-->
  139. <tr><td>Средний рабочий день:</td><td id='averageWorkday'></td></tr>
  140. <tr><td>Явок по табелю:</td><td id='turnoutByTimesheets'></td></tr>
  141. <tr><td>Посещений:</td><td id='visits'></td></tr>
  142. <tr><td>Разница посещений:</td><td id='visitsDiff'></td></tr>
  143. <tr><td>Требуется часов:</td><td id='hoursExpected'></td></tr>
  144. <tr><td>Отработано часов:</td><td id='hoursWorked'></td></tr>
  145. <tr><td>Переработано/пропущено часов:</td><td id='hoursDiff'></td></tr>
  146. <!--tr><td>Переработано часов:</td><td id='hoursOverWorked'></td></tr-->
  147. <tr><td>Нагрузка:</td><td id='loadage'></td></tr>
  148. </table>
  149. </li>
  150. </ul>
  151. <br><br>
  152. </div>
  153. </body>
  154. </html>
  155. <script src="https://voip.mephi.ru/public/js/3rdparty/jquery-1.9.1.min.js" type="text/javascript" charset="utf-8"></script>
  156. <script src="https://voip.mephi.ru/public/3rdparty/jquery-ui-1.11.4.custom/jquery-ui.min.js" type="text/javascript" charset="utf-8"></script>
  157. <script src="https://voip.mephi.ru/public/js/misc.js" type="text/javascript" charset="utf-8"></script>
  158. <script src="https://voip.mephi.ru/public/js/misc-3rdparty.js" type="text/javascript" charset="utf-8"></script>
  159. <script src="/js/common.js?v=2.18" type="text/javascript" charset="utf-8"></script>
  160. <script src="/js/3rdparty/alasql/dist/alasql.min.js" type="text/javascript" charset="utf-8"></script>
  161. <script src="/js/3rdparty/xlsx.js/dist/xlsx.full.min.js" type="text/javascript" charset="utf-8"></script>
  162. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/css/voip.css">
  163. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/css/voip-portable.css">
  164. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/css/revel.css-minified.css">
  165. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/css/3rdparty/jstree/style.min.css">
  166. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/3rdparty/jquery-ui-1.11.4.custom/jquery-ui.min.css">
  167. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/3rdparty/perfect-scrollbar-0.6.7/perfect-scrollbar.min.css">
  168. <link rel="stylesheet" type="text/css" href="https://voip.mephi.ru/public/css/3rdparty/select2.min.css">
  169. <script>
  170. var personId = getUrlParameter('personId');
  171. //var startDate = '2011-01-01';
  172. var startDate = (new Date(new Date() - 30*24*3600*1000).getFullYear())+'-01-01';
  173. var endDate = (parseInt(startDate.split('-')[0])+1)+'-01-01';
  174. var startDate_param = getUrlParameter('startDate');
  175. var endDate_param = getUrlParameter('endDate');
  176. if (startDate_param != null) {
  177. startDate = startDate_param;
  178. }
  179. if (endDate_param != null) {
  180. endDate = endDate_param;
  181. }
  182. function changePhotoToURL(url) {
  183. $('#photoLink').attr('href', url);
  184. $('#photo').attr('src', url);
  185. }
  186. function showPerson(person) {
  187. console.log("person", person);
  188. $('#fullname').text(person.Fullname);
  189. var appointments = [];
  190. var appointmentsFull = [];
  191. var stake = 0.0;
  192. for (var fIdx = 0; fIdx < person.Formulars.length; fIdx++) {
  193. var formular = person.Formulars[fIdx];
  194. if (!formular.IsActive) {
  195. continue;
  196. }
  197. if (formular.UnitShortName == null) {
  198. formular.UnitShortName = "";
  199. }
  200. appointments.push(formular.AppointmentName+' <a href="calculate.html?unitIds='+formular.UnitId+'"&startDate='+startDate+'&endDate='+endDate+'&yearPicker='+yearPicker+'&monthPicker='+monthPicker+'>'+formular.UnitShortName.trim().toUpperCase()+'</a>');
  201. var fullAppointment = formular.AppointmentName+' '+formular.UnitName;
  202. if (formular.Rate != null && formular.Rate > 0) {
  203. fullAppointment += ' ['+formular.Rate+'ст. '+formular.FormularType.toLowerCase()+']';
  204. }
  205. appointmentsFull.push(fullAppointment);
  206. stake += formular.Rate;
  207. }
  208. if (appointments.length > 0) {
  209. $('#appointment').html(appointments.join(', ')).attr('title', appointmentsFull.join(', ')+' ('+person.Description+')');
  210. } else {
  211. $('#appointment').html(person.Description);
  212. }
  213. if (stake > 0) {
  214. $('#stake').text((Math.round(stake*100)/100)+' ст.').attr('title', appointmentsFull.join(', '));
  215. }
  216. $('#controls').css('display', 'table-row');
  217. }
  218. function fallbackPhotoToSecurityDepartment(personId) {
  219. changePhotoToURL('https://api.cps.mephi.ru/people/'+personId+'.jpg');
  220. }
  221. function showSubscriber(personId, subscriber) {
  222. var appointmentDescriptions = [];
  223. if (subscriber == null) {
  224. fallbackPhotoToSecurityDepartment(personId);
  225. return;
  226. }
  227. if (subscriber.Appointments == null) {
  228. subscriber.Appointments = [];
  229. }
  230. var appointmentDescriptions = {};
  231. for (var aIdx = 0; aIdx < subscriber.Appointments.length; aIdx++) {
  232. var appointment = subscriber.Appointments[aIdx];
  233. if (appointment.Description != null && appointment.Description != "" && appointment.Description != "-") {
  234. appointmentDescriptions[appointment.Description] = appointment.Description;
  235. }
  236. }
  237. console.log(subscriber);
  238. $('#appointmentDescription').text(Object.keys(appointmentDescriptions).join(', '));
  239. }
  240. function requestVoipFaceInfo(personId) {
  241. voipRequest("subscribers/"+personId+".json?renderFilter=subscriber_selected&effectiveSecurityLevel=1024", function(data){
  242. var subscriber = data.subscriber_selected;
  243. showSubscriber(personId, subscriber);
  244. }, null);
  245. }
  246. function showStatistics(statistics) {
  247. //$('#workdays').text(…);
  248. //$('#holidays')
  249. var averageWorkday = Math.round((statistics.secondsExpected / statistics.visitsExpected) / 60);
  250. $('#averageWorkday').text(Math.floor(averageWorkday / 60)+'ч '+(averageWorkday%60)+'м');
  251. $('#turnoutByTimesheets').text(statistics.visitsExpected);
  252. $('#visits').text(statistics.visits);
  253. $('#visitsDiff').text(statistics.visitsDiff);
  254. $('#hoursExpected').text(Math.round(statistics.secondsExpected / 3600));
  255. $('#hoursWorked').text(Math.round(statistics.secondsWorked / 3600));
  256. $('#hoursDiff').text(Math.abs(Math.round(statistics.secondsDiff / 3600)));
  257. $('#hoursOverWorked').text(Math.round((statistics.secondsWorked - statistics.secondsExpected)/ 3600));
  258. $('#loadage').text(Math.round(100 * statistics.secondsWorked / statistics.secondsExpected)+' %').attr('title', Math.round(statistics.secondsWorked/3600)+' / '+Math.round(statistics.secondsExpected/3600));
  259. $('#statisticsLi').css('display', 'inline');
  260. var colorGood = [0, 128, 0];
  261. var colorBad = [255, 0, 0];
  262. var colorNorm = [0, 0, 0];
  263. var colorRatioHoursDiff = 65535*Math.abs(statistics.secondsDiff) / statistics.secondsExpected;
  264. if (colorRatioHoursDiff > 1) {
  265. colorRatioHoursDiff = 1;
  266. }
  267. var dColorHoursDiff = colorNorm
  268. if (statistics.secondsDiff >= 0) {
  269. dColorHoursDiff = colorGood;
  270. } else {
  271. dColorHoursDiff = colorBad;
  272. }
  273. var colorNormRatioHoursDiff = 1-colorRatioHoursDiff;
  274. var colorHoursDiff = [
  275. colorNorm[0]*colorNormRatioHoursDiff+dColorHoursDiff[0]*colorRatioHoursDiff,
  276. colorNorm[1]*colorNormRatioHoursDiff+dColorHoursDiff[1]*colorRatioHoursDiff,
  277. colorNorm[2]*colorNormRatioHoursDiff+dColorHoursDiff[2]*colorRatioHoursDiff,
  278. ];
  279. $('#hoursDiff').css('color', 'rgba('+colorHoursDiff[0]+','+colorHoursDiff[1]+','+colorHoursDiff[2]+',1)');
  280. $('#statistics tr').each(function(idx, el){
  281. var jel = $(el);
  282. if (jel.text().indexOf("NaN") != -1) {
  283. jel.css('display', 'none');
  284. }
  285. })
  286. }
  287. function showDetails(person, perDayInfo, status) {
  288. //console.log('showDetails', person, perDayInfo, status);
  289. var formulars = {};
  290. for (var fIdx = 0; fIdx < person.Formulars.length; fIdx++) {
  291. var formular = person.Formulars[fIdx];
  292. formulars[formular.StfCardId] = formular;
  293. }
  294. var dateIter = dateString2date(startDate).getTime() + 12*3600*1000;
  295. var dateEndTS0 = dateString2date(endDate).getTime() - 12*3600*1000;
  296. var dateEndTS1 = dateString2date(status.lastEventDatetime.split('T')[0]).getTime();
  297. var dateEndTS = Math.min(dateEndTS0, dateEndTS1) + 18*3600*1000;
  298. var dates = [];
  299. while (dateIter < dateEndTS) {
  300. var _date = new Date(dateIter);
  301. dates.push(dateToDateStr(_date));
  302. dateIter += 24 * 3600 * 1000;
  303. }
  304. dates.sort(datesStrCompFunc);
  305. var secondsExpectedTotal = 0;
  306. for (var dateIdx = 0; dateIdx < dates.length; dateIdx++) {
  307. var date = dates[dateIdx];
  308. var dayInfo = perDayInfo[date];
  309. if (dayInfo == null) {
  310. continue;
  311. }
  312. secondsExpectedTotal += dayInfo.secondsExpected;
  313. }
  314. for (var dateIdx = 0; dateIdx < dates.length; dateIdx++) {
  315. var date = dates[dateIdx];
  316. var dayInfo = perDayInfo[date];
  317. var exits = [];
  318. var entries = [];
  319. var dayOfWeek = ( dateString2date(date+' 00:00:00').getDay())%7;
  320. var dayOfWeekMap = { 0: 'Вс', 1: 'Пн', 2: 'Вт', 3: 'Ср', 4: 'Чт', 5: 'Пт', 6: 'Сб' };
  321. if (dayInfo == null) {
  322. dayInfo = {};
  323. dayInfo.timesheetRows = [];
  324. dayInfo.visited = false;
  325. dayInfo.secondsExpected = 0;
  326. dayInfo.secondsWorked = 0;
  327. dayInfo.events = [];
  328. dayInfo.skipped = true;
  329. }
  330. var workedMins = Math.round(dayInfo.secondsWorked/60);
  331. var workedDescription = '';
  332. if (workedMins > 0) {
  333. workedDescription = Math.floor(workedMins/60)+'ч '+(workedMins%60)+'м';
  334. }
  335. if (dayInfo.skipped && workedDescription != '' && secondsExpectedTotal > 0) {
  336. workedDescription = '('+workedDescription+')';
  337. }
  338. //var expectedSecs = 0;
  339. var timesheetTitlesMap = {};
  340. var timesheetDescription = [];
  341. for (var i = 0; i < dayInfo.timesheetRows.length; i++) {
  342. var timesheetRow = dayInfo.timesheetRows[i];
  343. var hoursRequired = 0;
  344. if (timesheetRow.HoursRequired != null) {
  345. hoursRequired = timesheetRow.HoursRequired;
  346. //expectedSecs += hoursRequired*3600;
  347. }
  348. timesheetTitlesMap[timesheetRow.TimeUseCode] = true;
  349. timesheetDescription.push((hoursRequired*formulars[timesheetRow.StfCardId].Rate)+' ч. ('+timesheet_rowDescriptions[timesheetRow.TimeUseCode]+')');
  350. }
  351. var timesheetTitles = [];
  352. for (var timeUseCode in timesheetTitlesMap) {
  353. timesheetTitles.push(timeUseCode);
  354. }
  355. if (timesheetTitles.length == 0) {
  356. timesheetTitles.push('--');
  357. timesheetDescription.push('нет табельных данных');
  358. }
  359. var expectedMins = 0;
  360. var expectedDescription = '0 ч. 0 м.';
  361. var overWorkedMins = 0;
  362. var overWorkedDescription = '';
  363. if (!dayInfo.skipped) {
  364. expectedMins = Math.round(dayInfo.secondsExpected/60);
  365. expectedDescription = Math.floor(expectedMins/60)+' ч. '+(expectedMins%60)+' м.';
  366. overWorkedMins = Math.round((dayInfo.secondsWorked - dayInfo.secondsExpected)/60);
  367. if (Math.abs(overWorkedMins) > 0) {
  368. if (overWorkedMins > 0) {
  369. overWorkedDescription = Math.floor(overWorkedMins/60)+'ч '
  370. overWorkedDescription += (overWorkedMins%60)!=0?(overWorkedMins%60)+'м':'';
  371. } else {
  372. overWorkedDescription = '- '+Math.floor(Math.abs(overWorkedMins)/60)+'ч '
  373. overWorkedDescription += (overWorkedMins%60)!=0?(Math.abs(overWorkedMins)%60)+'м':'';
  374. }
  375. }
  376. }
  377. entriesExitsTitle = '';
  378. for (var i = 0; i < dayInfo.events.length; i++) {
  379. var ev = dayInfo.events[i];
  380. var time = ev.DtDate.split(" ")[1];
  381. var shortTime = time.substring(0, time.length-3);
  382. switch(ev.Direction) {
  383. case 0:
  384. case 2:
  385. while (entries.length-1 > exits.length) {
  386. exits.push("&nbsp;");
  387. }
  388. exits.push("<span class='"+(ev.Direction==0?'event-exit':'event-falseExit')+"'>"+shortTime+"</span>");
  389. break;
  390. case 1:
  391. case 3:
  392. while (exits.length > entries.length) {
  393. entries.push("&nbsp;");
  394. }
  395. entries.push("<span class='"+(ev.Direction==0?'event-entry':'event-falseEmtry')+"'>"+shortTime+"</span>");
  396. break;
  397. }
  398. }
  399. while (exits.length > entries.length) {
  400. entries.push("&nbsp;");
  401. }
  402. while (entries.length > exits.length) {
  403. exits.push("&nbsp;");
  404. }
  405. if (prodCalendar[date] == null) {
  406. console.log('Invalid date', date);
  407. }
  408. if (prodCalendar[date].cps_isfilled == 0) {
  409. timesheetTitles.push("✖");
  410. expectedDescription += ' (нет данных с проходной)';
  411. }
  412. $('#eventsHeader').after($('<tr'+(expectedMins==0&&secondsExpectedTotal>0?' class="holiday"':'')+'><td>'+date+': '+dayOfWeekMap[dayOfWeek]+'</td><td title="'+entriesExitsTitle+'">'+entries.join('<br>')+'</td><td title="'+entriesExitsTitle+'">'+exits.join('<br>')+'</td><td>'+workedDescription+'</td><td title="'+timesheetDescription.join(', ')+' = '+expectedDescription+'">'+timesheetTitles.join(',')+'</td><td>'+overWorkedDescription+'</td></tr>'));
  413. }
  414. var personnelNumberMap = {};
  415. for (var fIdx = 0; fIdx < person.Formulars.length; fIdx++) {
  416. var formular = person.Formulars[fIdx];
  417. if (!formular.IsActive) {
  418. continue;
  419. }
  420. if (personnelNumberMap[formular.PersonnelNumber] == null) {
  421. personnelNumberMap[formular.PersonnelNumber] = []
  422. }
  423. personnelNumberMap[formular.PersonnelNumber].push(formular);
  424. }
  425. var personnelNumberValues = Object.keys(personnelNumberMap);
  426. var personnelNumberDescriptions = [];
  427. for (var personnelNumber in personnelNumberMap) {
  428. var formulars = personnelNumberMap[personnelNumber];
  429. var unitNameMap = {};
  430. for (var fIdx = 0; fIdx < formulars.length; fIdx++) {
  431. var formular = formulars[fIdx];
  432. unitNameMap[formular.UnitShortName] = true;
  433. }
  434. var unitNames = Object.keys(unitNameMap);
  435. personnelNumberDescriptions.push(personnelNumber+' ['+unitNames.join(', ')+']');
  436. }
  437. if (personnelNumberValues.length > 0) {
  438. $('#personnelNumber').text(personnelNumberValues.join(', ')).attr('title', personnelNumberDescriptions.join(';'));
  439. $('#personnelNumberRow').css('display', 'inline-block');
  440. } else {
  441. $('#personnelNumberRow').css('display', 'none');
  442. }
  443. $('#details').css('display', 'block');
  444. window.scrollTo(0,document.body.scrollHeight);
  445. }
  446. var person = null
  447. var cpsInfo = null;
  448. var timesheet = null;
  449. var prodCalendar = null;
  450. var statistics = null;
  451. var apiStatus = null;
  452. function tryDisplayStatisticsAndDetails() {
  453. if (person == null || cpsInfo == null || timesheet == null || prodCalendar == null || apiStatus == null) {
  454. //console.log('try', person, cpsInfo, timesheet, prodCalendar, apiStatus);
  455. return;
  456. }
  457. var formularsMap = {};
  458. for (var fIdx = 0; fIdx < person.Formulars.length; fIdx++) {
  459. var formular = person.Formulars[fIdx];
  460. formularsMap[formular.StfCardId] = formular;
  461. }
  462. statistics = calculateStatistics(person, cpsInfo, timesheet, prodCalendar, formularsMap);
  463. showStatistics(statistics);
  464. showDetails(person, statistics.perDayInfo, apiStatus);
  465. }
  466. function requestFaceInfo(personId) {
  467. personId=parseInt(personId);
  468. request("people?person.Id="+personId+"&prepareFormulars=true&startDate="+startDate+"&endDate="+endDate, function(people){
  469. person = people[0];
  470. if (person == null) {
  471. person = {};
  472. }
  473. if (person.Formulars == null) {
  474. person.Formulars = [];
  475. }
  476. if (person.EmpGUID != null) {
  477. $('#voipLink').attr('href', 'https://voip.mephi.ru/subscribers?Subscriber.PersonId='+personId+'&effectiveSecurityLevel=1024').css('display', 'inline-block');
  478. } else {
  479. $('#voipLink').css('display', 'none');
  480. }
  481. showPerson(person);
  482. tryDisplayStatisticsAndDetails();
  483. }, null);
  484. return;
  485. }
  486. function displayPassesProblem() {
  487. $('#details').prepend("<hr><h1 style='color:red'>Отсутствует информация об актуальных пропусках сотрудника. Необходимо внести справления через <a href='https://fixok.ut.mephi.ru/' target='_blank'>https://fixok.ut.mephi.ru/</a></h1>");
  488. return;
  489. }
  490. function requestFacePassesInfo(personId) {
  491. personId=parseInt(personId);
  492. request("passes?pass.PersonId="+personId+'&startDate='+startDate+'&endDate='+endDate, function(passes){
  493. if (passes == null) {
  494. passes = [];
  495. }
  496. if (passes.length == 0) {
  497. displayPassesProblem();
  498. }
  499. }, null);
  500. return;
  501. }
  502. function requestFaceCPSInfo(personId) {
  503. personId=parseInt(personId);
  504. request("events?event.PersonId="+personId+"&optimizeOutput=true&startDate="+startDate+'&endDate='+endDate, function(events){
  505. cpsInfo = events;
  506. if (cpsInfo == null) {
  507. cpsInfo = [];
  508. }
  509. $('#loading').css('display', 'none');
  510. tryDisplayStatisticsAndDetails();
  511. }, null);
  512. return;
  513. }
  514. function requestFaceTimesheet(personId) {
  515. personId=parseInt(personId);
  516. request("timesheetRows?timesheetRow.PersonId="+personId+"&optimizeOutput=true&startDate="+startDate+'&endDate='+endDate, function(_timesheet){
  517. timesheet = _timesheet;
  518. if (timesheet == null) {
  519. timesheet = [];
  520. }
  521. tryDisplayStatisticsAndDetails();
  522. }, null);
  523. return;
  524. }
  525. function requestStatus() {
  526. request("status", function(_status) {
  527. apiStatus = _status;
  528. tryDisplayStatisticsAndDetails();
  529. }, null);
  530. return;
  531. }
  532. function exportToXls() {
  533. var statisticsClean = {};
  534. statisticsClean[l("days")] = statistics.days;
  535. statisticsClean[l("visits")] = statistics.visits;
  536. statisticsClean[l("visitsExpected")] = statistics.visitsExpected;
  537. statisticsClean[l("visitsDiff")] = statistics.visitsDiff;
  538. statisticsClean[l("hoursWorked")] = Math.round(statistics.secondsWorked / 3600);
  539. statisticsClean[l("hoursExpected")] = Math.round(statistics.secondsExpected / 3600);
  540. statisticsClean[l("hoursDiff")] = Math.round(statistics.secondsDiff / 3600);
  541. var headers = [{sheetid:'Физ. лицо', headers:false},{sheetid:'статистика',header:true},{sheetid:'журнал',header:true}];
  542. var datas = [];
  543. datas.push([{
  544. 'Полное имя': person.Fullname,
  545. 'Описание': person.Description,
  546. }]);
  547. datas.push([statisticsClean]);
  548. var cpsInfoLocalized = [];
  549. for (var idx = 0; idx < cpsInfo.length; idx++) {
  550. var origRow = cpsInfo[idx];
  551. var row = {};
  552. row['Отметка времени'] = origRow['DtDate'];
  553. //row['Идент. физ. лица'] = origRow['PersonId'];
  554. //row['Номер сотрудника'] = origRow['EmpGUID'];
  555. switch (origRow['Direction']) {
  556. case 0:
  557. row['Тип события'] = 'выход';
  558. break;
  559. case 1:
  560. row['Тип события'] = 'вход';
  561. break;
  562. case 2:
  563. row['Тип события'] = '(выход)';
  564. break;
  565. case 3:
  566. row['Тип события'] = '(вход)';
  567. break;
  568. }
  569. cpsInfoLocalized.push(row);
  570. }
  571. console.log(cpsInfoLocalized);
  572. datas.push(cpsInfoLocalized);
  573. for (var month in statistics.perMonthInfo) {
  574. headers.push({sheetid:'месяц_'+month,header:true});
  575. console.log(statistics.perMonthInfo[month]);
  576. var statsLocalized = {};
  577. statsLocalized[l('days')] = statistics.perMonthInfo[month]['days'];
  578. statsLocalized[l('visits')] = statistics.perMonthInfo[month]['visits'];
  579. statsLocalized[l('visitsExpected')] = statistics.perMonthInfo[month]['visitsExpected'];
  580. statsLocalized[l('visitsMissed')] = statistics.perMonthInfo[month]['visitsMissed'];
  581. statsLocalized[l('visitsDiff')] = statistics.perMonthInfo[month]['visitsDiff'];
  582. statsLocalized[l('hoursWorked')] = Math.round(statistics.perMonthInfo[month]['secondsWorked'] / 3600);
  583. statsLocalized[l('hoursExpected')] = Math.round(statistics.perMonthInfo[month]['secondsExpected'] / 3600);
  584. //statsLocalized[l('hoursMissed')] = Math.round(statistics.perMonthInfo[month]['secondsMissed'] / 3600);
  585. statsLocalized[l('hoursDiff')] = Math.round(statistics.perMonthInfo[month]['secondsDiff'] / 3600);
  586. datas.push([statsLocalized]);
  587. }
  588. var res = alasql('SELECT * INTO XLSX("statistics.xlsx",?) FROM ?', [headers,datas]);
  589. }
  590. getProdCalendar(startDate, endDate, function(_prodCalendar){
  591. prodCalendar = _prodCalendar;
  592. tryDisplayStatisticsAndDetails();
  593. });
  594. if (personId != null) {
  595. changePhotoToURL('https://voip.mephi.ru/subscribers/.jpg?Subscriber.PersonId='+personId);
  596. requestFaceInfo(personId);
  597. requestFaceCPSInfo(personId);
  598. requestFaceTimesheet(personId);
  599. requestFacePassesInfo(personId);
  600. requestVoipFaceInfo(personId);
  601. requestStatus();
  602. }
  603. initDatepickers();
  604. initStartEndDatesPanel();
  605. </script>