Будь ласка, використовуйте цей ідентифікатор, щоб цитувати або посилатися на цей матеріал: https://er.chdtu.edu.ua/handle/ChSTU/9029
Назва: Розробка інформаційної системи по управлінню персоналом компанії
Автори: Куницька, Світлана Юріївна
Аркалюк, Віктор Андрійович
Дата публікації: 9-чер-2022
URI (Уніфікований ідентифікатор ресурсу): https://er.chdtu.edu.ua/handle/ChSTU/9029
Розташовується у зібраннях:121 Інженерія програмного забезпечення (Інженерія програмного забезпечення)

Файли цього матеріалу:
Файл Опис РозмірФормат 
Кваліфікаційна робота бакалавра Аркалюк Віктор Андрійович.pdf
  Restricted Access
2.14 MBAdobe PDFПереглянути/Відкрити    Запит копії


Усі матеріали в архіві електронних ресурсів захищено авторським правом, усі права збережено.

Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ 
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ 
ФАКУЛЬТЕТ ІНФОРМАЦІЙНИХ ТЕХНОЛОГІЙ І СИСТЕМ 
 
КАФЕДРА ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ АВТОМАТИЗОВАНИХ 
СИСТЕМ 
         
 
 
 
 
 
 
ПОЯСНЮВАЛЬНА ЗАПИСКА 
до кваліфікаційної роботи бакалавра 
 
 
  
на тему: «РОЗРОБКА ІНФОРМАЦІЙНОЇ СИСТЕМИ ПО 
УПРАВЛІННЮ ПЕРСОНАЛОМ КОМПАНІЇ» 
 
 
 
 
 
 
Виконав: студент 4 курсу групи ПЗ-184 
Напряму підготовки: 
121 «Інженерія програмного забезпечення» 
Віктор АРКАЛЮК  
 
Керівник  
к.т.н., доцент Світлана КУНИЦЬКА 
 
Рецензент 
к.т.н., старший викладач кафедри 
інформаційних технологій ЧНУ  
ім. Б. Хмельницького Тетяна СТАБЕЦЬКА  
 
 
 
 
Черкаси 2022 року 
 
 
 
ЧДТУ.221819.002 ПЗ 
 
 
ЗМІСТ 
ВСТУП .................................................................................................................. 4 
1 ДОСЛІДЖЕННЯ ПРЕДМЕТНОЇ ОБЛАСТІ.................................................... 5 
1.1 Аналіз предметної області ......................................................................... 5 
1.2 Огляд існуючих аналогів............................................................................ 5 
2. ПОСТАНОВКА ЗАДАЧІ. ТЕХНІЧНЕ ЗАВДАННЯ ...................................... 8 
2.1 Мета та завдання роботи ............................................................................ 8 
2.2. Короткий опис та призначення ................................................................. 8 
2.3 Загальна характеристика системи.............................................................. 9 
2.4 Вимоги до обчислювального середовища............................................... 12 
2.5 Зв’язок із зовнішнім середовищем  .......................................................... 13 
2.6 Документація системи .............................................................................. 14 
2.7 Якість системи .......................................................................................... 19 
3. ПРОГРАМНО-ТЕХНІЧНІ ЗАСОБИ. ОПИС РОЗРОБЛЕНОЇ СИСТЕМИ  . 20 
3.1 Вибір програмно-технічних засобів ........................................................ 20 
3.2 Опис інтерфейсу та функціональних можливостей ............................... 29 
ВИСНОВОК ....................................................................................................... 38 
ВИКОРИСТАНА ЛІТЕРАТУРА  ....................................................................... 39 
ДОДАТОК А ...................................................................................................... 40 
ДОДАТОК Б ....................................................................................................... 42 
ДОДАТОК В..................................................................................................... 105 
ДОДАТОК Г ..................................................................................................... 115 
 
 
 
ЧДТУ.221819.002 ПЗ 
 
Зм. Лист №документа Підпис Дата 
Розроб. Аркалюк В.А. РОЗРОБКА ІНФОРМАЦІЙНОЇ СИСТЕМИ Літ. Лист Листів 
Керівник Куницька С.Ю. ПО УПРАВЛІННЮ ПЕРСОНАЛОМ 3 
КОМПАНІЇ  
 ФІТІС гр. ПЗ-184 
Н.контр. Півень О.Б. 
Затв. Первунінський С.М. (Пояснювальна записка) 3 
  
 
 Півень О. Б. 
ЧДТУ.221819.002 ПЗ 
ВСТУП 
В сучасному світі все прямує до автоматизації і спрощення. 
Автоматизація поступово захоплює різні сфери життя і якщо раніше 
намагалися автоматизувати і спростити якісь надтяжкі задачі, які значно 
полегшувалися після автоматизації, то зараз, коли найважливіші проблеми 
автоматизації знайшли рішення, прогрес взявся за автоматизацію повсякденних 
справ. 
При роботі над дипломним проектом було створено інформаційну 
систему для спрощення управління персоналом. Проект реалізований як веб-
додаток для простого доступу користувачів до системи. В ньому присутні всі 
необхідні для спрощення даної задачі функції, такі як: перегляд інформації про 
контракторів, можливість створення та опрацювання заяв, перегляд інформації 
про заяви за допомогою звітів у форматі xlsx та csv. 
Працівники HR-відділу зіткнулись із проблемою зберігання інформації 
про персонал і  відслідковування відсутностей. Вони все робили вручну і це 
займало дуже багато часу і це призводило до втрати актуальності інформації та 
людського фактору. Тому данний проект вирішує проблему співставлення 
інформації і ця система надає потрібний функціонал з простим і зручним 
інтерфейсом. 
  
4 
ЧДТУ.221819.002 ПЗ 
РОЗДІЛ 1 
ДОСЛІДЖЕННЯ ПРЕДМЕТНОЇ ОБЛАСТІ 
1.1 Аналіз предметної області 
Для виконання завдання необхідно вивчити предметну область, в якій ми 
будемо працювати, а саму тому потрібно зрозуміти основні аспекти в 
управлінні персоналом компанії. 
Управління персоналом — систематично-організаційний процес 
відтворювання і ефективного використання персоналу.  
Основним завданням інформаційної системи обліку є збереження повної  
інформації про працівників компанії та просте регулювання і відслідковування 
відсутностей. 
Головною метою роботи це розробка інформаційної системи для 
управління персоналом. 
1.2 Огляд існуючих аналогів 
1. hurma.work 
Завдання партнерської програми Hurma System — дати можливість 
експертам в HR- сфері ділитися корисним контентом, а брендам — бути 
почутими своєю аудиторією. Ще недавно автоматизація HR та рекрутингу була 
трендом, але сьогодні вона стала необхідністю та головним інструментом для 
роботи будь-якого фахівця з управління персоналом. Якісна HRM-система 
підвищує ефективність роботи не тільки HR-менеджера, а й кожної людини в 
компанії: від рядового співробітника до HRD і CEO. 
HRM - це CRM-система, яка допомагає працювати не з покупцями, а з 
власним персоналом. Це комплексна система автоматизації найму, відбору, 
залучення персоналу, забезпечення орієнтації, навчання і розвитку, оцінки їх 
роботи, визначення винагороди та надання пільг, мотивації, підтримки 
належних відносин з кожним членом команди та забезпечення безпеки роботи 
за рахунок застосування програм для HR і HR-аналітики. 
 
5 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 1.1 -  Зовнішній вигляд веб-додатку hurma.work 
 
Переваги: 
➢ можливість розміщення вакансій на джоб порталах; 
➢ проведення онбордингу, адаптації та офбордингу; 
➢ автоматичний розрахунок заробітної плати; 
➢ швидке додавання кандидатів до воронки; 
➢ HR та рекрутинг аналітика без Excel. 
 
Недоліки: 
➢ відсутність подачі заяв; 
➢ генерації звітів. 
1.2.1 peopleforce.io 
 
Переваги: 
➢ єдина система для інформації без таблиць та додаткового програмного 
забезпечення; 
➢ надійни спосіб зберігання даних; 
6 
ЧДТУ.221819.002 ПЗ 
➢ просте отримання інформації, хто буде відстуній в офісі протягом 
наступних кількох днів. 
 
Недоліки: 
➢ відсутність інтерфейсу для управління кількома користувачами; 
➢ убогість можливостей безкоштовної версії; 
➢ дорога вартість одного курса. 
 
 
Рисунок 1.2 - Зовнішній вигляд веб-додатку peopleforce.io 
 
 
 
 
7 
ЧДТУ.221819.002 ПЗ 
РОЗДІЛ 2 
ПОСТАНОВКА ЗАДАЧІ. ТЕХНІЧНЕ ЗАВДАННЯ 
 
2.1 Мета та завдання роботи 
Основна мета і завдання цієї дипломної роботи це розробка інформаційної 
системи, яка зможе вирішити проблему для HR-відділу перегляду інформації 
про контракторів компанії та надасть можливість відслідковувати відсутності 
контракторів та перегляд цієї інформації у вигляду звітів у форматі xlsx або csv. 
2.2. Короткий опис та призначення 
Система повинна надавати доступ лише тим користувачам, які 
зареєструвались.  
У процесі перегляду системи через браузер система розміщується на веб-
сервері, який буде працювати з «вихідними даними» і надавати користувачеві 
лише «кінцеві дані». В процесі отримання «кінцевих даних» також може 
призвести до «виняткових ситуацій».  
Вихідні дані: 
➢ відомості про виконавця та його заяву із зазначенням кількості днів та 
документів та їх зберігання. 
Кінцеві дані: 
➢ звітність. 
Виняткові ситуації: 
➢ втрата з'єднання з Інтернетом; 
➢ помилка в обробці даних; 
➢ втрата зв'язку з базою даних. 
Система міститиме функцію звітності. Структурований звіт буде 
представлено у форматі xlsx або csv файлу.  
Вихідні дані: 
➢ відомості про надходження і витрати коштів, місцях зберігання. 
Кінцеві дані: 
➢ статистика і баланс. 
8 
ЧДТУ.221819.002 ПЗ 
Виняткові ситуації: 
➢ втрата з'єднання з мережею інтернет; 
➢ закінчення сесії на сервері, виділене після авторизації користувача; 
➢ втрата з'єднання з базою даних. 
 
2.3 Загальна характеристика системи 
2.3.1 Сфера застосування 
Система буде використовуватись HR-відділом в компанії для 
управлінням персоналом та переглядом інформації про контракторів та його 
відсутностей. 
2.3.2 Функції системи  
Основні функції, які надає система користувачу:  
➢ авторизація користувача в систему; 
➢ перегляд інформації про всіх користувачів системи; 
➢ можливість редагування профіль контрактора; 
➢ створення заяв з різними типами; 
➢ опрацювання заяв для менеджера; 
➢ перегляд інформації про заяв у вигляді звіту. 
Схему бази даних проекту зображено на рисунку 2.1. 
 
9 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 2.1 – Схема бази даних контракторів 
 
 
10 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 2.2 – Схема бази даних заяв 
 
2.3.4 Результати, вихідні дані 
Користувач переходить по посиланню на систему і логіниться в систему. 
Для створення заяви потрібно перейти на сторінку Мої заяви та обрати 
потрібний тип заяви, після чого відкриється модальне вікно створення заяви, 
яке потрібно заповнити і подати запит на відсутність. Для перегляду інформації 
по контракторах потрібно перейти на сторінку Контрактори і на ній буде 
11 
ЧДТУ.221819.002 ПЗ 
відповідна таблиця з інформацією по всіх активних користувачам. Для 
генераціїї репортів потрібно залогінитись за користувача з ролю HR та перейти 
на сторінку Звіти та заповнити форму для генерації звітів і обрати формат звіту. 
При виконанні будь-якої операції в системі, якщо виникне якась помилка 
при відповіді від серверу, то користувач буде повідомлений відповідним 
випливаючим вікном з текстом помилки. 
 
2.4 Вимоги до обчислювального середовища  
2.4.1 Склад та конфігурація апаратних засобів  
Мінімальна конфігурація: 
➢ тип процесора: pentium і вище; 
➢ об’єм оперативної пам’яті: 512 Мб і більше. 
Рекомендована конфігурація: 
➢ тип процесора: intel t4300; 
➢ об’єм оперативної пам’яті: 500 Мб. 
2.4.2 Програмні засоби 
Інформаційна система встановлюється на сервер, який повинен 
задовольняти наступні вимоги програмного забезпечення: 
➢ підтримка PostgreSQL 13; 
➢ підтримка RabbitMQ; 
➢ підтримка ReactJS 16.13.1; 
➢ підтримка NodeJS 16.15.1 LTS. 
2.4.3 Режим роботи 
Програмно система матиме 3 режими роботи: 
− режим контрактора; 
− режим менеджера; 
− режим адміністратора. 
 
 
 
12 
ЧДТУ.221819.002 ПЗ 
2.5 Зв’язок із зовнішнім середовищем  
2.5.1 Формати і протоколи обміну для вхідних даних  
Система має HTTP протокол обміну для вхідних даних. HTTP — це 
протокол передачі даних, що використовується в комп'ютерних мережах. Назва 
скорочена від HyperText Transfer Protocol, протокол передачі гіпертекстових 
документів. 
HTTP належить до протоколів моделі OSI 7-го прикладного рівня. 
Основним призначенням протоколу HTTP є передача вебсторінок (текстових 
файлів з розміткою HTML), хоча за допомогою нього успішно передаються як 
інші файли, які пов'язані з вебсторінками (зображення та застосунки), так і не 
пов'язані з ними (у цьому HTTP конкурує зі складнішим FTP). 
HTTP припускає, що клієнтська програма — веббраузер — здатна 
відображати гіпертекстові вебсторінки та файли інших типів у зручній для 
користувача формі. Для правильного відображення HTTP дозволяє клієнтові 
дізнатися мову та кодування символів вебсторінки й/або запитати версію 
сторінки в потрібних мові/кодуванні, використовуючи позначення зі стандарту 
MIME. 
 
2.5.2 Формати і протоколи для вихідних даних  
Вихідна інформація буде представлена в вигляді таблиць. Таблиця має 
назву колонку та значення в цих колонках. Приклад таблиці наведено на 
рисунку 2.3. 
 
13 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 2.3 – Приклад таблиці 
 
2.5.3 Формати та правила зміни керуючих параметрів 
Варто зазначити що в користувачів є ролі. В кожної ролі є перміції, які 
надають доступ до функцій або окремих сторінок. Доступ до зміни інформації 
матимуть зареєстровані користувачі системи.  
Так як ми використовуємо реляційну базу даних  PostgreSQL, якщо 
користувач створить новий запис в базу на уже існуючий, то ці зміни не будуть 
прийняті. Змінити поля для бізнес моделей матиме можливість менеджер або 
адміністратор і в меншій мірі контрактор. 
 
2.6 Документація системи  
2.6.1 Перелік та вимоги до опису інструкції користувача  
Розроблювана система повинна мати короткий опис кожної сторінки та 
описом всіх можливих флоу. 
Вимоги до опису інструкції користувача: 
➢ опис модальних вікон системи; 
➢ опис полів введення даних; 
➢ опис кнопок системи; 
14 
ЧДТУ.221819.002 ПЗ 
➢ опис посилань системи; 
➢ проблематику, яку вирішує система; 
➢ опис функцій системи. 
2.6.2 Вимоги до оформлення вихідних кодів  
Програмний вихідний код системи повинен мати тести, які нададуть 
системі надійності роботи та забезпечить подальшу розробку без завдання 
шкоди системи. Також вихідний код повинен мати коментарі з описом методів 
та моделей в системі. 
Мікросервіси системи повинні мати детальний опис за що вони 
відповідають в README в Gitlab. 
2.6.3 Вимоги до опису структури і формати даних  
На етапі розробки система повинна мати формальні моделі свого 
існування: 
➢ DFD – діаграма потоків даних Вона показує структурний аналіз системи, 
описує зовнішні по відношенню до системи джерела даних, логічні 
функції, потоки та сховища даних до яких є доступ; 
➢ діаграма варіантів використання; 
➢ IDEF0 – методологія функціонального моделювання і графічна нотація, 
призначена для формалізації і опису бізнес-процесів. Відмінною 
особливістю IDEF0 є її акцент на підпорядкованість об'єктів. Модель 
IDEF0 системи зображена на рисунку; 
➢ діаграма декомпозиції – система розбивається на підсистеми і кожна 
підсистема описується окремо. Діаграма декомпозиції системи зображена 
на рисунку 2.4. 
 
15 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 2.4– Діаграма DFD 
16 
ЧДТУ.221819.002 ПЗ 
 
 
Рисунок 2.5 – Діаграма варіантів використання 
17 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 2.6 – Модель IDEF0 
 
 
Рисунок 2.7 – Діаграма декомпозиції 
 
18 
ЧДТУ.221819.002 ПЗ 
2.7 Якість системи  
2.7.1 Виконання стандартів та узгодження  
На створення та виконання було узгоджено стандарт ДЕСТ 34. ДЕСТ 34 – 
технічне завдання на створення автоматизованої системи, яка регламентує 
структуру технічного завдання на створення саме системи, в яку входять: 
− програмне забезпечення; 
− апаратне забезпечення; 
− люди, які працюють з програмним забезбеченням та системою; 
− автоматизовані процеси системи. 
2.7.2 Можливість перенесення 
Для переносу даної системи на інший сервер не складає ніякої складності, 
або можливості локальної розробки. Для того, щоб розробляти систему 
локально на своєму персональному комп’ютері потрібно склонувати 
репозиторій з кодом собі на комп’ютер і встановити необхідні технології, як: 
React, NodeJS, PostgreSQL, Docker, RabbitMQ та інструменти для розробки, як: 
WebStorm та Intelijj Idea. 
Для того щоб перенести систему на інший сервер, потрібно також 
склонувати репозиторій з кодом та огорнути кожен мікросервіс в docker 
контейнер і запустити їх. 
2.7.3 Надійність функціонування 
 Система повинна налічувати в собі ряд unit та e2e тестів, які мають 
зменшити ризики виникнення критичних дефектів в систем та забезпечити 
стабільну роботу системи. 
Передбачення внесення некоретних даних в систему і зменшення 
кількості запитів в базу даних, які можуть впливати на швидкодію системи. 
Захист системи від дудос атак або взлому шахраїв, які можуть зробити 
систему недієвою або викрасти конфіденційну інформацію про працівників 
компанії. 
Якщо дотримуватись вище перелічених вимог система буде працювати 
швидко, ефективно і надійно. 
19 
ЧДТУ.221819.002 ПЗ 
РОЗДІЛ 3 
ПРОГРАМНО-ТЕХНІЧНІ ЗАСОБИ. ОПИС РОЗРОБЛЕНОЇ СИСТЕМИ 
 
3.1 Вибір програмно-технічних засобів  
Для розробки  інформаційної системи для управління персоналом були 
використані наступні технології описані в цьому розділі. 
3.1.1 React 
React - це бібліотека для створення інтерфейсів користувача. React не є 
фреймворком - він навіть не розрахований виключно на web. Він 
використовується для візуалізації та у зв'язці з іншими бібліотеками. 
Наприклад, React Native можна використовувати для створення мобільних 
програм; React 360 можна використовувати для створення програм віртуальної 
реальності; Крім того, є й інші варіанти. 
Для створення веб-додатків розробники використовують React у тандемі з 
ReactDOM. React and ReactDOM часто обговорюються в тому ж просторі і 
використовуються для вирішення тих же проблем, що й інші реальні 
фреймворки для веб-розробки. Коли ми посилаємося на React як на 
"фреймворк", ми маємо на увазі це розмовне розуміння. 
Основна мета React - мінімізувати помилки, що виникають при розробці 
інтерфейсів користувача. Це досягається за рахунок використання компонентів 
- автономних логічних фрагментів коду, які описують частину інтерфейсу 
користувача. А вже ці компоненти об'єднуються для створення повноцінного 
інтерфейсу користувача. React абстрагує більшість роботи з візуалізації, 
залишаючи вам можливість зосередитися на дизайні. 
На відміну від інших платформ React не зобов'язує до суворих правил 
щодо угод про код або організацію файлів. Це дозволяє командам домовлятися, 
що їм підходить, і структурувати React проект відповідним чином. React може 
відповідати за одну кнопку, кілька частин або весь користувальницький 
інтерфейс програми. 
20 
ЧДТУ.221819.002 ПЗ 
Хоча React можна використовувати для маленьких елементів інтерфейсу, 
«зайти» в нього не так просто, як, наприклад, в jQuery, або навіть у Vue. Куди 
легше це зробити створивши всю програму за допомогою React. 
Крім того, такі переваги React-програми, як написання інтерфейсів за 
допомогою JSX, вимагають процесу компіляції. Додавання на сайт компілятора 
Babel призводить до більш повільного виконання коду, тому такі інструменти 
зазвичай налаштовуються для процесу збирання. Так, можливо, React має 
серйозні вимоги до інструментарію, але його можна освоїти. 
3.1.2 RabbitMQ 
RabbitMQ — платформа, що реалізує систему обміну повідомленнями 
між компонентами програмної системи на основі стандарту AMQP (Advanced 
Message Queuing Protocol). 
RabbitMQ – це реалізація AMQP (Advanced Message Queuing Protocol) з 
відкритим вихідним кодом, розроблена Erlang. Поява AMQP насправді є 
відповіддю потреби широких мас людей. Хоча у світі синхронного обміну 
повідомленнями є багато відкритих стандартів (таких як IIOP Cobar, SOAP 
тощо. буд.), це стосується асинхронної обробці повідомлень. Підприємства 
мають деякі комерційні реалізації (такі як Microsoft MSMQ, IBM WebSphere 
MQ і т. д.). Тому у червні 2006 року Cisco, Red Hat, iMatix тощо. Спільно 
розробили громадський стандарт AMQP. 
RabbitMQ розроблено RabbitMQ Technologies Ltd і надає комерційну 
підтримку. Компанія була придбана SpringSource (підрозділ VMware) у квітні 
2010 року. Він був об'єднаний з Pivotal у травні 2013 року. Насправді VMware, 
Pivotal та EMC – це, по суті, одна сім'я. Різниця полягає в тому, що VMware є 
незалежною дочірньою компанією, в той час як Pivotal об'єднує деякі ресурси 
EMC і зараз не зареєстрована. Далі перелічимо основні складові RabbitMQ. 
RabbitMQ Server 
Також званий Broker Server, це не вантажівка, яка доставляє їжу, а сервіс 
передачі. Вихідна пропозиція: RabbitMQ - це не вантажівка, а служба доставки, 
його роль у тому, щоб підтримувати маршрут від виробника до споживача, щоб 
21 
ЧДТУ.221819.002 ПЗ 
забезпечити можливість передачі даних певним способом. Хоча ця гарантія не є 
100% гарантією, вона є достатньою для звичайних додатків. Звичайно, для 
комерційних систем можна забезпечити ще один рівень захисту узгодженості 
даних, щоб забезпечити повністю узгодженість системи. 
Client P 
Також називається Producer, відправник даних. Створює повідомлення та 
публікує їх на сервер брокера (RabbitMQ). Повідомлення складається з двох 
частин: корисне навантаження (корисне навантаження) та мітка (мітка). 
Корисне навантаження, як випливає з назви, є передані дані. Мітка - це ім'я 
біржі або тега, який описує корисне навантаження, і RabbitMQ також 
використовує цю мітку, щоб визначити, кому Споживач надіслати це 
Повідомлення. AMQP описує лише мітку, а RabbitMQ визначає правила 
використання цієї мітки. 
Також називається Consumer, одержувач даних. Споживачі 
підключаються до сервера брокера (RabbitMQ) та підписуються на чергу. 
Порівняйте чергу з іменованим поштовою скринькою. Коли повідомлення 
надходить до певної поштової скриньки, RabbitMQ відправляє його одному зі 
своїх передплатників, який є Споживачем. Звичайно, те саме повідомлення 
може бути надіслано багатьом споживачам. У цьому повідомленні лише 
корисне навантаження та мітка були видалені. Споживач не знає, хто надіслав 
цю інформацію, але сам протокол не підтримує її. Звичайно, це інше питання, 
якщо корисне навантаження, надіслане Виробником, містить інформацію 
Виробника. 
Для правильної передачі даних від виробника до споживача необхідно 
прояснити ще три поняття: обміни, черги та прив'язки. 
Є також кілька концепцій, зокрема, З'єднання (з'єднання) і Канал (канал, 
канал). 
Connection 
22 
ЧДТУ.221819.002 ПЗ 
Це з'єднання TCP. Як виробник, і споживач підключені до серверу 
RabbitMQ через TCP. Як побачимо пізніше, це TCP-з'єднання встановлюється 
на початку програми. 
Channel 
Віртуальне з'єднання. Це встановлено у вищезгаданому з'єднанні TCP. 
Потік даних здійснюється у каналі. Іншими словами, загальна ситуація така, що 
TCP-з'єднання встановлюється на початку програми, і другим кроком є 
встановлення каналу. 
3.1.3 PostgreSQL  
PostgreSQL - це об'єктно-реляційна система управління базами даних 
(ОРСУБД, ORDBMS), яка базується на POSTGRES, Version 4.2 - програмі, 
розробленій на факультеті комп'ютерних наук Каліфорнійського університету в 
Берклі. У POSTGRES з'явилося багато нововведень, які були реалізовані в 
деяких комерційних СУБД набагато пізніше. 
PostgreSQL - СУБД з відкритим вихідним кодом, основою якого був код, 
написаний Берклі. Вона підтримує більшу частину стандарту SQL і пропонує 
безліч сучасних функцій: 
➢ складні запити; 
➢ зовнішні ключі; 
➢ тригери; 
➢ змінні уявлення; 
➢ транзакційна цілісність; 
➢ багатоверсійність. 
 
Крім того, користувачі можуть всіляко розширювати можливості 
PostgreSQL, наприклад, створюючи свої: 
➢ типи даних; 
➢ функції; 
➢ оператори; 
➢ агрегатні функції; 
23 
ЧДТУ.221819.002 ПЗ 
➢ методи індексування; 
➢ процедурні мови. 
 
А завдяки вільній ліцензії, PostgreSQL дозволяється безкоштовно 
використовувати, змінювати та розповсюджувати всім і для будь-яких цілей – 
особистих, комерційних чи навчальних. 
Об'єктно-реляційна система управління базами даних, що називається 
сьогодні PostgreSQL, походить від пакету POSTGRES, написаного в Берклі, 
Каліфорнійському університеті. Після двох десятиліть розробки PostgreSQL 
став найрозвиненішою СУБД із відкритим вихідним кодом. 
Проект POSTGRES, який очолює професор Майкл Стоунбрейкер, 
спонсорували агентство DARPA при Міноборони США, Управління військових 
досліджень (ARO), Національний Науковий Фонд (NSF) і компанія ESL, Inc. 
Реалізація POSTGRES почалася 1986 р. Початкові концепції системи було 
представлено у документі The design of POSTGRES, а опис першої моделі 
даних з'явилося The POSTGRES data model . Проект системи правил був 
представлений у The design of the POSTGRES rules system. Суть та архітектура 
менеджера сховища були розписані у The design of the POSTGRES storage 
system. 
З того часу POSTGRES пройшов кілька етапів розвитку. Перша 
"демоверсія" запрацювала в 1987 році і була показана в 1988 році на 
конференції ACM-SIGMOD. Версія 1, описана в The implementation of 
POSTGRES, була випущена для кількох зовнішніх користувачів у червні 1989. 
У відповідь на критику першої системи правил (A commentary on the 
POSTGRES rules system), вона була перероблена (On Rules, Procedures, Caching 
and Views in Database Systems), і у версії 2, випущеної червні 1990, була вже 
нова система правил. У 1991 вийшла версія 3, в якій з'явилася підтримка різних 
менеджерів сховища, покращений виконавець запитів та переписана система 
правил. Наступні випуски до Postgres95 в основному були спрямовані на 
покращення портованості та надійності. 
24 
ЧДТУ.221819.002 ПЗ 
POSTGRES застосовувався для реалізації безлічі дослідницьких та виробничих 
завдань. Серед них: система аналізу фінансових даних, пакет моніторингу 
роботи реактивних двигунів, база даних спостережень за астероїдами, база 
даних медичної інформації, кілька географічних інформаційних систем. 
POSTGRES також використовувався для навчання у кількох університетах. 
Нарешті, компанія Illustra Information Technologies (яка пізніше стала частиною 
Informix, яка зараз належить IBM) скористалася кодом і знайшла йому 
комерційне застосування. Наприкінці 1992 р. POSTGRES став основною СУБД 
наукового обчислювального проекту Sequoia 2000. 
У 1993 році число зовнішніх користувачів подвоїлося. Стало очевидно, 
що обслуговування коду та підтримка займає дуже багато часу, і його не 
вистачає на дослідження. Для зниження цього навантаження проект POSTGRES 
у Берклі був офіційно закритий на версії 4.2. 
 
Postgres95 
У 1994 році Ендрі Ю і Джоллі Чен додали в POSTGRES інтерпретатор 
мови SQL. Вже з новим ім'ям Postgres95 був опублікований в Інтернеті і 
розпочав свій шлях як нащадок розробленого в Берклі POSTGRES, з відкритим 
вихідним кодом. 
Код Postgres95 був приведений у повну відповідність до ANSI C і 
зменшився на 25%. Завдяки безлічі внутрішніх змін він став швидшим і 
зручнішим. Postgres95 версії 1.0.x працював приблизно на 30-50% швидше за 
POSTGRES версії 4.2 (за тестами Wisconsin Benchmark). Крім виправлення 
помилок, відбулися такі зміни: 
➢ на зміну мови запитів PostQUEL надійшов SQL (реалізований на сервері). 
(Інтерфейсна бібліотека libpq успадкувала своє ім'я від PostQUEL.) Підзапити 
не підтримувалися до виходу PostgreSQL (див. нижче), хоча їх можна було 
імітувати в Postgres95 за допомогою функцій SQL. Були наново реалізовані 
агрегатні функції. Також виникла підтримка пропозиції GROUP BY. 
25 
ЧДТУ.221819.002 ПЗ 
➢ Для інтерактивних SQL-запитів було розроблено нову програму (psql), яка 
використовувала GNU Readline. Стара програма monitor стала непотрібною. 
➢ З'явилася нова клієнтська бібліотека libpgtcl для підтримки клієнтів Tcl. 
Приклад оболонки, pgtclsh, представляв нові команди Tcl взаємодії програм Tcl 
із сервером Postgres95. 
➢ Було вдосконалено інтерфейс до роботи з великими об'єктами. Єдиним 
механізмом збереження таких даних стали інверсійні об'єкти. (Інверсійна 
файлова система була видалена.) 
➢ Видалено систему правил на рівні примірників; перезаписуючі правила 
збереглися. 
➢ З вихідним кодом стали поширюватися короткі описи можливостей 
стандартного SQL, а також Postgres95. 
➢ Для складання використовувався GNU make (замість BSD make). Крім того, 
стало можливо скомпілювати Postgres95 з немодифікованою версією GCC 
(виправлено вирівнювання даних). 
У 1996 р. стало зрозуміло, що ім'я Postgres95 не витримає випробування 
часом. Ми вибрали нове ім'я PostgreSQL, що відображає зв'язок між 
оригінальним POSTGRES і пізнішими версіями з підтримкою SQL. У той же 
час ми продовжили нумерацію версій з 6.0, повернувшись до послідовності, 
розпочатої в проекті Берклі POSTGRES. 
Багато хто продовжує називати PostgreSQL ім'ям "Postgres" (тепер вже 
рідко великими літерами) за традицією або для простоти. Ця назва закріпилася 
як псевдонім чи неформальне позначення. 
У процесі розробки Postgres95 основними завданнями були пошук і 
розуміння проблем у серверному коді. З переходом до PostgreSQL акценти 
змістилися до реалізації нових функцій та можливостей, хоча робота 
продовжується у всіх напрямках.  
Говорячи технічною мовою, PostgreSQL реалізований в архітектурі 
клієнт-сервер. Робочий сеанс PostgreSQL включає наступні взаємодіючі 
процеси (програми). 
26 
ЧДТУ.221819.002 ПЗ 
Головний серверний процес, що управляє файлами баз даних, приймає 
підключення клієнтських додатків та виконує різні запити клієнтів до баз 
даних. Ця програма сервера БД називається postgres. 
Клієнтська програма користувача, яка бажає виконувати операції в базі 
даних. Клієнтські програми можуть бути дуже різноманітними: це може бути 
текстова утиліта, графічна програма, веб-сервер, що використовує базу даних 
для відображення веб-сторінок, або спеціалізований інструмент для 
обслуговування БД. Деякі клієнтські програми поставляються у складі 
дистрибутива PostgreSQL, проте більшість створюють сторонні розробники. 
Як і в інших типових клієнт-серверних програмах, клієнт і сервер можуть 
розташовуватися на різних комп'ютерах. У цьому випадку вони взаємодіють 
через мережу TCP/IP. Важливо не забувати і розуміти, що файли, доступні на 
клієнтському комп'ютері, можуть бути недоступні (або доступні лише під 
іншим ім'ям) на комп'ютері-сервері. 
Сервер PostgreSQL може одночасно обслуговувати кілька підключень 
клієнтів. І тому він запускає ( " породжує " ) окремий процес кожного 
підключення. Можна сказати, що клієнт та серверний процес спілкуються, не 
торкаючись головного процесу postgres. Таким чином, головний серверний 
процес завжди працює і чекає на підключення клієнтів, приймаючи які, він 
організує взаємодію клієнта та окремого серверного процесу. (Звичайно, все це 
відбувається непомітно для користувача, а ця схема розглядається тут тільки 
для розуміння.) 
3.1.4 Node.js 
Node.js – це середовище виконання JavaScript з відкритим кодом, яке 
працює на стороні сервера на основі ядра JavaScript V8 (Chrome). Вона була 
створена Райаном Далем (Ryan Dahl) і випущена у 2009 році. 
Node.js в основному використовується для створення швидких та 
масштабованих веб-застосунків. При цьому використовується керована подіями 
неблокуюча модель вводу-виводу, що робить цю платформу простою та 
ефективною. Це відмінне рішення для розробки програм реального часу, що 
27 
ЧДТУ.221819.002 ПЗ 
обробляють великі обсяги даних і виконуються на розподілених пристроях. 
Нижче наведено кілька прикладів, які можна створити за допомогою Node.js: 
➢ Односторінкові програми. Це веб-програми, які працюють у 
браузері та не вимагають повторного завантаження сторінки при кожному її 
використанні для отримання нових даних. Деякі приклади односторінкових 
програм включають програми соціальних мереж, електронну пошту, програми 
карт, текст в Інтернеті, інструменти для малювання і т. д. 
➢ Програми у режимі реального часу. Це веб-застосунки, які 
сприяють користувачам отримувати інформацію відразу після публікації 
автором, а не вимагати періодичних перевірок джерела на наявність оновлень. 
Деякі приклади RTA включають програми для обміну миттєвими 
повідомленнями та кімнати чатів, розраховані на багато користувачів онлайн-
ігри, які можна грати в браузері, документи для спільної роботи в мережі, 
сховище спільноти, програми відеоконференцій і т. д. 
➢ Програми потокової передачі даних. Це програми (або служби), які 
надсилають дані або вміст у міру їх надходження (або створення), зберігаючи 
підключення для продовження завантаження додаткових даних, вмісту або 
компонентів у міру необхідності. До деяких прикладів належать програми 
потокової передачі відео та аудіо. 
➢ API REST. Це інтерфейси, які надають дані для взаємодії з веб-
програмою іншого користувача. Наприклад, календарний календар API може 
надавати дати та час для концертного приміщення, яке може 
використовуватися веб-сайтом локальних подій іншого користувача. 
➢ Готові до перегляду програми на стороні сервера (SSR). Ці веб-
програми можуть працювати як на клієнті (у вашому браузері/зовнішньому 
інтерфейсі), так і на сервері (внутрішній), дозволяючи динамічно відображати 
(створювати HTML) будь-який відомий вміст і швидко перехоплювати 
невідомий у міру його доступності. Їх часто називають "ізоморфними" або 
"універсальними" додатками. Служби SSR використовують методи SPA, 
оскільки вони не повинні перезавантажуватися при кожному використанні. Тим 
28 
ЧДТУ.221819.002 ПЗ 
не менш, SSR пропонують кілька переваг, які можуть бути або не бути 
важливими для вас, наприклад, включення вмісту вашого сайту в результати 
пошуку Google і надання зображення для попереднього перегляду, коли 
посилання на вашу програму публікуються в соціальних мережах, таких як 
Twitter або Facebook. Потенційним недоліком є те, що вони потребують 
постійної роботи сервера Node.js. З точки зору прикладів, програма для 
соціальних мереж, що підтримує події, які користувачі захочуть бачити в 
результатах пошуку та соціальних мережах, може отримати вигоду з SSR, в той 
час як програма для електронної пошти може добре працювати як SPA. Ви 
також можете запускати перетворені сервером програми, відмінні від SPA, 
наприклад, блог WordPress. Як бачите, все може ускладнитись, вам просто 
варто вирішити, що важливо. 
➢ Кошти командного рядка. Вони дозволяють автоматизувати 
завдання, що повторюються, а потім поширити ваш засіб по великій екосистемі 
Node.js. Прикладом засобу командного рядка є cURL, під яким мається на увазі 
клієнтська URL-адреса і який використовується для завантаження контенту з 
URL-адреси Інтернету. cURL часто використовується для встановлення таких 
компонентів як Node.js, в нашому випадку менеджера версій Node.js. 
➢ Програмування устаткування. Хоча Node.js не такий популярний, як 
веб-програми, в даний час Node.js користується все більшою популярністю для 
використання в IoT, наприклад, для збору даних з датчиків, маяків, передавачів, 
двигунів або всього, що генерує великі обсяги даних. Node.js може забезпечити 
збір даних, аналіз цих даних, обмін даними між пристроєм та сервером та 
вживання заходів на основі аналізу. NPM містить більше 80 пакетів для 
контролерів Arduino, raspberry pi, Intel IoT Edison, різних датчиків та пристроїв 
Bluetooth. 
3.1.5 Git 
Git — розподілена система керування версіями файлів та спільної роботи. 
Проект створив Лінус Торвальдс для керування розробкою ядра Linux, а 
сьогодні підтримується Джуніо Хамано.  
29 
ЧДТУ.221819.002 ПЗ 
Git є однією з найефективніших, надійних і високопродуктивних систем 
керування версіями, що надає гнучкі засоби нелінійної розробки, що базуються 
на відгалуженні і злитті гілок. Для забезпечення цілісності історії та стійкості 
до змін заднім числом використовуються криптографічні методи, також 
можлива прив'язка цифрових підписів розробників до тегів і комітів.  
Прикладами проектів, що використовують Git, є ядро Linux, Android, 
LibreOffice, Cairo, GNU Core Utilities, Mesa 3D, Wine, багато проектів з X.org, 
XMMS2, GStreamer, Debian DragonFly BSD, Perl, Eclipse, GNOME, KDE, Qt, 
Ruby on Rails, PostgreSQL, VideoLAN, PHP, One Laptop Per Child (OLPC), АБІС 
Koha, GNU LilyPond та ELinks і деякі дистрибутиви GNU/Linux. Система 
спроектована як набір програм, спеціально розроблених з врахуванням їхнього 
використання у скриптах. Це дозволяє зручно створювати спеціалізовані 
системи керування версіями на базі Git або користувацькі інтерфейси.  
Наприклад, Cogito є саме таким прикладом фронтенда до репозиторіїв 
Git. А StGit використовує Git для управління колекцією латок. Система має ряд 
користувацьких інтерфейсів: наприклад, gitk та git-gui розповсюджуються з 
самим Git. Віддалений доступ до репозиторіїв Git забезпечується git-демоном, 
SSH або HTTP сервером. TCP-сервіс git-daemon входить у дистрибутив Git і є 
разом з SSH найпоширенішим і надійним методом доступу. Метод доступу 
HTTP, незважаючи на ряд обмежень, дуже популярний в контрольованих 
мережах, тому що дозволяє використання існуючих конфігурацій мережевих 
фільтрів. 
1.2 Опис інтерфейсу та функціональних можливостей 
3.2.1 Процес авторизації користувача 
Для того, щоб авторизуватись користувачу у систему, потрібно:  
- зайти на сайт системи;  
- у формі авторизації потрібно ввести логін та пароль облікового 
запису користувача, який йому надав адміністратор системи та 
натиснути на кнопку «Увійти».  
Форма авторизації в систему зображено на рисунку 3.1. 
30 
ЧДТУ.221819.002 ПЗ 
 
 
Рисунок 3.1 – Форма авторизації користувача 
 
3.2.2 Процес створення заяви 
Для того, щоб створити заяву потрібно:  
➢ зайти на сайт системи;  
➢ натиснути на кнопку «Подати заяву» та обрати потрібний тип заяви;  
➢ після цього відкриється форма створення заяви.  
 
Рисунок 3.2 – Форма створення заяви  
 
3.2.3 Процес перегляду створених заяв користувача 
Для того, щоб переглянути створені заяви користувача у системі, потрібно:  
31 
ЧДТУ.221819.002 ПЗ 
➢ зайти на сайт веб-додатку;  
➢ у формі авторизації потрібно ввести логін та пароль облікового запису 
користувача, який надав адмін системи та натиснути на кнопку «Увійти».  
Після процесу авторизації, користувачу за замовчуванням відкриється сторінка 
його заяв. Сторінка заяв зображено на рисунку 3.3. 
Сторінка заяв користувача складається з:  
➢ таблиця зі створеними заявами користувача (рисунок 3.3);  
➢ меню навігації веб-додатку (рисунок 3.4);  
➢ блок з балансом днів (рисунок 3.5). 
 
 
Рисунок 3.3 – Сторінка заяв користувача 
 
 
32 
ЧДТУ.221819.002 ПЗ 
 
 
 
Рисунок 3.4 – Меню навігації системи 
 
33 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 3.5 – Блок балансу днів користувача 
Блок балансу днів користувача відображає наступну інформацію:  
➢ баланс днів на відпочинок;  
➢ баланс днів на відпочинок з урахуванням поданих заяв;  
➢ компенсацію на місяць за станом здоров’я. 
 
3.2.4 Процес опрацювання заяви менеджером 
Після того, як контрактор створив заяву потрібно щоб менеджер її 
опрацював. Для того, щоб це зробити менеджер повинен залогінитись в 
систему і перейти на сторінку Заяви на якій відображаються заяви усіх 
контракторів. Після цього знайти потрібну заяву, натиснути на контекстне 
меню і обрати пункт “Опрацювати” і відкриється модальне вікно опрацювання 
заяви, яке зображено на рисунку. Менеджер має дві опції або підтвердити заяву 
або відхилити її. Після вибору із цих двох опцій заява змінює свій стан на 
“Підтверджений” або “Відхилений”. 
 
34 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 3.6 – Модальне вікно опрацювання заяви 
 
3.2.5 Процес перегляду інформації про контракторів компанії 
Для перегляду інформації про рахунки користувача потрібно:  
➢ зайти на сайт веб-додатку;  
➢ у формі авторизації потрібно ввести електронну адресу та пароль 
облікового запису користувача та натиснути на кнопку «Увійти»;  
➢ у меню навігації веб-додатку клікнути на пункт «Контрактори»;  
➢ в таблиці обрати потрібного контрактора і відкриється профіль 
контрактора.  
Сторінку перегляду інформації про контрактора зображено на рисунку.  
 
 
35 
ЧДТУ.221819.002 ПЗ 
 
Рисунок 3.8 – Сторінку перегляду інформації про контрактора компанії 
 
Сторінку перегляду інформації про контрактора складається з кнопки 
«Доступні дії» та блоку з інформацією та аватаркою користувача.  
В блоці з інформацією користувача відображаєть наступна інформація 
про нього: 
➢ ПІП;  
➢ дата народження;  
➢ відділ;  
➢ позиція;  
➢ студент;  
➢ телефон;  
➢ дата, коли був найнятий;  
➢ корпоративний емейл. 
3.2.5 Процес генерації звітів по заявам користувача 
Для того щоб згенерувати звіт потрібно обрати наступні поля:  
➢ дата початку;  
36 
ЧДТУ.221819.002 ПЗ 
➢ дата закінчення;  
➢ відділ − формат звіту (xlsx, csv)  
Сторінку створення рахунку користувача зображено на рисунку 3.9. 
 
Рисунок 3.9 – Сторінка звітів по заявам 
 
Після генерації репорту він має наступний вигляд зображено на рисунку 
3.10. Звіт має наступні колонки з інформацією по заявам:  
➢ ім’я, хто подав заяву;  
➢ ID;  
➢ відділ; 
➢ тип;  
➢ статус;  
➢ дата початку; 
➢ дата закінчення;  
➢ кількість днів;  
➢ до компенсації;  
➢ коментар. 
 
Рисунок 3.10 – Вигляд звіту по заявам 
37 
ЧДТУ.221819.002 ПЗ 
 
ВИСНОВОК 
 
У результаті виконання випускної бакалаврської роботи були отримані 
навички по розробці мікросервісної архітектури та взаємодії між 
мікросервісами та закріплені навички по web-розробці. В рамках роботи над 
випускною бакалаврською роботою було реалізовано інформаційна система для 
управління персоналом. 
В процесі розробки системи, вона пройшла всі необхідні етапи, а саме: 
огляд предметної області, проектування, реалізація, тестування та 
впровадження системи або розгортання. 
На етапі огляду предметної області була проведена робота над 
проблематикою та вимогами по необхідним функціях системи. 
На етапі проектування були створені: діаграму використання, діаграму 
потоків даних, діаграма декомпозиції. 
На етапі реалізації системи, були реалізовані наступні функції: 
авторизація користувача в систему, перегляд створених заяв, 
створення/редагування/опрацювання заяви, перегляд інформації по 
контракторам компанії. 
На етапі тестування були написані unit та e2e тести на всі доступні дії для 
користувача. 
Для розробки системи було використані такі програмні засоби, як: 
JetBrains Intelijj Idea та WebStorm, GitLab, Draw.io. Для  розробки роботи 
системи було використано такі сучасні framework як: React та NodeJS.  
В майбутньому планується додати до системи нові функції, а саме: режим 
проектів та назначень та режим заміток по контрактору. 
 
38 
ЧДТУ.221819.002 ПЗ 
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ 
 
1 Фовлер М., Рефакторинг, 2003. 50 – 70 ст. 
2 Макконнелл С., Скільки коштує програмний продукт, 2008, 91 –  100 ст. 
3 Фізерс М., Ефективна робота з унаслідкованим кодом, 2009, 120 – 130ст. 
4 Документація по React [Електронний ресурс] https://reactjs.org. 
5 Документація по PostgreSQL [Електронний ресурс] 
https://www.postgresql.org 
6 Документація по Nodejs [Електронний ресурс] https://nodejs.org/uk 
7 Документація по Git [Електронний ресурс] https://git-scm.com 
8 Документація по RabbiMQ [Електронний ресурс] 
https://www.rabbitmq.com/documentation.html 
 
 
39 
ДОДАТОК А 
 
ЗАТВЕРДЖЕНО: 
Зав. кафедрою ПЗАС, професор 
_____________ Первунінський С.М. 
„____” _________________ 2022 р. 
 
 
 
 
РОЗРОБКА ІНФОРМАЦІЙНОЇ СИСТЕМИ ПО УПРАВЛІННЮ 
ПЕРСОНАЛОМ КОМПАНІЇ 
 
Специфікація 
482. ЧДТУ 221819.002   
Листів 2 
 
 
 
Розробник ________________ Аркалюк В.А. 
Керівник ________________ Куницька С.Ю. 
 
 
 
 
 
 
 
 
2022 рік
ЧДТУ.221819.002 ПЗ 
 
Позначення Найменування Примітка 
482.ЧДТУ. 221819-01 12-01 Текст програми   
482.ЧДТУ. 221819-01 34-01 Інструкція користувачеві  
482.ЧДТУ. 221819-01 90-01 Графічні матеріали  
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
41 
Додаток Б 
ДОДАТОК Б 
 
 
 
 
 
 
РОЗРОБКА ІНФОРМАЦІЙНОЇ СИСТЕМИ ПО УПРАВЛІННЮ 
ПЕРСОНАЛОМ КОМПАНІЇ 
Текст програми 
482.ЧДТУ. 221819-01 12-01 
 
Листів 62 
 
 
 
 
Розробник: ________________________________________Аркалюк В.А. 
 
 
 
 
 
 
 
 
 
Черкаси, 2022
ЧДТУ.221819.002 ПЗ 
Текст програми 
Текст файлу dialog-add-leave.html 
 
<div class="modal-window"> 
  <div class="modal-window-header"> 
    <h2 mat-dialog-title class="modal-window-header-title"> 
      <span *ngIf="!showDropdown"> 
       {{ 'ContractorsModule.AddLeaves.TitleLeave' | translate  }} 
      </span> 
      <span *ngIf="showDropdown"> 
      {{ title | translate }} 
      </span> 
    </h2> 
    <span class="material-icons modal-window-header-cancel" 
          (click)="close()">clear</span> 
  </div> 
 
  <div class="modal-window-content"> 
    <div class="leave-type-content" *ngIf="!showDropdown"> 
      <span class="leave-type-title">{{ 
'ContractorsModule.MyStatements.StatementType' | translate  }} </span> 
      <span class="leave-type-block">{{ getTypeTitle() | translate  }}</span> 
    </div> 
 
    <form [formGroup]="form" 
          (ngSubmit)="onSubmitClick()" 
          class="modal-window-content-form"> 
      <div class="all-content"> 
        <div class="action-bar" *ngIf="showDropdown"> 
          <div class="action-bar-controls input-wrapper"> 
            <span class="item-label">{{ 'HrModule.ProcessStatement.StatementType' | 
translate }}</span> 
            <div class="dropdown " [style.width]="getDropdownWidth()"> 
              <app-leave-dropdown id="type" 
                                  fieldName="type" 
                                  [form]="form" 
                                  [defaultChosenValue]="leaveTypes[selectedTypeIndex] | 
localTitle" 
                                  [dropdownList]="leaveTypes" 
                                  (selectItem)="changeLeaveType($event)"> 
              </app-leave-dropdown> 
            </div> 
          </div> 
43 
ЧДТУ.221819.002 ПЗ 
        </div> 
 
        <app-leave-rule-info-block *ngIf="infoRule && infoRule.status" 
                                   [leaveRuleMessages]="getLeaveInfoRules()" 
                                   [leaveRuleStatus]="getStatusByName(infoRule.status)"> 
        </app-leave-rule-info-block> 
 
        <div *ngIf="isAdditionalLeave() && !isStudyLeave()" class="event-date"> 
          <label class="item-label">{{'ContractorsModule.AddLeaves.DateOfEvent' | 
translate }} </label> 
          <app-input 
            [form]="form" 
            [submitted]="submitted" 
            [validators]="fromDateFieldValidators" 
            fieldName="eventDate" 
            [prefix]="this.leaveType.name | leaveDatePrefix: fromDateOption" 
            type="date" 
            [rightLabelPosition]="true" 
            (dateChange)="eventDateChanged()"> 
          </app-input> 
        </div> 
 
        <div class="sick-leave-container" *ngIf="isSickLeaveType()"> 
          <app-input 
            [form]="form" 
            [submitted]="submitted" 
            fieldName="fromDate" 
            type="date" 
            (dateChange)="startDateChanged()" 
            [rightLabelPosition]="true" 
            [label]="'ContractorsModule.AddLeaves.StartDateOfAbsence' | translate"> 
          </app-input> 
          <app-input 
            #toDate 
            *ngIf="isLeaveFromDateInPast() || isSickLeaveEditForm()" 
            [form]="form" 
            [submitted]="submitted" 
            fieldName="toDate" 
            type="date" 
            [minDate]="form.value.fromDate" 
            [rightLabelPosition]="true" 
            [label]="'ContractorsModule.AddLeaves.EndDateOfAbsence' | translate" 
            (dateChange)="finishDateChanged()"> 
          </app-input> 
          <app-input 
44 
ЧДТУ.221819.002 ПЗ 
            #numberOfDisabilityLetter 
            *ngIf="this.form.value.toDate" 
            [form]="form" 
            [submitted]="submitted" 
            fieldName="numberOfDisabilityLetter" 
            type="text" 
            [rightLabelPosition]="true" 
            [label]="'ContractorsModule.Commons.DisabilityLetterNumber' | translate 
" 
            [placeholder]="'ContractorsModule.Commons.EnterDisabilityLetterNumbe
r' | translate" 
            inFocus="true" 
          > 
            <a class="ml-2 link-button-info" 
               [ngbTooltip]="'ContractorsModule.AddLeaves.NoDisabilityLetterToolTi
p' | translate" 
               tooltipClass="info-tooltip" 
            > {{ 'ContractorsModule.AddLeaves.NoDisabilityLetter' | translate }}</a> 
          </app-input> 
        </div> 
 
        <div class="dates" *ngIf="!isSickLeaveType()"> 
          <label *ngIf="!isDayOff" 
                 class="item-
label">{{'ContractorsModule.AddLeaves.PlannedAbsencePeriod' | translate }} 
</label> 
          <div class="dates-container"> 
            <div class="date-view-style mr-3" [ngClass]="{'one-line-date': 
isDayOff}"> 
              <app-input 
                [form]="form" 
                [submitted]="submitted" 
                [validators]="fromDateFieldValidators" 
                fieldName="fromDate" 
                [label]="(!isDayOff ? 'ContractorsModule.AddLeaves.From' : 
'ContractorsModule.AddLeaves.DateOfUnscheduledActivity') | translate" 
                [prefix]="this.leaveType.name | leaveDatePrefix: fromDateOption" 
                type="date" 
                [rightLabelPosition]="true" 
                [minDate]="minDate" 
                [maxDate]="maxDate" 
                (dateChange)="startDateChanged()"> 
              </app-input> 
            </div> 
45 
ЧДТУ.221819.002 ПЗ 
            <div class="date-view-style" [ngClass]="{'one-line-date second-date': 
isDayOff}"> 
              <app-input 
                *ngIf="!isSickLeaveType()" 
                [form]="form" 
                [submitted]="submitted" 
                [validators]="textFieldValidators" 
                fieldName="toDate" 
                [label]="(!isDayOff ? 'ContractorsModule.AddLeaves.To' : 
'ContractorsModule.AddLeaves.ScheduledDateOfAbsence') | translate " 
                [prefix]="this.leaveType.name | leaveDatePrefix: toDateOption" 
                type="date" 
                [rightLabelPosition]="true" 
                [minDate]="minDate" 
                [maxDate]="maxDate" 
                (dateChange)="finishDateChanged()"> 
              </app-input> 
            </div> 
          </div> 
        </div> 
 
        <app-leave-rule-info-block *ngIf="leaveRuleMessages && 
leaveRuleMessages.length" 
                                   [leaveRuleMessages]="leaveRuleMessages" 
                                   [leaveRuleStatus]="getStatusByRules()"> 
        </app-leave-rule-info-block> 
 
        <div class="description" style="margin-bottom: 12px"> 
          <app-input 
            [form]="form" 
            [submitted]="submitted" 
            label="ContractorsModule.AddLeaves.Description" 
            fieldName="description" 
            (change)="changeDescription()" 
            placeholder="ContractorsModule.AddLeaves.EnterDescription" 
            type="textarea"> 
          </app-input> 
        </div> 
 
        <div class="filename" *ngIf="sickLeaveEditable && 
sickLeaveEditable.documentIds"> 
          <div class="file" *ngFor="let id of sickLeaveEditable.documentIds"> 
            <span>{{id}}</span> 
            <span (click)="deleteFileId(id)" class="material-icons icon-
button">close</span> 
46 
ЧДТУ.221819.002 ПЗ 
          </div> 
        </div> 
 
        <div class="filename" *ngIf="files && isTypeNeedDocument()"> 
          <div class="file" *ngFor="let file of files"> 
            <div> 
              <span>{{file.name}}</span> 
              <span (click)="deleteFile(file)" class="material-icons icon-
button">close</span> 
            </div> 
          </div> 
        </div> 
 
        <div *ngIf="this.isTypeNeedDocument()" class="add-document"> 
          <div [ngbTooltip]="getAttachButtonTooltip()" data-container="body"> 
            <app-browse-file-button 
              (file)="attachFile($event)" 
              [disabled]="attachFileButtonDisabled" 
            ></app-browse-file-button> 
          </div> 
        </div> 
 
        <app-leave-rule-info-block *ngIf="documentError || 
showFilesAmountWarning || showNotAllowedFileTypeError" 
                                   [leaveRuleMessages]="[documentError]" 
                                   [leaveRuleStatus]="getStatusByName(documentError.status)
"> 
        </app-leave-rule-info-block> 
      </div> 
 
      <div class="modal-window-content-form-actions"> 
        <app-main-button 
          [submitted]="this.isSubmitted()" 
          [disabled]="isSubmitButtonDisabled()" 
        > 
          {{ this.getSubmitButtonText() | translate }} 
        </app-main-button> 
        <button type="button" class="modal-close" 
                (click)="close()"> {{ 'AppModule.Commons.Close' | translate }} 
</button> 
      </div> 
    </form> 
  </div> 
</div> 
 
47 
ЧДТУ.221819.002 ПЗ 
 
Текст файлу dialog-add-leave.css 
import "src/global"; 
@import "src/dialog"; 
@import "~bootstrap/scss/forms"; 
@import "~bootstrap/scss/custom-forms"; 
@import "~bootstrap/scss/buttons"; 
@import "~bootstrap/scss/dropdown"; 
@import "~src/variables"; 
 
.modal-window-content-form { 
  width: 100%; 
} 
 
.all-content { 
  width: 100%; 
 
  .dates { 
    display: flex; 
    justify-content: flex-start; 
    flex-wrap: wrap; 
    margin-bottom: 0.5rem; 
    align-items: center; 
 
    .dates-container { 
      display: flex; 
      flex-wrap: wrap; 
 
      .one-line-date { 
        width: auto; 
      } 
 
      .second-date { 
        margin-top: 1rem; 
      } 
    } 
  } 
 
  .item-label { 
    font-size: 14px; 
    line-height: 14px; 
    margin-right: 0.5rem; 
    margin-bottom: 0 !important; 
    color: $secondary-color; 
48 
ЧДТУ.221819.002 ПЗ 
  } 
 
  .add-document { 
    margin-top: .5rem; 
    margin-bottom: 1.5rem; 
    display: flex; 
    justify-content: flex-start; 
    align-items: center; 
  } 
 
  .filename { 
    color: $secondary-color; 
    font-size: 14px; 
 
    span { 
      display: flex; 
      flex-wrap: wrap; 
    } 
  } 
 
  @media (max-height: 750px) { 
    .filename { 
      display: flex; 
      flex-wrap: wrap; 
    } 
  } 
 
  .file { 
    display: flex; 
    justify-content: flex-start; 
    align-items: start; 
 
    > div { 
      display: flex; 
    } 
  } 
 
  .file span { 
    &:first-child { 
      white-space: nowrap; 
    } 
 
    padding: .2rem; 
  } 
 
49 
ЧДТУ.221819.002 ПЗ 
  @media (max-width: 512px) { 
    .file span { 
      &:first-child { 
        white-space: normal; 
      } 
    } 
  } 
 
  span.icon-button { 
    color: $cross-button; 
    cursor: pointer; 
    font-size: 14px; 
    margin: 3px 0; 
  } 
 
  span.icon-button:hover { 
    color: red; 
  } 
 
  .dropdown-margin { 
    margin-bottom: 1rem; 
  } 
} 
 
.leave-type-content { 
  margin-bottom: 10px; 
 
  .leave-type-title { 
    font-size: 14px; 
    line-height: 14px; 
    color: $secondary-color; 
  } 
 
  .leave-type-block { 
    min-width: 40%; 
    width: fit-content; 
    padding: .3125rem .5rem; 
    border-radius: .2rem; 
    margin-bottom: 1rem; 
    margin-left: 0.5rem; 
    font-size: 14px; 
    background-color: $main-border-color; 
  } 
} 
 
50 
ЧДТУ.221819.002 ПЗ 
::ng-deep app-browse-file-button { 
  .link-button { 
    padding: 0 !important; 
  } 
} 
 
::ng-deep .date-wrapper { 
  width: 115px; 
} 
 
::ng-deep .right-label-position { 
  margin: 0; 
} 
 
.event-date { 
  display: flex; 
  justify-content: flex-start; 
  align-items: center; 
  flex-wrap: wrap; 
  margin-bottom: 8px; 
} 
 
@media (max-width: 550px) { 
  .all-content > .dates > .dates-container { 
    display: flex; 
    justify-content: space-between; 
 
    > div:nth-child(3) { 
      margin-top: 0.5rem; 
    } 
 
    .date-view-style:nth-child(2) { 
      margin-right: 0; 
    } 
 
    .date-view-style { 
      width: 100%; 
    } 
 
    .date-view-style > app-input { 
      ::ng-deep .date-wrapper { 
        width: 112px; 
      } 
    } 
  } 
51 
ЧДТУ.221819.002 ПЗ 
} 
 
.modal-close { 
  @extend .tiny-button-cancel; 
  color: #6D6E82; 
} 
 
.wrapper { 
  font-size: 14px; 
  display: flex; 
  justify-content: flex-start; 
  width: 100%; 
  margin-bottom: 14px; 
 
  label { 
    width: 42%; 
    line-height: 14px; 
    position: relative; 
    top: 4px; 
  } 
 
  input { 
    @extend .form-control; 
    font-size: 14px; 
    line-height: 24px; 
    height: 24px; 
    box-shadow: none !important; 
    padding: 0 0.5rem; 
    color: #6D6E82; 
    border-color: #E3ECF3; 
 
    &:hover { 
      border-color: #C5C7DA; 
    } 
 
    &:focus { 
      border-color: #5C86F2; 
    } 
  } 
 
  textarea { 
    resize: none; 
  } 
 
  input::placeholder, textarea::placeholder { 
52 
ЧДТУ.221819.002 ПЗ 
    color: $light-gray; 
  } 
 
  input[type=date]:invalid { 
    color: $light-gray; 
  } 
 
  input.is-invalid { 
    border-color: #C3C5D8; 
    background: none; 
 
    &:hover { 
      border-color: #C5C7DA; 
    } 
 
    &:focus { 
      border-color: #5C86F2; 
    } 
  } 
 
  .password-wrapper { 
    position: relative; 
    flex-grow: 1; 
 
    &-input { 
      padding-right: 1.5rem; 
      background-image: none !important; 
    } 
 
    &-eye-icon { 
      position: absolute; 
      right: 1.4rem; 
      top: 0.2rem; 
      width: 0; 
      color: #5C86F2; 
      font-size: 14px; 
      cursor: pointer; 
    } 
  } 
} 
 
@media (max-width: 432px) { 
  .all-content > .dates > .dates-container { 
    flex-wrap: nowrap; 
  } 
53 
ЧДТУ.221819.002 ПЗ 
} 
 
@media (max-width: 486px) { 
  .leave-type-content { 
    display: flex; 
    flex-direction: column; 
    gap: 10px; 
    margin-bottom: 0; 
 
    .leave-type-block { 
      margin: 0 0 10px; 
    } 
  } 
 
  .all-content > .dates > .dates-container { 
    max-height: 75px; 
    flex-direction: column; 
    margin-top: 15px; 
    gap: 5px; 
 
    .second-date { 
      margin-top: 0; 
    } 
 
    ::ng-deep { 
      app-input { 
        label { 
          min-width: 40px; 
        } 
      } 
    } 
 
    .date-view-style.one-line-date { 
      ::ng-deep { 
        app-input { 
          label { 
            min-width: 180px; 
            max-width: 180px; 
          } 
        } 
      } 
    } 
 
    .date-view-style > app-input { 
54 
ЧДТУ.221819.002 ПЗ 
      ::ng-deep .date-wrapper { 
        margin-bottom: 0.5rem; 
      } 
    } 
  } 
 
  .sick-leave-container { 
    ::ng-deep { 
      app-input { 
        label { 
          min-width: 150px !important; 
        } 
      } 
    } 
  } 
} 
@media (max-width: 320px) { 
  .all-content > .dates > .dates-container { 
    .date-view-style.one-line-date { 
      ::ng-deep { 
        app-input { 
          label { 
            min-width: 160px; 
            max-width: 160px; 
          } 
        } 
      } 
    } 
  } 
  app-main-button { 
    ::ng-deep .align-items-center { 
      font-size: 12px; 
    } 
  } 
} 
 
.sick-leave-container { 
 
  ::ng-deep { 
 
    a { 
      font-size: 13px; 
      text-decoration: underline; 
      color: #5c86f2; 
55 
ЧДТУ.221819.002 ПЗ 
    } 
 
    app-input { 
      label { 
        color: #626588; 
        min-width: 220px; 
      } 
 
      & > div { 
        margin-bottom: 10px !important; 
      } 
 
      &[type="date"] ::ng-deep .right-label-position { 
        input { 
          max-width: 115px; 
        } 
      } 
    } 
  } 
} 
 
Текст файлу dialog-add-leave.ts 
import { Component, Inject, OnChanges, OnInit, ViewChild } from '@ react 
/core'; 
import { AbstractControl, FormBuilder, FormGroup } from '@react/forms'; 
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@ react 
/material/dialog'; 
import { TranslateService } from '@ngx-translate/core'; 
import * as moment from 'moment'; 
import { Observable } from 'rxjs'; 
 
import { InputComponent } from 
'../../contractors/components/input/input.component'; 
import { LeaveTypeCategory } from '../../enums/leave-type-category.enum'; 
import { LeaveTypesEnum } from '../../enums/leave-types.enum'; 
import { AvailableLeavesDaysModel } from '../../model/available-leaves-
days.model'; 
import { HolidayType } from '../../model/holiday-type'; 
import { LeaveRuleMessageModel } from '../../model/leave-rule-message.model'; 
import { LeaveRuleStatus } from '../../model/leave-rule-status.model'; 
import { MyLeaveModel } from '../../model/my-leave.model'; 
import { SubordinateLeaveModel } from '../../model/subordinate-leave.model'; 
import { TypeModel } from '../../model/type.model'; 
56 
ЧДТУ.221819.002 ПЗ 
import { FROM_DATE_OPTION, TO_DATE_OPTION } from '../../pipes/leave-
input.pipe'; 
import { ContractorService } from '../../service/contractor.service'; 
import { HolidayService } from '../../service/holiday.service'; 
import { LeavesService } from '../../service/leaves.service'; 
import { MyStatementsService } from '../../service/my-statements.service'; 
import { NotificationService } from '../../service/notification.service'; 
import { UserService } from '../../service/user.service'; 
import { isEmpty } from '../../utils/checks-utils'; 
import { DialogActionVerificationComponent } from '../dialog-action-
verification/dialog-action-verification.component'; 
 
@Component({ 
  selector: 'app-dialog-add-leave', 
  templateUrl: './dialog-add-leave.component.html', 
  styleUrls: ['./dialog-add-leave.component.scss'], 
}) 
export class DialogAddLeaveComponent implements OnInit, OnChanges { 
 
  readonly fromDateOption = FROM_DATE_OPTION; 
  readonly toDateOption = TO_DATE_OPTION; 
 
  showDropdown = false; 
  selectedTypeIndex = 0; 
  title = 'ContractorsModule.AddLeaves.CreateLeave'; 
  description = ''; 
  leaveFormErrors = {fromDate: ''}; 
  holidays: string; 
  contractorId: number; 
  files: File[] = []; 
  form: FormGroup; 
  leaveType: TypeModel; 
  fromDate: AbstractControl; 
  toDate: AbstractControl; 
  leaveTypes: TypeModel[] = []; 
  availableLeavesDays: AvailableLeavesDaysModel; 
  infoRule: LeaveRuleMessageModel; 
  documentError: LeaveRuleMessageModel; 
  leaveRuleMessages: LeaveRuleMessageModel[]; 
  sickLeaveEditable: SubordinateLeaveModel; 
  sickLeaveDocumentsToDelete: string[] = []; 
  currentUserId: number; 
  submitted = false; 
  isSubmitDisabled = false; 
  isDisabledDateInput = false; 
57 
ЧДТУ.221819.002 ПЗ 
  minDate: Date; 
  maxDate: Date; 
  showFilesAmountWarning = false; 
  showNotAllowedFileTypeError = false; 
  filesMaxAmount = 10; 
  numberOfLetter: string; 
  textFieldValidators = [{name: 'required', message: 'IsRequired'}]; 
 
  fromDateFieldValidators = [ 
    ...this.textFieldValidators, 
    {name: 'leaveAlreadyExistsInSelectedPeriod', message: 
'.LeaveAlreadyExistsInSelectedPeriod'}, 
  ]; 
  attachFileButtonDisabled = false; 
  @ViewChild('toDate') toDateRef: InputComponent; 
  @ViewChild('numberOfDisabilityLetter') numberOfDisabilityLetterRef: 
InputComponent; 
 
  static isNullOrInvalidDate(date: any): boolean { 
    return date === null || date.toString() === 'Invalid date'; 
  } 
 
  constructor( 
    public dialogRef: MatDialogRef<DialogAddLeaveComponent>, 
    @Inject(MAT_DIALOG_DATA) public data: any, 
    private readonly formBuilder: FormBuilder, 
    private readonly notificationService: NotificationService, 
    private readonly translate: TranslateService, 
    private readonly leavesService: LeavesService, 
    private readonly myStatementsService: MyStatementsService, 
    private readonly dialog: MatDialog, 
    private readonly userService: UserService, 
    private readonly holidayService: HolidayService, 
    private readonly contractorService: ContractorService, 
  ) { 
  } 
 
  ngOnInit(): void { 
    this.currentUserId = this.userService.selfInformation$.getValue().id; 
    this.parseData(); 
 
    this.form = this.createFormGroup(); 
    if (this.isMarriageEditForm()) { 
      this.form.controls.fromDate.setValue(this.sickLeaveEditable.fromDate); 
      this.form.controls.toDate.setValue(this.sickLeaveEditable.toDate); 
58 
ЧДТУ.221819.002 ПЗ 
      this.form.controls.eventDate.setValue(this.sickLeaveEditable.eventDate); 
      this.form.controls.description.setValue(this.sickLeaveEditable.description); 
      this.description = this.sickLeaveEditable.description; 
      this.loadLeaveRuleMessages(); 
    } 
    if (this.isStudyEditForm()) { 
      this.form.controls.fromDate.setValue(this.sickLeaveEditable.fromDate); 
      this.form.controls.toDate.setValue(this.sickLeaveEditable.toDate); 
      this.form.controls.description.setValue(this.sickLeaveEditable.description); 
      this.description = this.sickLeaveEditable.description; 
      this.loadLeaveRuleMessages(); 
    } 
 
    if (this.isSickLeaveEditForm()) { 
      this.form.controls.fromDate.setValue(this.sickLeaveEditable.fromDate); 
      this.form.controls.toDate.setValue(this.sickLeaveEditable.toDate); 
      this.form.controls.description.setValue(this.sickLeaveEditable.description); 
      this.numberOfLetter = this.sickLeaveEditable.numberOfDisabilityLetter; 
      this.description = this.sickLeaveEditable.description; 
      this.loadLeaveRuleMessages(); 
    } 
 
    this.fromDate = this.form.controls.fromDate; 
    this.toDate = this.form.controls.toDate; 
 
    this.loadLeaveRuleInfoMessage(this.leaveType); 
 
    this.isDisabledDateInput = this.isAdditionalLeave() && !this.isStudyLeave(); 
    this.updateAttachFileButtonDisabled(); 
    this.countDays(); 
  } 
 
  ngOnChanges(): void { 
    const newSelectedLeaveType = this.leaveTypes[this.selectedTypeIndex]; 
    if (!this.leaveType || (this.leaveType.name !== newSelectedLeaveType.name)) { 
      this.changeLeaveType(newSelectedLeaveType); 
    } 
  } 
 
  get isDayOff(): boolean { 
    return this.leaveType.name === LeaveTypesEnum.DAYOFF; 
  } 
 
  changeLeaveType(newSelectedLeaveType: TypeModel): void { 
    this.leaveType = newSelectedLeaveType; 
59 
ЧДТУ.221819.002 ПЗ 
    this.selectedTypeIndex = this.leaveTypes.findIndex(type => type.name === 
newSelectedLeaveType.name); 
    if (this.form) { 
      this.fromDate.setValue(this.isSickLeaveType() && 
!this.isSickLeaveEditForm() ? moment() : ''); 
      this.toDate.setValue(''); 
      this.form.controls.type.setValue(newSelectedLeaveType); 
      this.isDisabledDateInput = this.isAdditionalLeave() && !this.isStudyLeave(); 
      this.form.controls.eventDate.setValue(null); 
      this.leaveRuleMessages = null; 
      this.documentError = null; 
      this.minDate = null; 
      this.maxDate = null; 
      this.submitted = false; 
    } 
    this.loadLeaveRuleInfoMessage(this.leaveType); 
    this.countDays(); 
  } 
 
  parseData(): void { 
    this.contractorId = this.data.contractorId; 
    if (this.data.showDropdown) { 
      this.showDropdown = this.data.showDropdown; 
      this.leaveTypes = this.data.leaveTypes ? this.data.leaveTypes : null; 
      this.leaveType = this.leaveTypes[this.selectedTypeIndex]; 
      this.contractorId = this.data.contractorId; 
    } else { 
      this.leaveType = this.data.leaveType; 
 
      if (this.isSickLeaveType() || this.isMarriageLeaveType() || 
this.isStudyLeaveType()) { 
        if (this.data.sickLeaveEditable) { 
          this.contractorId = this.data.sickLeaveEditable.contractor ? 
            this.data.sickLeaveEditable.contractor.id : 
            this.data.sickLeaveEditable.subordinate.id; 
        } 
        this.sickLeaveEditable = this.data.sickLeaveEditable; 
      } 
    } 
    this.availableLeavesDays = this.data.availableLeavesDays; 
  } 
 
  countDays(): void { 
    if (!!this.fromDate.value && !(new Date(this.fromDate.value).getFullYear() < 
2000)) { 
60 
ЧДТУ.221819.002 ПЗ 
      let startDate = moment(new Date(this.fromDate.value)) 
        .format('YYYY-MM-DD'); 
      if (this.isSickLeaveType() && this.toDate.value === '') { 
        this.updateHolidays(startDate, startDate); 
      } 
      const finishDate = moment(new Date(this.toDate.value)) 
        .format('YYYY-MM-DD'); 
      if (this.fromDate.value <= this.toDate.value) { 
        if (this.isDayOff) { 
          startDate = finishDate; 
        } 
        this.updateHolidays(startDate, finishDate); 
      } 
      this.loadLeaveRuleMessages(); 
    } else { 
      this.leaveRuleMessages = null; 
    } 
  } 
 
  startDateChanged(): void { 
    if ((!this.toDate.value || this.toDate.value <= this.fromDate.value) && 
this.isNotSickLeaveType()) { 
      this.toDate.setValue(this.fromDate.value); 
    } 
 
    if (this.isSickLeaveType() && !this.isSickLeaveEditForm()) { 
      this.checkIsFromDateInFuture(); 
    } 
 
    this.countDays(); 
    this.resetFormErrors(); 
  } 
 
  resetFormErrors(): void { 
    this.leaveFormErrors = {fromDate: ''}; 
  } 
 
  finishDateChanged(): void { 
    if (this.inType('DAYOFF')) { 
      this.countDays(); 
    } else { 
      if (this.toDate.value < this.fromDate.value) { 
        this.toDate.setValue(this.fromDate.value); 
      } 
      if (!this.fromDate.value) { 
61 
ЧДТУ.221819.002 ПЗ 
        this.fromDate.setValue(this.toDate.value); 
      } 
      this.countDays(); 
    } 
    this.resetFormErrors(); 
    this.setFocusIntoDisabilityLetter(); 
  } 
 
  eventDateChanged(): void { 
    const eventDate = this.form.controls.eventDate.value; 
    const eventDateIsInvalid = 
DialogAddLeaveComponent.isNullOrInvalidDate(eventDate); 
    if (!eventDateIsInvalid) { 
      this.holidayService.getEventMinMaxDates(eventDate) 
        .subscribe(d => { 
          this.isDisabledDateInput = false; 
 
          this.minDate = moment(d.minDate).toDate(); 
          this.maxDate = moment(d.maxDate).toDate(); 
        }); 
    } else { 
      this.isDisabledDateInput = true; 
      this.minDate = null; 
      this.maxDate = null; 
    } 
 
    this.fromDate.setValue(null); 
    this.toDate.setValue(null); 
    this.leaveRuleMessages = null; 
  } 
 
  changeDescription(): void { 
    this.description = this.form.controls.description.value; 
  } 
 
  attachFile(files: File[]): void { 
    if (this.files.concat(files).length > this.filesMaxAmount) { 
      this.showFilesAmountWarning = true; 
      this.translate.get('ContractorsModule.AddLeaves.DocumentsLimitReached') 
        .subscribe(message => this.documentError = {status: 
LeaveRuleStatus.ERROR.name, text: message}); 
 
      return; 
    } 
 
62 
ЧДТУ.221819.002 ПЗ 
    this.showFilesAmountWarning = false; 
    this.showNotAllowedFileTypeError = false; 
    this.documentError = null; 
 
    if (files.length > 0) { 
      this.files = this.files.concat(files); 
    } 
 
    this.checkOnRequiredDocument(); 
  } 
 
  isValidLeave(): boolean { 
    if (this.isSickLeaveEditForm()) { 
      return Boolean(this.form.value.toDate && this.leaveRuleMessages && 
        !this.leaveRuleMessages.find(m => LeaveRuleStatus.getByName(m.status) 
=== LeaveRuleStatus.ERROR)); 
    } 
 
    if (this.isMarriageEditForm() || this.isStudyEditForm()) { 
      return Boolean(this.form.value.toDate && this.leaveRuleMessages && 
        !this.leaveRuleMessages.find(m => LeaveRuleStatus.getByName(m.status) 
=== LeaveRuleStatus.ERROR) && 
        (this.files.length || this.sickLeaveEditable.documentIds.length)); 
    } 
    if (this.isSickLeaveType() || this.isMarriageLeaveType()) { 
      return Boolean(this.fromDate.status === 'VALID' && this.toDate.status === 
'VALID' && 
        this.leaveRuleMessages && 
        !this.leaveRuleMessages.find(m => LeaveRuleStatus.getByName(m.status) 
=== LeaveRuleStatus.ERROR)); 
    } 
 
    return Boolean(this.fromDate.status === 'VALID' && this.toDate.status === 
'VALID' && 
      this.leaveRuleMessages && this.form.controls.toDate.value && 
      !this.leaveRuleMessages.find(m => LeaveRuleStatus.getByName(m.status) 
=== LeaveRuleStatus.ERROR)); 
  } 
 
  isTypeNeedDocument = (): boolean => 
this.inCategory(LeaveTypeCategory.ADDITIONAL); 
 
  deleteFile(file: File): void { 
    this.files.splice(this.files.indexOf(file), 1); 
 
63 
ЧДТУ.221819.002 ПЗ 
    this.showNotAllowedFileTypeError = false; 
 
    if (this.files.length < this.filesMaxAmount) { 
      this.showFilesAmountWarning = false; 
    } 
  } 
 
  onSubmitClick(): void { 
    if (!this.isSickLeaveType()) { this.submitted = true; } 
    if (this.isSickLeaveType() && this.isSickLeaveDocumentNumberExist()) { 
      this.submitted = true; 
    } 
 
    if (!this.checkOnRequiredDocument()) { 
      return; 
    } 
 
    if (this.form.valid) { 
      const leave: MyLeaveModel = this.generateLeave(); 
 
      if (this.isTypeNeedDocument()) { 
        leave.documents = this.files.map(file => ({file, type: leave.type})); 
      } 
 
      if (this.isSickLeaveEditForm()) { 
        leave.events = this.sickLeaveEditable.events; 
        leave.id = this.sickLeaveEditable.id; 
        leave.workingDays = this.sickLeaveEditable.workingDays; 
        leave.numberOfDisabilityLetter = this.form.value.numberOfDisabilityLetter; 
 
        this.updateSickLeave(leave); 
 
        return; 
      } 
 
      if (this.isMarriageEditForm() || this.isStudyEditForm()) { 
        leave.events = this.sickLeaveEditable.events; 
        leave.id = this.sickLeaveEditable.id; 
        leave.workingDays = this.sickLeaveEditable.workingDays; 
        leave.documentIds = this.sickLeaveEditable.documentIds; 
 
        this.patchAndReleaseMarriageLeave(leave); 
 
        return; 
      } 
64 
ЧДТУ.221819.002 ПЗ 
      if (this.leaveType.name === 'SICK' && this.isLeaveFromDateInPast()) { 
        this.createSickLeaveOnPastPeriod(leave); 
 
        return; 
      } 
 
      (this.showDropdown) ? this.createContractorLeave(leave) : 
this.createNewLeave(leave); 
    } 
  } 
 
  openConfirmDialog(config, leave: MyLeaveModel): void { 
    this.isSubmitDisabled = true; 
    const dialogRef = this.dialog.open(DialogActionVerificationComponent, 
config); 
    const isVerificationValid = verification => Boolean(verification === 'yes'); 
    const isVerificationCanceled = verification => Boolean(verification === 'no'); 
    const isVerificationInvalid = verification => Boolean(verification === 'close' || 
!verification); 
    const isNextStepsDialogClosed = verification => 
      Boolean(!this.isCompletedLeave() && (isVerificationInvalid(verification) || 
isVerificationValid(verification))); 
 
    dialogRef.afterClosed() 
      .subscribe(verification => { 
        if (isVerificationInvalid(verification)) { 
          this.isSubmitDisabled = false; 
 
          return; 
        } 
 
        if (isNextStepsDialogClosed(verification)) { 
          this.submitted = true; 
          this.createLeave(leave); 
 
          return; 
        } 
 
        if (isVerificationValid(verification)) { 
          this.submitted = true; 
          this.isSickLeaveEditForm() ? this.patchAndRelease(leave) : 
this.createAndReleaseSickLeave(leave); 
 
          return; 
        } 
65 
ЧДТУ.221819.002 ПЗ 
 
        if (isVerificationCanceled(verification)) { 
          this.submitted = true; 
          this.isSickLeaveEditForm() ? this.patchLeave(leave) : 
this.createLeaveByOpenedDialog(leave); 
        } 
 
        if (verification === 'yes' || verification === 'no') { 
          if (this.sickLeaveDocumentsToDelete.length > 0) { 
            this.leavesService.removeDocuments(this.sickLeaveDocumentsToDelete).s
ubscribe(); 
          } 
        } 
      }); 
  } 
 
  isSickLeaveDocumentNumberExist(): boolean { 
    return this.form.value.numberOfDisabilityLetter; 
  } 
 
  updateSickLeave(leave: MyLeaveModel): void { 
 
    if (this.form.value.numberOfDisabilityLetter !== '') { 
      this.patchAndRelease(leave); 
    } else { 
      this.openConfirmDialog(this.getConfirmDialogConfig(), leave); 
    } 
  } 
 
  createLeave(leave: MyLeaveModel): void { 
    this.leavesService.addNew(leave).subscribe((l: MyLeaveModel) => { 
        this.leavesService.setNewLeave(l); 
        this.myStatementsService.setNewLeave(l); 
        this.dialogRef.close('successfullyCreated'); 
        this.dialogRef.afterClosed().subscribe(() => { 
          this.isSubmitDisabled = false; 
        }); 
        this.showNotAllowedFileTypeError = false; 
        this.documentError = null; 
      }, 
      e => { 
        if (e === 'Allowed only images') { 
          this.showNotAllowedFileTypeError = true; 
          this.translate.get('ContractorsModule.AddLeaves.DocumentTypeError') 
66 
ЧДТУ.221819.002 ПЗ 
            .subscribe(message => this.documentError = {status: 
LeaveRuleStatus.ERROR.name, text: message}); 
        } else { 
          this.showServerError(); 
        } 
        this.isSubmitDisabled = false; 
      }, 
    ); 
  } 
 
  createContractorLeave(leave: MyLeaveModel): void { 
    this.submitted = true; 
    this.leavesService.createContractorLeave(leave, this.contractorId) 
      .subscribe(l => { 
          this.leavesService.setNewContractorLeave(l); 
          this.dialogRef.close('successfullyCreated'); 
          this.dialogRef.afterClosed().subscribe(() => { 
            this.isSubmitDisabled = false; 
          }); 
          this.showNotAllowedFileTypeError = false; 
          this.documentError = null; 
        }, 
        e => { 
          if (e === 'Allowed only images') { 
            this.showNotAllowedFileTypeError = true; 
            this.translate.get('ContractorsModule.AddLeaves.DocumentTypeError') 
              .subscribe(message => this.documentError = {status: 
LeaveRuleStatus.ERROR.name, text: message}); 
          } else { 
            this.showServerError(); 
          } 
          this.isSubmitDisabled = false; 
        }); 
  } 
 
  patchLeave(leave: MyLeaveModel): void { 
    if (this.sickLeaveEditable.contractor && this.sickLeaveEditable.contractor.id 
=== this.currentUserId) { 
      this.leavesService.patchSickLeave(leave) 
        .subscribe((l: MyLeaveModel) => this.onSuccessPatchLeave(l), 
          _ => this.showServerError(), 
        ); 
    } else { 
      this.leavesService.patchContractorSickLeave(leave, 
this.sickLeaveEditable.contractor ? 
67 
ЧДТУ.221819.002 ПЗ 
        this.sickLeaveEditable.contractor.id : this.sickLeaveEditable.subordinate.id) 
        .subscribe((l: MyLeaveModel) => { 
            this.onSuccessPatchLeave(l); 
            this.isSubmitDisabled = false; 
          }, 
          _ => this.showServerError(), 
        ); 
    } 
  } 
 
  releaseLeaveRequest(leave: MyLeaveModel): void { 
    this.leavesService.releaseLeave(leave, this.contractorId, 
      this.currentUserId) 
      .subscribe((l: MyLeaveModel) => this.onSuccessPatchLeave(l), 
        _ => this.showServerError(), 
      ); 
  } 
 
  releaseAdditionalLeaveRequest(leave: MyLeaveModel): void { 
    this.leavesService.releaseAdditionalLeave(leave, this.contractorId, 
      this.currentUserId) 
      .subscribe((l: MyLeaveModel) => this.onSuccessPatchLeave(l), 
        _ => this.showServerError(), 
      ); 
  } 
 
  patchAndRelease(leave: MyLeaveModel): void { 
    if (this.contractorId === this.currentUserId || this.contractorId === undefined) { 
      this.leavesService.patchSickLeave(leave) 
        .subscribe((l: MyLeaveModel) => { 
            this.releaseLeaveRequest(l); 
 
            return this.onSuccessPatchLeave(l); 
          }, _ => this.showServerError(), 
        ); 
    } else { 
      this.leavesService.patchContractorSickLeave(leave, this.contractorId) 
        .subscribe((l: MyLeaveModel) => { 
            this.releaseLeaveRequest(leave); 
 
            return this.onSuccessPatchLeave(l); 
          }, 
          _ => this.showServerError(), 
        ); 
    } 
68 
ЧДТУ.221819.002 ПЗ 
  } 
 
  patchAndReleaseMarriageLeave(leave: MyLeaveModel): void { 
    if (this.contractorId === this.currentUserId || this.contractorId === undefined) { 
      this.leavesService.patchMarriageLeave(leave) 
        .subscribe((l: MyLeaveModel) => { 
            this.releaseAdditionalLeaveRequest(l); 
 
            return this.onSuccessPatchLeave(l); 
          }, _ => this.showServerError(), 
        ); 
    } else { 
      this.leavesService.patchContractorMarriageLeave(leave, this.contractorId) 
        .subscribe((l: MyLeaveModel) => { 
            this.releaseAdditionalLeaveRequest(leave); 
 
            return this.onSuccessPatchLeave(l); 
          }, 
          _ => this.showServerError(), 
        ); 
    } 
  } 
 
  onSuccessPatchLeave(leave: MyLeaveModel): void { 
    if (this.contractorId === undefined || this.contractorId === this.currentUserId) { 
      this.leavesService.editSickLeave(leave); 
      this.myStatementsService.editSickLeave(leave); 
    } else { 
      this.leavesService.editContractorSickLeave(leave); 
    } 
 
    this.dialogRef.close('successfullyUpdated'); 
  } 
 
  isSickLeaveEditForm(): boolean { 
    return !!(this.isSickLeaveType() && this.sickLeaveEditable); 
  } 
 
  isSickLeaveType(): boolean { 
    return (this.leaveType.name === 'SICK'); 
  } 
 
  isMarriageLeaveType(): boolean { 
    return (this.leaveType.name === 'MARRIAGE'); 
  } 
69 
ЧДТУ.221819.002 ПЗ 
 
  isMarriageEditForm(): boolean { 
    return !!(this.isMarriageLeaveType() && this.sickLeaveEditable); 
  } 
 
  isStudyLeaveType(): boolean { 
    return (this.leaveType.name === 'STUDY'); 
  } 
 
  isStudyEditForm(): boolean { 
    return !!(this.isStudyLeaveType() && this.sickLeaveEditable); 
  } 
 
  showServerError(): void { 
    this.translate.get('AppModule.CommonErrors.InternalServerError') 
      .subscribe(error => this.notificationService.showError(error)); 
  } 
 
  close(): void { 
    this.dialogRef.close('close'); 
  } 
 
  getTypeTitle(): string { 
    switch (this.leaveType.name) { 
      case LeaveTypesEnum.BIRTH: 
        return 'ContractorsModule.AddLeaves.TitleBirth'; 
      case LeaveTypesEnum.DEATH: 
        return 'ContractorsModule.AddLeaves.TitleDeath'; 
      case LeaveTypesEnum.MARRIAGE: 
        return 'ContractorsModule.AddLeaves.TitleMarriage'; 
      case LeaveTypesEnum.STUDY: 
        return 'ContractorsModule.AddLeaves.TitleStudy'; 
      case LeaveTypesEnum.DAYOFF: 
        return 'ContractorsModule.AddLeaves.TitleDayOff'; 
      case LeaveTypesEnum.SICK: 
        return 'ContractorsModule.AddLeaves.TitleSick'; 
      case LeaveTypesEnum.UNPAID: 
        return 'ContractorsModule.AddLeaves.TitleUnpaid'; 
      case LeaveTypesEnum.VACATION: 
        return 'ContractorsModule.AddLeaves.TitleVacation'; 
      case LeaveTypesEnum.RELOCATION: 
        return 'ContractorsModule.AddLeaves.TitleRelocation'; 
      default: 
        return ''; 
    } 
70 
ЧДТУ.221819.002 ПЗ 
  } 
 
  getStatusByName(name: string): LeaveRuleStatus { 
    return LeaveRuleStatus.getByName(name); 
  } 
 
  getStatusByRules(): LeaveRuleStatus { 
    return this.getStatusByName(this.leaveRuleMessages.find(m => m.status !== 
undefined).status); 
  } 
 
  isSubmitButtonDisabled(): boolean { 
    return !this.isValidLeave() || this.isSubmitDisabled || 
!this.checkOnRequiredDocument(); 
  } 
 
  isAdditionalLeave(): boolean { 
    return this.leaveType.category && this.leaveType.category === 
'ADDITIONAL'; 
  } 
 
  isStudyLeave(): boolean { 
    return this.leaveType.name === LeaveTypesEnum.STUDY; 
  } 
 
  getAttachButtonTooltip(): string { 
    return this.attachFileButtonDisabled ? 
this.translate.instant('ContractorsModule.AddLeaves.CanNotAttachFileForInternL
eave') : ''; 
  } 
 
  isLeaveFromDateInPast(): boolean { 
    return moment().isAfter(moment(this.form.value.fromDate).format('YYYY-
MM-DD'), 'date'); 
  } 
 
  getInfoAboutFindDisabilityLetter(): string { 
    return 
this.translate.instant('ContractorsModule.AddLeaves.HowFindDisabilityLetter', 
      {aboutDisabilityLetter: this.infoRule.text, howToFindDisabilityLetterLink: ''}); 
  } 
 
  getConfirmDialogConfig(): object { 
    return { 
      width: '410px', 
71 
ЧДТУ.221819.002 ПЗ 
      data: { 
        title: 
this.translate.instant('ContractorsModule.AddLeaves.CreateLeavePopupTitle'), 
        displayReason: 
this.translate.instant('ContractorsModule.AddLeaves.CreateLeavePopupDescriptio
nWithoutNumberLetterAndDays'), 
        yesButtonTitle: 
this.translate.instant('ContractorsModule.AddLeaves.SaveLeave'), 
        noButtonTitle: this.translate.instant('ContractorsModule.AddLeaves.Save'), 
        yesButtonHint: 
this.translate.instant('ContractorsModule.AddLeaves.ReleaseSickLeaveSentButton
Hint'), 
        noButtonHint: 
this.translate.instant('ContractorsModule.AddLeaves.ReleaseSickLeaveSaveButton
Hint'), 
      }, 
    }; 
  } 
 
  checkIsFromDateInFuture(): void { 
    if (!this.isLeaveFromDateInPast()) { 
      this.form.get('toDate').reset(); 
    } 
  } 
 
  deleteFileId(id): void { 
    this.sickLeaveDocumentsToDelete.push(id); 
    const {documentIds} = this.sickLeaveEditable; 
    const idIndex = documentIds.findIndex(d => d === id); 
    this.sickLeaveEditable.documentIds = [...documentIds.splice(0, idIndex), 
...documentIds.splice(idIndex + 1)]; 
  } 
 
  getLeaveInfoRules(): LeaveRuleMessageModel[] { 
    return [this.isSickLeaveType() ? {...this.infoRule, text: 
this.getInfoAboutFindDisabilityLetter()} : this.infoRule]; 
  } 
 
  getDropdownWidth(): string { 
    const largestType = this.leaveTypes.reduce((a, b) => 
      this.getTypeTitleByCurrentLang(b).length > 
this.getTypeTitleByCurrentLang(a).length ? b : a); 
 
    return `${this.getTypeTitleByCurrentLang(largestType).length * 10}px`; 
  } 
72 
ЧДТУ.221819.002 ПЗ 
 
  getTypeTitleByCurrentLang(type: TypeModel): string { 
    return this.translate.currentLang === 'uk' ? type.titleUk : type.titleEn; 
  } 
 
  setFocusIntoDisabilityLetter(): void { 
    if (this.numberOfDisabilityLetterRef) { 
      this.numberOfDisabilityLetterRef.setFocus(true); 
    } 
  } 
 
  createLeaveByOpenedDialog(leave: MyLeaveModel): void { 
    this.showDropdown ? this.createContractorLeave(leave) : 
this.createLeave(leave); 
  } 
 
  openDatePickerAndFocus(inputRef: InputComponent): void { 
    if (inputRef) { 
      inputRef.openDatePicker(); 
    } 
  } 
 
  getSubmitButtonText(): string { 
    return this.sickLeaveEditable ? 'ContractorsModule.AddLeaves.SaveLeave' : 
'ContractorsModule.AddLeaves.ReportAbsence'; 
  } 
 
  isSubmitted(): boolean { 
    return this.submitted && this.isValidLeave(); 
  } 
 
  checkOnRequiredDocument(): boolean { 
    const invalid = ((this.inCategory(LeaveTypeCategory.ADDITIONAL) && 
!this.isMarriageLeaveType()) && !this.isStudyLeaveType() && 
        this.files.length === 0) || (this.isMarriageEditForm() && this.files.length === 
0 && 
      (this.sickLeaveEditable !== undefined && 
this.sickLeaveEditable.documentIds.length === 0)) || 
      (this.isStudyEditForm() && this.files.length === 0 && 
      (this.sickLeaveEditable !== undefined && 
this.sickLeaveEditable.documentIds.length === 0)); 
    this.translate.get('ContractorsModule.AddLeaves.RequiredDocument') 
      .subscribe(message => this.documentError = invalid ? 
        {status: LeaveRuleStatus.ERROR.name, text: message} : null); 
 
73 
ЧДТУ.221819.002 ПЗ 
    return !invalid; 
  } 
 
  private loadLeaveRuleInfoMessage(leaveType: TypeModel): void { 
    this.infoRule = null; 
    this.leaveRuleMessages = null; 
    this.leavesService.getLeaveRuleInfoMessage(leaveType) 
      .subscribe(info => { 
        this.infoRule = info.text ? info : undefined; 
        this.openDatePickerForToDateInEdit(); 
      }); 
  } 
 
  private getRule(): Observable<LeaveRuleMessageModel[]> { 
    const leave: MyLeaveModel = this.generateLeave(); 
 
    if (this.isSickLeaveEditForm()) { 
      if (this.sickLeaveEditable.contractor && this.sickLeaveEditable.contractor.id 
=== this.currentUserId) { 
        return this.leavesService.getLeaveRuleMessages(leave); 
      } 
 
      return this.leavesService.getContractorLeaveRuleMessages(leave, 
        this.sickLeaveEditable.contractor ? 
          this.sickLeaveEditable.contractor.id : 
          this.sickLeaveEditable.subordinate.id); 
    } else if (this.data.contractorId && this.data.contractorId !== this.currentUserId) 
{ 
 
      return this.leavesService.getContractorLeaveRuleMessages(leave, 
this.data.contractorId); 
    } 
 
    return this.leavesService.getLeaveRuleMessages(leave); 
  } 
 
  private loadLeaveRuleMessages(): void { 
    this.getRule().subscribe(messages => { 
      this.leaveRuleMessages = messages; 
      if (this.holidays && !this.hasErrorStatuses(messages)) { 
        this.translate.get('ContractorsModule.AddLeaves.HolidaysList', {holidays: 
this.holidays}) 
          .subscribe(message => { 
            if (this.leaveType.name === 'SICK') { 
              this.leaveRuleMessages.unshift({status: 'WARNING', text: message}); 
74 
ЧДТУ.221819.002 ПЗ 
            } else { 
              this.leaveRuleMessages.unshift({status: undefined, text: message}); 
            } 
          }); 
      } 
      if (this.isSickLeaveType() && this.isToDateFieldEmpty() && 
this.isValidLeave()) { 
        setTimeout(() => this.openDatePickerAndFocus(this.toDateRef)); 
      } 
    }); 
  } 
 
  private createFormGroup(): FormGroup { 
    return this.formBuilder.group({ 
      fromDate: [ 
        this.isSickLeaveType() && !this.isSickLeaveEditForm() ? moment() : '', 
      ], 
      toDate: [ 
        '', 
      ], 
      type: [ 
        this.leaveType, 
      ], 
      description: [ 
        '', 
      ], 
      eventDate: [ 
        '', 
      ], 
      numberOfDisabilityLetter: [ 
        '', 
      ], 
    }); 
  } 
 
  private generateLeave(): MyLeaveModel { 
    const leave: MyLeaveModel = {...this.form.value}; 
    leave.description = this.description.trim(); 
    leave.type = this.leaveType; 
    if (this.isSickLeaveEditForm() || this.isMarriageEditForm() || 
this.isStudyEditForm()) { 
      leave.id = this.sickLeaveEditable.id; 
    } 
 
    if (!this.isAdditionalLeave()) { 
75 
ЧДТУ.221819.002 ПЗ 
      leave.eventDate = ''; 
    } 
 
    if (this.isDayOff) { 
      leave.extraDate = leave.fromDate; 
      leave.fromDate = leave.toDate; 
    } 
 
    return leave; 
  } 
 
  private readonly inType = (leaveType: string): boolean => 
    this.leaveType.name === leaveType; 
 
  private readonly inCategory = (leaveTypeCategory: string): boolean => 
    this.leaveType.category === leaveTypeCategory; 
 
  private readonly hasErrorStatuses = (messages: LeaveRuleMessageModel[]): 
boolean => 
    !!messages.find(m => m.status === LeaveRuleStatus.ERROR.name); 
 
  private updateHolidays(startDate, finishDate): void { 
    this.leavesService.getWorkingDaysReport(startDate, finishDate) 
      .subscribe(data => { 
        data.numberOfWorkingDays === 0 ? this.isSubmitDisabled = true : 
this.isSubmitDisabled = false; 
        this.holidays = (!isEmpty(data.holidays)) ? data.holidays 
            .filter(holiday => holiday.type === HolidayType.HOLIDAY) 
            .map(holiday => `<strong>${holiday.title}</strong>`) 
            .join(', ') 
          : ''; 
      }); 
  } 
 
  private updateAttachFileButtonDisabled(): void { 
    if (this.contractorId !== this.currentUserId) { 
      this.contractorService.getOne(this.contractorId) 
        .subscribe(contractor => { 
          this.attachFileButtonDisabled = contractor.intern; 
        }); 
    } else { 
      this.attachFileButtonDisabled = 
this.userService.selfInformation$.getValue().intern; 
    } 
  } 
76 
ЧДТУ.221819.002 ПЗ 
 
  private createSickLeaveOnPastPeriod(leave: MyLeaveModel): void { 
    if (leave.numberOfDisabilityLetter === '') { 
      this.openConfirmDialog(this.getConfirmDialogConfig(), leave); 
    } else { 
      this.createAndReleaseSickLeave(leave); 
    } 
  } 
 
  private createAndReleaseSickLeave(leave: MyLeaveModel): void { 
    if (!this.showDropdown) { 
      this.leavesService.addNew(leave).subscribe((l: MyLeaveModel) => { 
        this.leavesService.setNewLeave(l); 
        this.myStatementsService.setNewLeave(l); 
        this.dialogRef.close('successfullyCreated'); 
        this.dialogRef.afterClosed().subscribe(() => { 
          this.isSubmitDisabled = false; 
        }); 
        this.showNotAllowedFileTypeError = false; 
        this.documentError = null; 
        this.patchPastSickLeave(leave, l); 
      }); 
    } else { 
      this.leavesService.createContractorLeave(leave, this.contractorId) 
        .subscribe(l => { 
            this.leavesService.setNewContractorLeave(l); 
            this.dialogRef.close('successfullyCreated'); 
            this.dialogRef.afterClosed().subscribe(() => { 
              this.isSubmitDisabled = false; 
            }); 
            this.showNotAllowedFileTypeError = false; 
            this.documentError = null; 
            this.patchPastSickLeave(leave, l); 
          }, 
          e => { 
            if (e === 'Allowed only images') { 
              this.showNotAllowedFileTypeError = true; 
              this.translate.get('ContractorsModule.AddLeaves.DocumentTypeError') 
                .subscribe(message => this.documentError = {status: 
LeaveRuleStatus.ERROR.name, text: message}); 
            } else { 
              this.showServerError(); 
            } 
            this.isSubmitDisabled = false; 
          }); 
77 
ЧДТУ.221819.002 ПЗ 
    } 
  } 
 
  private patchPastSickLeave(leave: MyLeaveModel, l: MyLeaveModel): void { 
    leave.events = l.events; 
    leave.id = l.id; 
    leave.workingDays = l.workingDays; 
    leave.numberOfDisabilityLetter = l.numberOfDisabilityLetter; 
    this.leavesService.patchSickLeave(leave) 
      .subscribe((le: MyLeaveModel) => { 
          this.releaseLeaveRequest(le); 
        }, _ => this.showServerError(), 
      ); 
  } 
 
  private openNextStepsInfoDialog(leave: MyLeaveModel): void { 
    this.openConfirmDialog(this.getNextStepsDialogConfig(), leave); 
  } 
 
  private getNextStepsDialogConfig(): object { 
    return { 
      width: '450px', 
      data: { 
        title: 
this.translate.instant('ContractorsModule.AddLeaves.ConfirmInfoDialogTitle'), 
        displayReason: 
this.translate.instant('ContractorsModule.AddLeaves.ConfirmInfoDialogReason'), 
        yesButtonTitle: 
this.translate.instant('ContractorsModule.AddLeaves.ConfirmInfoDialogButton'), 
        infoDialog: true, 
      }, 
    }; 
  } 
 
  private createNewLeave(leave: MyLeaveModel): void { 
    if (this.isSickLeaveType()) { 
      this.openNextStepsInfoDialog(leave); 
    } else { 
      this.createLeave(leave); 
    } 
 
  } 
 
  private openDatePickerForToDateInEdit(): void { 
    if (this.isSickLeaveEditForm() && !this.form.value.toDate) { 
78 
ЧДТУ.221819.002 ПЗ 
      this.openDatePickerAndFocus(this.toDateRef); 
    } 
  } 
 
  private isCompletedLeave(): boolean { 
    return Boolean(this.form.value.fromDate && this.form.value.toDate); 
  } 
 
  private isToDateFieldEmpty(): boolean { 
    return Boolean(!this.form.value.toDate); 
  } 
 
  private isNotSickLeaveType(): boolean { 
    return !this.isSickLeaveType(); 
  } 
} 
 
Текст файлу сontractors-page.html 
<div class="page-wrapper" 
    *ngxPermissionsOnly="['READ_CONTRACTOR', 
'WRITE_CONTRACTOR']"> 
 <main> 
   <div class="bar"> 
     <app-action-bar 
       [disabled]="!contractors" 
       (changeFilter)="changeFilter()" 
       (changeSettings)="changeSettings()" 
       (exportExcel)="exportExcel()" 
       (exportCsv)="exportCsv()"> 
     </app-action-bar> 
   </div> 
   <app-filter *ngIf="isOpenFilter" 
               [config]="filterConfig" 
               [tags]="filters$.getValue()" 
               [filterModelType]="filterModelType" 
79 
ЧДТУ.221819.002 ПЗ 
               (add)="addFilter($event)" 
               (delete)="deleteFilter($event)" 
               (save)="saveFilters($event)" 
               (clear)="clearFilters()" 
               [selectedFilters$]="selectedFilters$" 
               [currentFilters]="filters$.getValue()" 
               @filterInOut> 
   </app-filter> 
 
   <app-loader *ngIf="!contractors; else content"></app-loader> 
   <ng-template #content> 
     <div class="table-wrapper"> 
       <app-table-view 
         [contractors]="contractors" 
         [config]="config" 
         [initConfig]="initConfig" 
         [paginationConfig]="getPaginationConfig()"> 
       </app-table-view> 
     </div> 
   </ng-template> 
 </main> 
</div> 
 
Текст файлу сontractors-page.css 
@import "~src/global"; 
@import "~bootstrap/scss/utilities/spacing"; 
 
app-filter { 
  display: block; 
  z-index: 1000; 
} 
 
.contractors-cards { 
80 
ЧДТУ.221819.002 ПЗ 
  @extend .mt-2; 
  width: 100%; 
  display: flex; 
  flex-wrap: wrap; 
  justify-content: space-between; 
  align-items: stretch; 
} 
 
.container { 
  margin-right: auto; 
  margin-left: 0; 
} 
 
.bar { 
  margin-bottom: 15px; 
} 
 
section { 
  @extend .container; 
} 
 
app-contractors-card { 
  width: 100%; 
  margin: 1rem 0; 
  height: auto; 
} 
 
@media screen and (min-width: 769px) { 
  app-contractors-card { 
    width: 49%; 
    margin: 1% 0; 
  } 
} 
 
app-filters, app-server-filters { 
  display: block; 
  margin-top: 1rem; 
} 
 
.contractors-table { 
  @include card; 
  padding-bottom: 2rem; 
  margin-top: 1rem; 
  position: relative; 
} 
81 
ЧДТУ.221819.002 ПЗ 
 
.table-wrapper { 
  margin-top: 1rem; 
} 
 
@include media-breakpoint-down(lg) { 
  .page-wrapper { 
    flex-direction: column-reverse; 
  } 
 
  aside { 
    width: 100% !important; 
    margin-bottom: 1rem; 
  } 
} 
 
@include media-breakpoint-down(md) { 
  .container { 
    padding: 0.5rem; 
    margin: 0; 
    max-width: 100%; 
  } 
 
  .main-content-container { 
    flex-direction: column-reverse; 
  } 
} 
app-table-view{ 
  z-index: -1000; 
} 
 
@include media-breakpoint-down(sm) { 
  app-table-view { 
    display: block; 
    width: 100%; 
  } 
 
  .contractors-table { 
    margin: 1rem -0.5rem 0.5rem -0.5rem; 
  } 
 
  .contractors-table { 
    border-radius: 0; 
    border: none; 
    box-shadow: none; 
82 
ЧДТУ.221819.002 ПЗ 
  } 
} 
 
.main-content-container { 
  display: flex; 
} 
 
.app-department-celebrants { 
  width: 100% !important; 
  height: 100px; 
} 
 
app-switcher { 
  width: auto; 
} 
 
main { 
  margin-right: 1rem; 
} 
 
Текст файлу сontractors-page.ts 
import { animate, query, state, style, transition, trigger, useAnimation } from 
'@react/animations'; 
import { Component, OnInit } from '@ react/core'; 
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@ 
react/forms'; 
import { MatDialog } from '@ react/material/dialog'; 
import { TranslateService } from '@ngx-translate/core'; 
import * as fileSaver from 'file-saver'; 
import { NgxPermissionsService } from 'ngx-permissions'; 
import { BehaviorSubject, from, Observable } from 'rxjs'; 
 
import { environment } from '../../../../environments/environment'; 
import { cardClose, cardOpen } from '../../../animations'; 
import { col, field, table } from '../../../components/list/responsive-list.util'; 
import { ContractorsTypesEnum } from '../../../enums/contractors-types.enum'; 
import { FilterModelTypeEnum } from '../../../enums/filter-model-type.enum'; 
import { FilterType } from '../../../enums/filter-type.enum'; 
import { AssignmentDashboardModel } from 
'../../../hr/projects/common/models/assignment-dashboard.model'; 
import { AssignmentService } from 
'../../../hr/projects/common/services/assignment.service'; 
import { ContractorModel } from '../../../model/contractor.model'; 
import { DictionaryModel } from '../../../model/dictionary.model'; 
83 
ЧДТУ.221819.002 ПЗ 
import { FilterConfig } from '../../../model/filter-config'; 
import { FilterModel } from '../../../model/filter.model'; 
import { OptionModel } from '../../../model/option.model'; 
import { Column, PaginationConfig, TableConfig } from '../../../model/responsive-
list-config.model'; 
import { ShortLocaleDatePipe } from '../../../pipes/date-format.pipe'; 
import { LocalContractorPipe } from '../../../pipes/local-contractor-name.pipe'; 
import { ContractorFilterService } from '../../../service/contractor-filter.service'; 
import { ContractorService } from '../../../service/contractor.service'; 
import { DepartmentCelebrantsService } from '../../../service/department-
celebrants.service'; 
import { NotificationService } from '../../../service/notification.service'; 
import { PaginationService } from '../../../service/pagination.service'; 
import { TitleService } from '../../../service/title.service'; 
import { SummaryService } from '../../service/summary.service'; 
import { ConfigurationColumnComponent } from '../configuration-
column/configuration-column.component'; 
import { ContractorHrefComponent } from '../contractor-href/contractor-
href.component'; 
import { ContractsFieldComponent } from '../contracts-field/contracts-
field.component'; 
import { ProjectService} from 
"../../../hr/projects/common/services/project.service"; 
 
@Component({ 
  selector: 'app-contractors', 
  templateUrl: './contractors.component.html', 
  styleUrls: ['./contractors.component.scss'], 
  animations: [ 
    trigger('filterInOut', [ 
      state('void', style({ 
        height: 0, 
        'margin-top': 0, 
        opacity: 0, 
        paddingLeft: 0, 
        paddingRight: 0, 
        paddingTop: 0, 
        paddingBottom: 0, 
      })), 
      transition('void <=> *', animate(300)), 
    ]), 
    trigger('filterInOut', [ 
      state('void', style({ 
        height: 0, 
        'margin-top': 0, 
84 
ЧДТУ.221819.002 ПЗ 
        opacity: 0, 
        paddingLeft: 0, 
        paddingRight: 0, 
        paddingTop: 0, 
        paddingBottom: 0, 
      })), 
      transition('void <=> *', animate(300)), 
    ]), 
    trigger('fadeInOut', [ 
      state('void', style({ 
        opacity: 0, 
      })), 
      state('*', style({ 
        opacity: 1, 
      })), 
      transition('void <=> *', animate('3s')), 
    ]), 
  ], 
}) 
export class ContractorsComponent implements OnInit { 
  contractors: ContractorModel[]; 
  isOpenFilter = false; 
  isOpenSettings = false; 
  filterConfig: FilterConfig[]; 
  fullContractors: ContractorModel[]; 
  months: OptionModel[]; 
  filterModelType = FilterModelTypeEnum.CONTRACTOR; 
  filters$ = new BehaviorSubject<FilterModel[]>([]); 
  departmentsDic: DictionaryModel[] = []; 
  positions: DictionaryModel[] = []; 
  positionsIntern: DictionaryModel[] = []; 
  positionsJun: DictionaryModel[] = []; 
  positionsDev: DictionaryModel[] = []; 
  positionsOther: DictionaryModel[] = []; 
  positionCeo: DictionaryModel[] = []; 
  checkedProbation = false; 
  checkedInterns = false; 
  positionsMap = new Map<string, FilterModel[]>(); 
  officesMap = new Map<string, FilterModel[]>(); 
  internshipMap = new Map<string, FilterModel[]>(); 
  departmentsMap = new Map<string, FilterModel[]>(); 
  managersMap = new Map<string, FilterModel[]>(); 
  projectsMap = new Map<string, FilterModel[]>(); 
  form: FormGroup; 
  formContract: FormGroup; 
85 
ЧДТУ.221819.002 ПЗ 
  config: TableConfig; 
  columns: any[]; 
  selectedFilters$: Observable<FilterModel[]>; 
  array = []; 
  initConfig: any; 
  positionsManagement: DictionaryModel[] = []; 
  assignments: AssignmentDashboardModel[] = []; 
  departmentIds: number[] = [1, 2, 3, 4, 5, 6, 7]; 
  private monthCelebrants; 
  private contractorsType: ContractorsTypesEnum; 
  private fromDate: AbstractControl; 
  private toDate: AbstractControl; 
  private managers: ContractorModel[]; 
  private columnsName: Column[] = []; 
  private locale: string; 
  private contractor: string[]; 
 
  constructor( 
    private readonly projectsService: ProjectService, 
    private readonly summaryService: SummaryService, 
    private readonly contractorService: ContractorService, 
    private readonly assignmentService: AssignmentService, 
    private readonly celebrantsService: DepartmentCelebrantsService, 
    private readonly titleService: TitleService, 
    private readonly permissionService: NgxPermissionsService, 
    private readonly paginationService: PaginationService, 
    private readonly contractorFilterService: ContractorFilterService, 
    private readonly formBuilder: FormBuilder, 
    readonly translate: TranslateService, 
    readonly notificationService: NotificationService, 
    private readonly dialog: MatDialog, 
    private readonly shortLocaleDatePipe: ShortLocaleDatePipe, 
    private readonly localContractorPipe: LocalContractorPipe, 
  ) { 
    this.selectedFilters$ = this.contractorFilterService.filters$; 
  } 
 
  ngOnInit(): void { 
    this.locale = this.translate.currentLang; 
    from(this.permissionService.hasPermission('READ_PROJECTS')) 
      .subscribe(hasAuthority => { 
        if (hasAuthority) { 
          this.assignmentService.getDashboard(this.departmentIds).subscribe(assignm
ent => this.assignments = assignment); 
        } 
86 
ЧДТУ.221819.002 ПЗ 
      }); 
    this.contractorService.getManagers().subscribe(managers => this.managers = 
managers); 
    this.form = this.createFormGroup(); 
 
    this.contractorFilterService.filters$ 
      .subscribe(filters => { 
        this.filters$.next(filters); 
      }); 
    this.paginationService.pageSize$.next(20); 
 
    this.celebrantsService.getMonthCelebrants() 
      .subscribe(data => { 
        this.monthsCelebrant(data); 
        this.monthCelebrants = data; 
      }); 
 
    from(this.permissionService.hasPermission('READ_DICTIONARY')) 
      .subscribe(hasAuthority => { 
        if (hasAuthority) { 
          this.managersMap.set('LineManager', []); 
          this.positionsMap.set('Position', []); 
          this.officesMap.set('Office', []); 
          this.internshipMap.set('Internship', []); 
          this.departmentsMap.set('Department', []); 
 
          this.contractorService.getInternships() 
            .subscribe(data => { 
 
              const internships: FilterModel[] = data.map(i => 
                ({ 
                  id: i.id, 
                  type: 'Internship', 
                  name: i.season + ' (' + i.typeSeason + ')', 
                })); 
              this.internshipMap.set('Internship', internships); 
            }); 
          this.contractorService.getManagers() 
            .subscribe(data => { 
              const contractors: FilterModel[] = data.map(c => 
                ({ 
                  id: c.id, 
                  type: 'LineManager', 
                  name: LocalContractorPipe.convertName(c), 
                })); 
87 
ЧДТУ.221819.002 ПЗ 
              this.managersMap.set('LineManager', contractors); 
            }); 
          this.projectsService.getAllActiveProjects() 
            .subscribe(data => { 
 
              const projects: FilterModel[] = data.map(p => 
                ({ 
                  id: p.id, 
                  type: 'Projects', 
                  name: p.name, 
                })); 
              this.projectsMap.set('Projects', projects); 
            }); 
          this.contractorService.getOffices() 
            .subscribe(data => { 
              const offices: FilterModel[] = data.map(o => 
                ({ 
                  id: o.id, 
                  type: 'Office', 
                  name: localStorage.getItem('currentLanguageKey') === 'uk' ? 
o.nameUk : o.nameEn, 
                })); 
              this.officesMap.set('Office', offices); 
            }); 
          this.contractorService.getFilterableDictionary() 
            .subscribe(elem => { 
              elem.forEach(value => { 
                if (this.departmentsMap.has(value.type)) { 
                  this.departmentsMap.get(value.type) 
                    .push({ 
                      id: value.id, 
                      type: value.type, 
                      name: value.value, 
                    }); 
                } else { 
                  this.positionsMap.get(value.type).push({ 
                    id: value.id, 
                    type: value.type, 
                    name: value.value, 
                  }); 
                } 
              }); 
              this.sortedPositions(this.positionsMap); 
              this.filterDictionaryByTypes(this.departmentsMap); 
            }); 
88 
ЧДТУ.221819.002 ПЗ 
        } 
      }); 
    this.titleService.setTitleKey('AppModule.Commons.Contractors'); 
    this.fromDate = this.form.controls.fromDate; 
    this.toDate = this.form.controls.toDate; 
    const localStorageFilters = JSON.parse(localStorage.getItem('filter')); 
    if (localStorageFilters !== null) { 
      localStorageFilters.forEach(f => { 
        if (f.type === 'probationFilter') { 
          this.checkedProbation = f.name === 'true'; 
        } 
        if (f.type === 'internFilter') { 
          this.checkedInterns = f.name === 'true'; 
        } 
 
        if (f.type === 'fromDateInternship' && f.name !== 'null') { 
          this.form.controls.fromDateInternship.setValue(f.name); 
        } 
        if (f.type === 'toDateInternship' && f.name !== 'null') { 
          this.form.controls.toDateInternship.setValue(f.name); 
        } 
        if (f.type === 'fromDateContract' && f.name !== 'null') { 
          this.form.controls.fromDateContract.setValue(f.name); 
        } 
        if (f.type === 'toDateContract' && f.name !== 'null') { 
          this.form.controls.toDateContract.setValue(f.name); 
        } 
        if (f.type === 'contractorsType') { 
          this.getContractorsType(f.name); 
        } 
      }); 
    } else { 
      this.getContractorsType('active'); 
    } 
 
    this.buildColumnsName(); 
    const columnsFromLocalStorage = 
JSON.parse(localStorage.getItem('columnsContractor')); 
    if (columnsFromLocalStorage) { 
      const columnsName = []; 
      this.columns.forEach(c => { 
        for (const column of columnsFromLocalStorage) { 
          if (c.id === column.id) { 
            c.checked = true; 
            columnsName.push(c); 
89 
ЧДТУ.221819.002 ПЗ 
          } 
        } 
      }); 
      columnsName.forEach(c => this.columnsName.push(c.value)); 
      this.initConfig = table(this.columnsName); 
    } 
    this.buildFilterConfig(); 
    this.loadContractors(); 
    this.resetPage(); 
  } 
 
  sortedPositions(dictionary): void { 
    const position = dictionary.get('Position'); 
    position.forEach(d => { 
      if (d.name === 'Dev Intern' || d.name === 'QA Intern' || d.name === 'PM 
Intern') { 
        return this.positionsIntern.push(d); 
      } 
      if (d.name === 'Junior Software Engineer' || 
        d.name === 'Strong Junior Software Engineer' || 
        d.name === 'Strong Software Engineer' || 
        d.name === 'Software Engineer' || 
        d.name === 'Senior Software Engineer') { 
        return this.positionsJun.push(d); 
      } 
      if ( 
        d.name === 'Junior QA Engineer' || 
        d.name === 'Strong Junior QA Engineer' || 
        d.name === 'QA Engineer' || 
        d.name === 'Senior QA Engineer' || 
        d.name === 'Project Manager' 
      ) { 
        return this.positionsDev.push(d); 
      } 
      if ( 
        d.name === 'CEO' || 
        d.name === 'CFO') { 
        return this.positionCeo.push(d); 
      } 
      if (d.name === 'Office Management') { 
        return this.positionsManagement.push(d); 
      } else { 
        return this.positionsOther.push(d); 
      } 
    }); 
90 
ЧДТУ.221819.002 ПЗ 
    this.positionsIntern.forEach(p => this.positions.push(p)); 
    this.positionsJun.forEach(p => this.positions.push(p)); 
    this.positionsDev.forEach(p => this.positions.push(p)); 
    this.positionsManagement.forEach(p => this.positions.push(p)); 
    this.positionsOther.forEach(p => this.positions.push(p)); 
    this.positionCeo.forEach(p => this.positions.push(p)); 
    dictionary.set('Position', this.positions); 
  } 
 
  filterDictionaryByTypes(dictionary): void { 
    const departments = dictionary.get('Department'); 
    departments.forEach(item => { 
      if (item.name === 'Internship') { 
        return this.getSpliceForDepartment(0, item); 
      } 
      if (item.name === 'TSD') { 
        return this.getSpliceForDepartment(2, item); 
      } 
      if (item.name === 'Management') { 
        return this.getSpliceForDepartment(4, item); 
      } 
      if (item.name === 'Sales & Marketing') { 
        return this.getSpliceForDepartment(6, item); 
      } 
      if (item.name === 'Service staff') { 
        return this.getSpliceForDepartment(7, item); 
      } 
      if (item.name === 'Other') { 
        return this.getSpliceForDepartment(8, item); 
      } 
      this.departmentsDic.push(item); 
    }); 
    dictionary.set('Department', this.departmentsDic); 
  } 
 
  getSpliceForDepartment(index, item): void { 
    this.departmentsDic.splice(index, 0, item); 
  } 
 
  monthsCelebrant(monthCelebrants): void { 
    this.months = monthCelebrants.map(months => 
      ({ 
        title: `AppModule.Month.${months.month}`, 
        count: months.count, 
        data: months, 
91 
ЧДТУ.221819.002 ПЗ 
      })); 
  } 
 
  resetPage(): void { 
    this.paginationService.resetCurrentPage(); 
  } 
 
  loadContractors(): void { 
    const { 
      filterPositionIds, 
      filterDepartmentIds, 
      filterLineManagers, 
      filterProjects, 
      filterOffices, 
      filterInternships, 
      showOnlyInterns, 
      showOnlyProbation, 
      contractorsType, 
      fromDateInternship, 
      toDateInternship, 
      fromDateContract, 
      toDateContract, 
    } = this.getCriteriaFromFilters(); 
 
    this.contractorService.getFilteredContractors( 
      filterDepartmentIds, 
      filterPositionIds, 
      filterLineManagers, 
      filterProjects, 
      filterOffices, 
      filterInternships, 
      showOnlyInterns, 
      showOnlyProbation, 
      contractorsType, 
      fromDateInternship, 
      toDateInternship, 
      fromDateContract, 
      toDateContract, 
    ) 
      .subscribe(contractorsResponse => { 
        this.contractors = contractorsResponse.body; 
        this.fullContractors = contractorsResponse.body; 
 
        this.paginationService.countOfPages$.next(+contractorsResponse.headers.get
('x-total-pages-count')); 
92 
ЧДТУ.221819.002 ПЗ 
      }); 
  } 
 
  getCriteriaFromFilters(): any { 
    const filterPositionIds = []; 
    const filterDepartmentIds = []; 
    const filterLineManagers = []; 
    const filterProjects = []; 
    const filterOffices = []; 
    const filterInternships = []; 
    let fromDateInternship; 
    let toDateInternship; 
    let toDateContract; 
    let fromDateContract; 
    let showOnlyInterns = false; 
    let showOnlyProbation = false; 
    const contractorsType = this.contractorsType.toString(); 
 
    const value = this.filters$.getValue(); 
    value.forEach(f => { 
      if (f.type === 'Department') { 
        filterDepartmentIds.push({id: f.id, name: f.name}); 
      } else if (f.type === 'Position') { 
        filterPositionIds.push({id: f.id, name: f.name}); 
      } else if (f.type === 'LineManager') { 
        filterLineManagers.push({id: f.id, name: f.name}); 
      } else if (f.type === 'Projects') { 
        filterProjects.push({id: f.id, name: f.name}); 
      } else if (f.type === 'Office') { 
        filterOffices.push({id: f.id, name: f.name}); 
      } else if (f.type === 'Internship') { 
        filterInternships.push({id: f.id, name: f.name}); 
      } else if (f.type === 'internFilter') { 
        showOnlyInterns = f.name === 'true'; 
      } else if (f.type === 'probationFilter') { 
        showOnlyProbation = f.name === 'true'; 
      } else if (f.type === 'fromDateInternship') { 
        fromDateInternship = f.name; 
      } else if (f.type === 'toDateInternship') { 
        toDateInternship = f.name; 
      } else if (f.type === 'toDateContract') { 
        toDateContract = f.name; 
      } else if (f.type === 'fromDateContract') { 
        fromDateContract = f.name; 
      } 
93 
ЧДТУ.221819.002 ПЗ 
    }); 
 
    return { 
      filterPositionIds, 
      filterDepartmentIds, 
      filterLineManagers, 
      filterProjects, 
      filterOffices, 
      filterInternships, 
      showOnlyInterns, 
      showOnlyProbation, 
      contractorsType, 
      fromDateInternship, 
      toDateInternship, 
      toDateContract, 
      fromDateContract, 
    }; 
  } 
 
  getContractorsType(name): void { 
    if (localStorage.getItem('currentLanguageKey') === 'uk') { 
      if (name === 'active') { 
        this.contractorsType = ContractorsTypesEnum.ACTIVE; 
        this.form.controls.contractorsType.setValue({ 
          id: 0, 
          type: 'contractorsType', 
          value: 'Активні профілі', 
        }); 
      } else if (name === 'blocked') { 
        this.contractorsType = ContractorsTypesEnum.BLOCKED; 
        this.form.controls.contractorsType.setValue({id: 1, type: 'contractorsType', 
value: 'Заблоковані профілі'}); 
      } else { 
        this.contractorsType = ContractorsTypesEnum.ARCHIVED; 
        this.form.controls.contractorsType.setValue({id: 2, type: 'contractorsType', 
value: 'Заархівовані профілі'}); 
      } 
    } else { 
      if (name === 'active') { 
        this.contractorsType = ContractorsTypesEnum.ACTIVE; 
        this.form.controls.contractorsType.setValue({ 
          id: 0, 
          type: 'contractorsType', 
          value: 'Active profiles', 
        }); 
94 
ЧДТУ.221819.002 ПЗ 
      } else if (name === 'blocked') { 
        this.contractorsType = ContractorsTypesEnum.BLOCKED; 
        this.form.controls.contractorsType.setValue({id: 1, type: 'contractorsType', 
value: 'Blocked profiles'}); 
      } else { 
        this.contractorsType = ContractorsTypesEnum.ARCHIVED; 
        this.form.controls.contractorsType.setValue({ 
          id: 2, 
          type: 'contractorsType', 
          value: 'Archived profiles', 
        }); 
      } 
    } 
  } 
 
  getContractorsTypes(): DictionaryModel[] { 
    if (localStorage.getItem('currentLanguageKey') === 'uk') { 
      return [{id: 0, type: 'contractorsType', value: 'Активні профілі'}, 
              { id: 1, type: 'contractorsType', value: 'Заблоковані профілі'}, 
              { id: 2, type: 'contractorsType', value: 'Заархівовані профілі'}, 
      ]; 
    } 
 
    return [{ id: 0, type: 'contractorsType', value: 'Active profiles'}, 
            { id: 1, type: 'contractorsType', value: 'Blocked profiles'}, 
            { id: 2, type: 'contractorsType', value: 'Archived profiles'}, 
    ]; 
  } 
 
  addFilter(filter: FilterModel): void { 
    if (filter.type === 'internFilter') { 
      this.checkedInterns = filter.name === 'true'; 
    } 
 
    if (filter.type === 'probationFilter') { 
      this.checkedProbation = filter.name === 'true'; 
    } 
 
    this.contractorFilterService.addFilter(filter); 
  } 
 
  deleteFilter(filter: FilterModel): void { 
    this.contractorFilterService.removeFilter(filter); 
  } 
 
95 
ЧДТУ.221819.002 ПЗ 
  changePageAndLoadContractors(currentPage: number): void { 
    this.paginationService.currentPage$.next(currentPage); 
    this.loadContractors(); 
  } 
 
  changePageSizeAndLoadContractors(currentPage: number): void { 
    this.paginationService.pageSize$.next(currentPage); 
    this.loadContractors(); 
  } 
 
  exportExcel(): void { 
    const { 
      filterPositionIds, 
      filterDepartmentIds, 
      filterLineManagers, 
      filterProjects, 
      filterOffices, 
      filterInternships, 
      showOnlyInterns, 
      showOnlyProbation, 
      contractorsType, 
      fromDateInternship, 
      toDateInternship, 
      fromDateContract, 
      toDateContract, 
    } = this.getCriteriaFromFilters(); 
    this.contractorService.exportExcel( 
      filterDepartmentIds, filterPositionIds, 
      filterLineManagers, filterProjects, filterOffices, 
      filterInternships, showOnlyInterns, 
      showOnlyProbation, contractorsType, 
      fromDateInternship, toDateInternship, 
      fromDateContract, toDateContract) 
      .subscribe(response => { 
        const blob = new Blob([response.body], {type: 'text/csv; charset=utf-8'}); 
        this.saveFile(blob, 'contractors.xlsx'); 
      }); 
  } 
 
  exportCsv(): void { 
    const { 
      filterPositionIds, 
      filterDepartmentIds, 
      filterLineManagers, 
      filterProjects, 
96 
ЧДТУ.221819.002 ПЗ 
      filterOffices, 
      filterInternships, 
      showOnlyInterns, 
      showOnlyProbation, 
      contractorsType, 
      fromDateInternship, 
      toDateInternship, 
      fromDateContract, 
      toDateContract, 
    } = this.getCriteriaFromFilters(); 
 
    this.contractorService.exportCsv(filterDepartmentIds, filterPositionIds, 
filterLineManagers, filterProjects, filterOffices, filterInternships, showOnlyInterns, 
showOnlyProbation, contractorsType, fromDateInternship, toDateInternship, 
fromDateContract, toDateContract) 
      .subscribe(response => { 
        const blob = new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), 
response.body], {type: 'text/csv; charset=utf-8'}); 
        this.saveFile(blob, 'contractors.csv'); 
      }); 
  } 
 
  saveFile(blob: Blob, filename?: string): void { 
    fileSaver.saveAs(blob, filename); 
  } 
 
  getPaginationConfig(): PaginationConfig { 
    return { 
      pagesCount: this.paginationService.countOfPages$, 
      activePage: this.paginationService.currentPage$, 
      pageSize: this.paginationService.pageSize$, 
 
      onChangePage: this.changePageAndLoadContractors.bind(this), 
      onChangePageSize: this.changePageSizeAndLoadContractors.bind(this), 
 
    }; 
  } 
 
  saveFilters(form: FormGroup): void { 
    if (form.controls.toDateInternship.value !== '') { 
      this.addFilter({id: 0, type: 'toDateInternship', name: 
form.controls.toDateInternship.value}); 
    } 
    if (form.controls.fromDateInternship.value !== '') { 
97 
ЧДТУ.221819.002 ПЗ 
      this.addFilter({id: 0, type: 'fromDateInternship', name: 
form.controls.fromDateInternship.value}); 
    } 
    if (form.controls.toDateContract.value !== '') { 
      this.addFilter({id: 0, type: 'toDateContract', name: 
form.controls.toDateContract.value}); 
    } 
    if (form.controls.fromDateContract.value !== '') { 
      this.addFilter({id: 0, type: 'fromDateContract', name: 
form.controls.fromDateContract.value}); 
    } 
    const value = form.controls.contractorsType.value.id; 
    if (value === 0) { 
      this.contractorsType = ContractorsTypesEnum.ACTIVE; 
    } else if (value === 1) { 
      this.contractorsType = ContractorsTypesEnum.BLOCKED; 
    } else { 
      this.contractorsType = ContractorsTypesEnum.ARCHIVED; 
    } 
    this.buildFilterConfig(); 
    this.loadContractors(); 
    this.resetPage(); 
    this.changeFilter(); 
  } 
 
  clearFilters(): void { 
    this.contractorFilterService.clearFilters(); 
    this.contractorsType = ContractorsTypesEnum.ACTIVE; 
    this.checkedProbation = false; 
    this.checkedInterns = false; 
    this.getContractorsType(this.contractorsType.toLowerCase()); 
    this.buildFilterConfig(); 
    this.loadContractors(); 
    this.resetPage(); 
    this.changeFilter(); 
  } 
 
  buildFilterConfig(): void { 
    this.filterConfig = [ 
      { 
        data: this.checkedInterns, 
        type: FilterType.CHECKBOX, 
        title: 'ContractorsModule.Filter.Intern', 
        name: 'internFilter', 
      }, 
98 
ЧДТУ.221819.002 ПЗ 
      { 
        data: this.checkedProbation, 
        type: FilterType.CHECKBOX, 
        title: 'ContractorsModule.Filter.Probation', 
        name: 'probationFilter', 
      }, 
      { 
        data: this.managersMap, type: FilterType.MULTISELECTOR, 
      }, 
      { 
        data: this.projectsMap, type: FilterType.MULTISELECTOR, 
      }, 
      { 
        data: this.officesMap, type: FilterType.MULTISELECTOR, 
      }, 
      { 
        data: this.internshipMap, type: FilterType.MULTISELECTOR, 
      }, 
      { 
        data: this.form, 
        type: FilterType.DATEPICKERS, 
        title: 'AppModule.Filter.Internship', 
        fromDate: 'fromDateInternship', 
        toDate: 'toDateInternship', 
      }, 
      { 
        data: this.form, 
        type: FilterType.DATEPICKERS, 
        title: 'AppModule.Filter.DateContracts', 
        fromDate: 'fromDateContract', 
        toDate: 'toDateContract', 
      }, 
      { 
        data: this.departmentsMap, type: FilterType.MULTISELECTOR, 
      }, 
      { 
        data: this.positionsMap, type: FilterType.MULTISELECTOR, 
      }, 
      { 
        data: this.getContractorsTypes(), 
        type: FilterType.SELECTOR, 
        title: 'ContractorsModule.Filter.ContractorsType', 
        name: 'contractorsType', 
      }, 
    ]; 
99 
ЧДТУ.221819.002 ПЗ 
  } 
 
  changeSettings(): void { 
    const names = this.columns; 
    const dialogRef = this.dialog.open(ConfigurationColumnComponent, {data: 
{names}, width: '400px'}); 
 
    dialogRef.afterClosed().subscribe(config => { 
      if (config) { 
        this.config = config; 
        this.array = []; 
        this.columns.forEach(c => { 
          if (c.checked === true) { 
            this.array.push({id: c.id, disabled: c.disabled, checked: c.checked}); 
          } 
        }); 
        localStorage.setItem('columnsContractor', JSON.stringify(this.array)); 
        this.translate 
          .get('AppModule.CommonSuccess.ConfigurationSuccess') 
          .subscribe(m => this.notificationService.showSuccess(m)); 
      } 
    }); 
  } 
 
  buildColumnsName(): void { 
    if (this.locale === 'en') { 
      this.contractor = ['surnameEng']; 
    } else { 
      this.contractor = ['surnameUk']; 
    } 
 
    const contractorNameField = field(this.getContractorComponentArguments, 
undefined, ContractorHrefComponent); 
    const contractsField = field(this.getContract, undefined, 
ContractsFieldComponent); 
    this.columns = [ 
      { 
        id: 1, 
        name: 'ContractorsModule.TableView.FullName', 
        disabled: true, 
        checked: true, 
        value: col('260px', 'ContractorsModule.TableView.FullName', 
contractorNameField, { 
          sorted: true, 
          typeSort: 'unsorted', 
100 
ЧДТУ.221819.002 ПЗ 
          sortColumnName: this.contractor, 
        }), 
      }, 
      { 
        id: 8, 
        name: 'ContractorsModule.Commons.LineManager', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.LineManager', 
field((contractor: ContractorModel) => this.getLineManager(contractor))), 
      }, 
      { 
        id: 9, 
        name: 'ContractorsModule.Commons.Project', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.Project', field((contractor: 
ContractorModel) => this.getProject(contractor).projects)), 
      }, 
      { 
        id: 11, 
        name: 'ContractorsModule.Commons.Office', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.Office', field((contractor: 
ContractorModel) => contractor.office ? contractor.office.nameUk : '')), 
      }, 
      { 
        id: 12, 
        name: 'ContractorsModule.Commons.Contract', 
        checked: false, 
        value: col('400px', 'ContractorsModule.Commons.Contract', contractsField), 
      }, 
      { 
        id: 2, 
        name: 'ContractorsModule.Commons.Department', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.Department', 
field((contractor: ContractorModel) => contractor.department.value)), 
      }, 
      { 
        id: 3, 
        name: 'ContractorsModule.Commons.Position', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.Position', field((contractor: 
ContractorModel) => contractor.position.value)), 
      }, 
      { 
101 
ЧДТУ.221819.002 ПЗ 
        id: 10, 
        name: 'ContractorsModule.Commons.DateOfEmployment', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.DateOfEmployment', 
field((contractor: ContractorModel) => contractor.dateOfSigningTheContract ? 
this.shortLocaleDatePipe.transform(contractor.dateOfSigningTheContract) : '')), 
      }, 
      { 
        id: 6, 
        name: 'ContractorsModule.Commons.SeasonInternship', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.SeasonInternship', 
field((contractor: ContractorModel) => contractor.internship !== undefined && 
contractor.internship !== null ? contractor.internship.season : '')), 
      }, 
      { 
        id: 4, 
        name: 'ContractorsModule.Commons.PersonalEmail', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.PersonalEmail', 
field((contractor: ContractorModel) => contractor.personalEmail)), 
      }, 
      { 
        id: 5, 
        name: 'ContractorsModule.Commons.Email', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.Email', field((contractor: 
ContractorModel) => contractor.email)), 
      }, 
      { 
        id: 7, 
        name: 'ContractorsModule.Commons.TelephoneNumber', 
        checked: false, 
        value: col('200px', 'ContractorsModule.Commons.TelephoneNumber', 
field((contractor: ContractorModel) => contractor.telephoneNumber)), 
      }, 
    ]; 
  } 
 
  getContractorComponentArguments = (contractor: ContractorModel): any => ({ 
    contractor, 
    size: 'S', 
  }); 
 
  getProject = (contractor: ContractorModel): any => { 
102 
ЧДТУ.221819.002 ПЗ 
    if (this.assignments !== undefined && this.assignments !== null) { 
      return { 
        projects: this.assignments 
          .filter(a => a.contractor.id === contractor.id) 
          .filter(a => this.assignments.filter(b => b.projectName === 
a.projectName).length > 1) 
          .map(a => a.projectName), 
      }; 
    } 
  }; 
 
  changeFilter(): void { 
    this.isOpenFilter = !this.isOpenFilter; 
  } 
 
  getContract(contractor: ContractorModel): any { 
    return { 
      contracts: contractor.contracts.reduce((a, b) => a.concat({contractNumber: 
b.contractNumber, agreements: b.agreements }), [])}; 
  } 
 
  private getLineManager(contractor: ContractorModel): string { 
    let contractorModel = null; 
    this.managers.forEach(m => { 
      if (m.id === contractor.lineManagerId) { 
        contractorModel = m; 
      } 
    }); 
 
    return contractorModel !== null && contractorModel !== undefined ? 
this.localContractorPipe.transform(contractorModel) : ''; 
  } 
 
  private createFormGroup(): FormGroup { 
    return this.formBuilder.group({ 
      fromDateInternship: [ 
        '', 
        Validators.required, 
      ], 
      toDateInternship: [ 
        '', 
        Validators.required, 
      ], 
      fromDateContract: [ 
        '', 
103 
ЧДТУ.221819.002 ПЗ 
        Validators.required, 
      ], 
      toDateContract: [ 
        '', 
        Validators.required, 
      ], 
      contractorsType: [ 
        '', 
        [Validators.required], 
      ], 
    }); 
  } 
} 
 
 
 
 
 
 
 
104 
Додаток В 
ДОДАТОК В 
 
 
 
 
 
 
РОЗРОБКА ІНФОРМАЦІЙНОЇ СИСТЕМИ ПО УПРАВЛІННЮ 
ПЕРСОНАЛОМ КОМПАНІЇ 
Інструкція користувачеві 
482.ЧДТУ. 221819-01 34-01 
 
Листів 10 
 
 
 
 
Розробник: ________________________________________Аркалюк В.А. 
 
 
 
 
 
 
 
 
 
 
Черкаси, 2022
ЧДТУ.221819.002 ПЗ 
 
Опис інтерфейсу та функціональних можливостей 
 
Процес авторизації користувача 
Для того, щоб авторизуватись користувачу у систему, потрібно:  
зайти на сайт системи;  
у формі авторизації потрібно ввести логін та пароль облікового запису 
користувача, який йому надав адміністратор системи та натиснути на кнопку 
«Увійти».  
Форма авторизації в систему зображено на рисунку В.1. 
 
 
Рисунок В.1 – Форма авторизації користувача 
 
Процес створення заяви 
Для того, щоб створити заяву потрібно:  
➢ зайти на сайт системи;  
➢ натиснути на кнопку «Подати заяву» та обрати потрібний тип заяви;  
➢ після цього відкриється форма створення заяви.  
Модальне вікно створення заяви зображена на рисунку В.2. 
106 
ЧДТУ.221819.002 ПЗ 
 
Рисунок В.2 – Форма створення заяви  
 
Процес перегляду створених заяв користувача 
Для того, щоб переглянути створені заяви користувача у системі, потрібно:  
➢ зайти на сайт веб-додатку;  
➢ у формі авторизації потрібно ввести логін та пароль облікового запису 
користувача, який надав адмін системи та натиснути на кнопку 
«Увійти».  
Після процесу авторизації, користувачу за замовчуванням відкриється 
сторінка його заяв. Сторінка заяв зображено на рисунку В.3. 
Сторінка заяв користувача складається з:  
➢ таблиця зі створеними заявами користувача (рисунок В.3);  
➢ меню навігації веб-додатку (рисунок В.4);  
➢ блок з балансом днів (рисунок В.5). 
 
107 
ЧДТУ.221819.002 ПЗ 
 
Рисунок В.3 – Сторінка заяв користувача 
 
 
108 
ЧДТУ.221819.002 ПЗ 
 
 
 
Рисунок В.4 – Меню навігації системи 
109 
ЧДТУ.221819.002 ПЗ 
 
 
Рисунок В.5 – Блок балансу днів користувача 
Блок балансу днів користувача відображає наступну інформацію:  
➢ баланс днів на відпочинок;  
➢ баланс днів на відпочинок з урахуванням поданих заяв;  
➢ компенсацію на місяць за станом здоров’я. 
 
 
Процес опрацювання заяви менеджером 
Після того, як контрактор створив заяву потрібно щоб менеджер її 
опрацював. Для того, щоб це зробити менеджер повинен залогінитись в 
систему і перейти на сторінку Заяви на якій відображаються заяви усіх 
контракторів. Після цього знайти потрібну заяву, натиснути на контекстне 
меню і обрати пункт “Опрацювати” і відкриється модальне вікно 
опрацювання заяви, яке зображено на рисунку В.6. Менеджер має дві опції 
або підтвердити заяву або відхилити її. Після вибору із цих двох опцій заява 
змінює свій стан на “Підтверджений” або “Відхилений”. 
 
110 
ЧДТУ.221819.002 ПЗ 
 
Рисунок В.6 – Модальне вікно опрацювання заяви 
 
Процес перегляду інформації про контракторів компанії 
Для перегляду інформації про рахунки користувача потрібно:  
➢ зайти на сайт веб-додатку;  
➢ у формі авторизації потрібно ввести електронну адресу та пароль 
облікового запису користувача та натиснути на кнопку «Увійти»;  
➢ у меню навігації веб-додатку клікнути на пункт «Контрактори»;  
➢ в таблиці обрати потрібного контрактора і відкриється профіль 
контрактора.  
Сторінку перегляду інформації про контрактора зображено на рисунку В.8.  
 
 
111 
ЧДТУ.221819.002 ПЗ 
 
Рисунок В.8 – Сторінку перегляду інформації про контрактора компанії 
 
Сторінку перегляду інформації про контрактора складається з кнопки 
«Доступні дії» та блоку з інформацією та аватаркою користувача.  
В блоку з інформацією користувача відображаєть наступна інформація про 
нього: 
➢ ПІП;  
➢ дата народження;  
➢ відділ;  
➢ позиція;  
➢ студент;  
➢ телефон;  
➢ дата, коли був найнятий;  
➢ корпоративний емейл. 
 
 
112 
ЧДТУ.221819.002 ПЗ 
Процес генерації звітів по заявам користувача 
Для того щоб згенерувати звіт потрібно обрати наступні поля:  
➢ дата початку;  
➢ дата закінчення;  
➢ відділ − формат звіту (xlsx, csv)  
Сторінку створення рахунку користувача зображено на рисунку В.9. 
 
. 
 
Рисунок В.9 – Сторінка звітів по заявам 
Після генерації репорту він має наступний вигляд зображено на 
рисунку В.10. Звіт має наступні колонки з інформацією по заявам:  
➢ ім’я, хто подав заяву;  
➢ ID;  
➢ відділ; 
➢ тип;  
➢ статус;  
➢ дата початку; 
➢ дата закінчення;  
➢ кількість днів;  
➢ до компенсації;  
➢ коментар. 
113 
ЧДТУ.221819.002 ПЗ 
 
Рисунок В.10 – Вигляд звіту по заявам 
114 
Додаток Г 
ДОДАТОК Г 
 
 
 
 
 
 
РОЗРОБКА ІНФОРМАЦІЙНОЇ СИСТЕМИ ПО УПРАВЛІННЮ 
ПЕРСОНАЛОМ КОМПАНІЇ 
Графічні матеріали 
482.ЧДТУ. 221819-01 90-01 
 
Листів 12 
 
 
 
 
Розробник: ________________________________________Аркалюк В.А. 
 
 
 
 
 
 
 
 
 
Черкаси, 2022
ЧДТУ.221819.002 ПЗ 
Схема бази даних проекту 
 
Рисунок Г.1 – Схема бази даних контракторів 
116 
ЧДТУ.221819.002 ПЗ 
 
Рисунок Г.2 – Схема бази даних заяв 
 
Діаграма DFD 
117 
ЧДТУ.221819.002 ПЗ 
 
Рисунок Г.3 – Діаграма DFD 
 
 
 
118 
ЧДТУ.221819.002 ПЗ 
Діаграма варіантів використання 
 
Рисунок Г.4 – Діаграма варіантів використання 
119 
ЧДТУ.221819.002 ПЗ 
Модель IDEF0 
 
Рисунок Г.5 – Модель IDEF0 
120 
ЧДТУ.221819.002 ПЗ 
Діаграма декомпозиції 
 
Рисунок Г.6 – Діаграма декомпозиції 
Форма авторизації користувача 
 
Рисунок Г.7 – Модальне вікно авторизації 
 
 
 
 
 
 
 
121 
ЧДТУ.221819.002 ПЗ 
 
 
Сторінка заяв користувача 
 
Рисунок Г.8 – Сторінка заяв користувача 
122 
ЧДТУ.221819.002 ПЗ 
Меню навігації інформаційної системи 
 
Рисунок Г.9 – Меню навігації інформаційної системи 
 
123 
ЧДТУ.221819.002 ПЗ 
Блок балансу днів контрактора 
 
Рисунок Г.10 – Блок балансу днів контрактора 
 
124 
ЧДТУ.221819.002 ПЗ 
Календар для вибору дати початку та кінця періоду відсутності 
 
Рисунок Г.11 – Календар для вибору дати початку та кінця періоду 
відсутності 
Сторінку перегляду інформації про контракторів 
 
125 
ЧДТУ.221819.002 ПЗ 
Рисунок Г.12 – Сторінку перегляду інформації про контракторів 
Сторінка профілю контрактора 
. 
Рисунок Г.13 – Сторінка профілю контрактора 
Сторінка редагування профілю контрактора  
 
Рисунок Г.14 – Сторінка редагування профілю контрактора  
126 
ЧДТУ.221819.002 ПЗ 
 
 
Модальне вікно створення заяви 
 
Рисунок Г.15 – Модальне вікно створення заяви 
 
Модальне вікно опрацювання заяви користувача 
 
Рисунок Г.16 – Модальне вікно опрацювання заяви користувача 
 
127 
ЧДТУ.221819.002 ПЗ 
Сторінка звітів  
 
Рисунок Г.17 – Сторінка звітів 
Вигляд звіту по заявам користувачів 
 
Рисунок Г.18 – Вигляд звіту по заявам користувачів 
 
128