Залишки відпусток (Employee Absences)¶
Залишки відпусток - це механізм управління балансом доступних і використаних днів відпустки для співробітників. Кожен запис про залишки прив'язаний до конкретного типу відпустки (щорічна, навчальна, тощо) та періоду (робочий рік або календарний рік).
Різниця між Залишками (Absences) та Задачами "Відпустка" (Vacation Tasks)¶
Важливо розуміти різницю між цими двома концепціями:
| Аспект | Залишки (Absences) | Задача "Відпустка" (Vacation Task) |
|---|---|---|
| Призначення | Управління балансом доступних днів відпустки | Процес оформлення конкретної відпустки з підписанням документів |
| Що зберігає | allowed_days (надано), left_days (залишок), used (використано — сума списань; обчислюється, але можна задати вручну) |
Деталі конкретної відпустки: дати, підписанти, документи |
| Період | Робочий або календарний рік | Конкретні дати початку і кінця відпустки |
| API операції | POST, GET, PATCH для управління балансом; POST /absences/bulk для пакетної синхронізації |
POST для створення задачі, GET для відстеження статусу підписання |
| Взаємодія | Автоматично оновлюється при створенні задачі "Відпустка" | Використовує залишки для валідації і списання днів |
Приклад взаємодії¶
-
Створюється запис про залишки: співробітнику надається 24 дні щорічної відпустки на робочий рік
{ "absence_type": "annual", "allowed_days": 24, "left_days": 24, "date_start": "2024-01-15" } -
Створюється задача "Відпустка": співробітник йде у відпустку на 10 днів
- Система автоматично зменшує
left_daysз 24 до 14 -
usedзбільшується з 0 до 10 (додається рядок списання вusages) -
Оновлення залишків: HR може вручну скоригувати баланс через PATCH ендпоінт, наприклад, якщо була помилка або додано додаткові дні. Через поле
usedможна також вручну задати кількість використаних за період днів (незалежно відleft_days)
Типи відпусток (Absence Types)¶
Кожен запис про залишки прив'язаний до одного з наступних типів:
| Тип залишків | Опис | Період за замовчуванням |
|---|---|---|
annual |
Щорічна основна відпустка | Робочий рік |
annual_hazardous |
Щорічна додаткова за шкідливі умови праці | Робочий рік |
annual_special_work |
Щорічна додаткова за особливий характер праці | Робочий рік |
social_maternity_leave |
Декретна відпустка | Календарний рік |
social_child_care |
Відпустка для догляду за дитиною | Календарний рік |
social_parental |
Відпустка для батьків | Календарний рік |
social_child_birth |
Відпустка при народженні дитини | Календарний рік |
social_adoption |
Відпустка при усиновленні | Календарний рік |
study_educational |
Навчальна відпустка | Календарний рік |
vacation_social_pco |
Додаткова для учасників бойових дій | Календарний рік |
vacation_social_chornobyl |
Додаткова для постраждалих від Чорнобильської катастрофи | Календарний рік |
vacation_social_revolution_dignity |
Додаткова для постраждалих учасників Революції Гідності | Календарний рік |
vacation_donor_rest_day |
День відпочинку донору | Календарний рік |
study_creative |
Творча відпустка | Календарний рік |
study_athletic |
Спортивна відпустка | Календарний рік |
unpaid |
Відпустка без збереження зарплати | Календарний рік |
unpaid_agreed |
Додаткова без збереження зарплати за згодою сторін | Календарний рік |
unpaid_martial_law |
Без збереження зарплати на період воєнного стану | Календарний рік |
vacation_annual_onboarding |
Основна відпустка при онбордингу | Робочий рік |
vacation_unpaid_onboarding |
Відпустка без збереження зарплати при онбордингу | Календарний рік |
custom |
Користувацький тип відпустки | Залежить від шаблону |
Типи періодів¶
Робочий рік (Working Year)¶
- Відлік починається з дати прийому на роботу (
date_joined) - Наприклад, якщо співробітник прийнятий 15 січня 2024, робочий рік: 15.01.2024 - 14.01.2025
- Використовується для щорічних основних та додаткових відпусток
Календарний рік (Calendar Year)¶
- Відлік з 1 січня по 31 грудня
- Використовується для соціальних, навчальних та неоплачуваних відпусток
Поля запису про залишки¶
Збережений запис про залишки (Absence) містить наступні поля:
| Поле | Тип | Опис | Обов'язкове |
|---|---|---|---|
id |
UUID | Унікальний ідентифікатор запису | Автоматично |
period_id |
UUID | ID періоду (робочого або календарного року), до якого прив'язаний запис | Автоматично |
absence_type |
AbsenceType | Тип відпустки (див. таблицю вище) | Так |
task_template_identifier |
UUID | ID шаблону відпустки для типу custom |
Так для custom |
allowed_days |
decimal | Кількість наданих днів відпустки | Ні (опційно для POST) |
left_days |
decimal | Залишок невикористаних днів | Ні (опційно для POST) |
monthly_changes |
array | Історія щомісячних змін кількості наданих днів | Автоматично |
Поле date_start приймається лише як вхідний параметр при створенні запису (POST) і визначає початок періоду; у збереженому записі дата зберігається в межах періоду.
Обчислювані поля при отриманні списку (GET)¶
При отриманні списку залишків (GET) кожен запис додатково містить обчислювані поля:
| Поле | Опис |
|---|---|
used |
Використано днів за період — сума рядків usages: списання за задачами "Відпустка" плюс ручні коригування "використано за період". Нарахування (accrued) не враховуються. null, якщо списань не було |
accrued |
Нараховано днів за період (автоматичні щомісячні нарахування). null, якщо нарахувань не було |
left_days_for_period |
Залишок за період, обчислюється як allowed_days - used |
usages |
Список усіх рядків руху днів за період: списання за задачами, ручні коригування «використано», а також автоматичні нарахування. used рахується як сума списань і ручних коригувань (нарахування йдуть в accrued) |
Важливо: used ≠ allowed_days - left_days
used — це сума фактичних списань (usages), а не різниця allowed_days - left_days.
Поле left_days — це окремий збережений баланс, який автоматично зменшується лише при
оформленні задачі "Відпустка". Ручне коригування used (через POST/PATCH) не змінює
left_days, і навпаки — ці два поля керуються незалежно.
Наприклад, для запису з allowed_days = 24, за яким списано 7 днів (5 за задачею + 2 ручним коригуванням):
used: 7 (сумаusages)left_days_for_period: 17 (24 - 7)left_days: значення збереженого балансу (окреме, залежить від автоматичних списань за задачами)
Поле used як вхідний параметр (POST / PATCH)¶
Окрім обчислення на GET, used можна задати вручну в запитах POST і PATCH. Передане значення трактується як абсолютна кількість використаних за період днів (а не приріст): бекенд обчислює різницю відносно поточного used і записує коригування в історію (usages + подія балансу). Це коригування не впливає на left_days. null або відсутнє поле означає «не змінювати used».
API ендпоінти¶
1. Створити запис про залишки відпустки¶
POST /api/public/v1/employee/{employee_id}/absences
Створює новий запис про залишки відпустки для співробітника.
Приклад запиту:
{
"absence_type": "annual",
"date_start": "2024-01-15",
"allowed_days": 24,
"left_days": 24
}
Приклад для користувацького типу:
{
"absence_type": "custom",
"date_start": "2024-01-01",
"allowed_days": 10,
"left_days": 10,
"task_template_identifier": "123e4567-e89b-12d3-a456-426614174000"
}
Приклад із зазначенням використаних днів (used):
{
"absence_type": "annual",
"date_start": "2024-01-15",
"allowed_days": 24,
"left_days": 19,
"used": 5
}
Створює запис і одразу фіксує 5 використаних за період днів (додається рядок ручного коригування в usages). Поле used не впливає на left_days.
Важливо:
- Для типу custom обов'язково вказувати task_template_identifier
- date_start визначає початок періоду і залежить від типу відпустки
- allowed_days, left_days та used можуть бути дробовими числами (наприклад, 24.5)
- used — опційне; це абсолютна кількість використаних за період днів, яка записується як коригування історії і не змінює left_days
2. Отримати список залишків відпусток¶
GET /api/public/v1/employee/{employee_id}/absences
Повертає список залишків відпусток для співробітника, згрупований за робочими періодами, з обчисленням поля used.
Відповідь має вкладену структуру: кожен елемент списку — це робочий період (FullWorkingPeriodAbsence) з полями id, employee_id, date_start, date_end та масивом absences. Кожен запис у масиві absences містить деталі залишків за цим типом відпустки разом з обчислюваними полями (used, accrued, left_days_for_period, usages).
Приклад відповіді:
{
"detail": {
"items": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"employee_id": "987e6543-e21b-12d3-a456-426614174001",
"date_start": "2024-01-15",
"date_end": "2025-01-14",
"absences": [
{
"id": "456e7890-e12b-34c5-d678-426614174003",
"period_id": "123e4567-e89b-12d3-a456-426614174000",
"absence_type": "annual",
"task_template_identifier": null,
"allowed_days": 24,
"left_days": 14,
"monthly_changes": [],
"task_template": null,
"used": 10,
"accrued": null,
"left_days_for_period": 14,
"usages": [
{
"id": "aaa11111-2222-3333-4444-555566667777",
"absence_id": "456e7890-e12b-34c5-d678-426614174003",
"task_id": "789e0123-e45b-67c8-d901-426614174005",
"used_days": 10,
"is_manual_adjustment": false
}
]
}
]
}
],
"page_num": 1,
"page_size": 100,
"has_prev": false,
"has_next": false,
"total_items": 1,
"total_pages": 1
}
}
3. Оновити залишки відпустки¶
PATCH /api/public/v1/employee/{employee_id}/absences/{absence_id}
Оновлює кількість доступних (allowed_days, left_days) та/або використаних (used) днів відпустки. Використовується для коригування балансу, наприклад, при помилці або нарахуванні додаткових днів. Передаються лише ті поля, які потрібно змінити — відсутні поля залишаються без змін.
Приклад запиту (коригування наданих днів):
{
"allowed_days": 26,
"left_days": 16
}
Це збільшить баланс на 2 дні (було 24, стало 26).
Приклад запиту (коригування використаних днів):
{
"used": 5
}
Встановлює абсолютну кількість використаних за період днів у 5. Бекенд обчислює різницю відносно поточного used і записує коригування в історію (usages). Це поле не змінює left_days — використані та залишкові дні керуються незалежно.
Поля запиту:
| Поле | Тип | Опис |
|---|---|---|
allowed_days |
decimal | Нова кількість наданих днів |
left_days |
decimal | Новий залишок днів |
used |
decimal | Абсолютна кількість використаних за період днів (записується як коригування; не впливає на left_days) |
task_template_identifier |
UUID | Ідентифікатор шаблону (для типу custom) |
apply_from |
date | Дата, з якої застосовувати зміну allowed_days |
4. Масово створити або оновити залишки¶
POST /api/public/v1/absences/bulk
Синхронний пакетний запит для інтеграцій, які регулярно (наприклад, щоночі) передають повний стан залишків по всіх співробітниках. Один запит приймає до 200 рядків; кожен рядок — це повний бажаний стан одного запису про залишки для одного співробітника за один період. Результат по кожному рядку повертається одразу в тілі відповіді — без черги, без полінгу.
Природний ключ рядка. Записи ідентифікуються не внутрішнім id, а комбінацією
employee_id + date_start + absence_type + task_template_identifier
Якщо запису з таким ключем ще немає — його буде створено (created); якщо є — оновлено (updated).
Зберігати absence_id на боці інтегратора не потрібно. Повторне надсилання того самого стану нічого
не змінює і не породжує записів в історії (skipped).
Приклад запиту:
{
"items": [
{
"employee_id": "0b1f2c3d-4e5f-6789-abcd-ef0123456789",
"absence_type": "annual",
"date_start": "2026-03-10",
"allowed_days": 24,
"left_days": 18.5,
"used": 5.5,
"apply_from": "2026-08-01",
"row_ref": "erp-2026-1001"
},
{
"employee_id": "0b1f2c3d-4e5f-6789-abcd-ef0123456789",
"absence_type": "custom",
"date_start": "2026-01-01",
"task_template_identifier": "123e4567-e89b-12d3-a456-426614174000",
"allowed_days": 10,
"left_days": 10,
"row_ref": "erp-2026-1002"
}
]
}
Поля рядка:
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
employee_id |
UUID | Так | Ідентифікатор співробітника. Має належати компанії токена |
absence_type |
AbsenceType | Так | Тип відпустки (див. таблицю типів вище) |
date_start |
date | Так | Початок періоду: 1 січня для календарних типів, річниця дати прийняття для типів робочого року |
task_template_identifier |
UUID | Для custom |
Ідентифікатор шаблону користувацького типу відпустки |
allowed_days |
decimal | Ні | Нараховано днів за період |
left_days |
decimal | Ні | Залишок днів. Керується окремо від used |
used |
decimal | Ні | Абсолютна кількість використаних за період днів (не приріст). Записується як коригування в історію, left_days не змінює |
apply_from |
date | Ні | Дата, з якої діє зміна allowed_days (лише при оновленні наявного запису) |
row_ref |
string | Ні | Довільний ключ інтегратора (до 255 символів). Повертається без змін у відповідному елементі результату |
Семантика полів така ж, як у поодиноких POST / PATCH. Пропущені поля не змінюються: рядок лише
з used не занулить allowed_days і left_days у наявному записі.
Приклад відповіді — 200 OK:
{
"detail": {
"summary": {"total": 3, "created": 1, "updated": 1, "skipped": 0, "failed": 1},
"items": [
{
"index": 0,
"row_ref": "erp-2026-1001",
"status": "updated",
"absence_id": "a9c1b2d3-1111-2222-3333-444455556666",
"error_code": null,
"error_message": null,
"field": null
},
{
"index": 1,
"row_ref": "erp-2026-1002",
"status": "created",
"absence_id": "b7d2e3f4-1111-2222-3333-444455556666",
"error_code": null,
"error_message": null,
"field": null
},
{
"index": 2,
"row_ref": "erp-2026-1003",
"status": "error",
"absence_id": null,
"error_code": "employee_not_found",
"error_message": "Employee 5e6f7a8b-... does not belong to this company.",
"field": "employee_id"
}
]
}
}
| Поле результату | Опис |
|---|---|
summary.total / created / updated / skipped / failed |
Підсумок по батчу. Ознака повного успіху — failed == 0 |
items[].index |
Позиція рядка в надісланому масиві items (від 0) |
items[].row_ref |
Ваш row_ref без змін; null, якщо не передавався |
items[].status |
created — запис створено; updated — оновлено; skipped — надіслані значення збігаються зі збереженими, запису немає; error — рядок не застосовано |
items[].absence_id |
Ідентифікатор запису про залишки для успішних рядків; null для помилкових |
items[].error_code / error_message / field |
Код помилки, пояснення і поле рядка, з яким пов'язана помилка (null, якщо не прив'язана до поля) |
Часткове застосування. Рядки обробляються незалежно: один невалідний рядок не скасовує решту.
Валідні рядки записуються, невалідні повертаються зі status = "error". Повторно надсилати треба лише
невдалі рядки — їх зручно зібрати за row_ref.
Коди помилок рівня рядка (закритий список, на нього можна писати автоматичну обробку):
error_code |
field |
Що сталося |
|---|---|---|
employee_not_found |
employee_id |
Співробітника не існує або він належить іншій компанії |
absence_type_invalid |
absence_type |
У компанії немає шаблону відпустки цього типу |
task_template_identifier_required |
task_template_identifier |
Для absence_type = "custom" не вказано шаблон |
task_template_not_found |
task_template_identifier |
Шаблон не існує, належить іншій компанії або не є шаблоном користувацької відпустки |
employee_date_joined_not_found |
employee_id |
У співробітника не заповнена дата прийняття, тож робочий рік не визначити |
working_period_invalid_start_date |
date_start |
date_start не є 1 січня для календарного типу відпустки |
working_period_before_date_joined |
date_start |
date_start раніший за дату прийняття співробітника |
working_period_not_same_day_month |
date_start |
date_start не збігається з днем і місяцем прийняття для типу робочого року |
validation_error |
— | Інша доменна помилка; текст — в error_message |
Помилки рівня запиту (жоден рядок не застосовується):
| HTTP | error_code |
Причина |
|---|---|---|
422 |
bulk_absences_too_many_items |
У items понад 200 елементів. Розбийте дані на кілька запитів |
422 |
ValidationError |
Формат payload не відповідає схемі: не дата, не число, невідомий absence_type, порожній items |
401 |
— | Відсутній або недійсний токен |
Обмеження.
- До 200 рядків за запит. 10 000 записів — це 50 послідовних викликів; батч, у якому нічого не змінилося, обробляється за частки секунди, батч із записами — приблизно 1 с.
- Запит нічого не видаляє: запис, якого немає в payload, лишається в системі без змін. Щоб обнулити
залишок, надішліть його явно (
allowed_days: 0,left_days: 0). - Захисту від паралельних запусків немає.
used— абсолютне значення, дельта якого рахується відносно поточного стану, тож два одночасні запуски по тих самих записах можуть подвоїти коригування. Запускайте синхронізацію послідовно, одним процесом.
Сценарії використання¶
Сценарій 1: Створення залишків при прийомі на роботу¶
При прийомі співробітника необхідно створити записи про залишки для різних типів відпусток:
-
Щорічна основна відпустка (24 дні):
POST /api/public/v1/employee/{employee_id}/absences { "absence_type": "annual", "date_start": "2024-01-15", // Дата прийому на роботу "allowed_days": 24, "left_days": 24 } -
Додаткова відпустка для учасника бойових дій (14 днів), якщо застосовується:
POST /api/public/v1/employee/{employee_id}/absences { "absence_type": "vacation_social_pco", "date_start": "2024-01-01", // Початок календарного року "allowed_days": 14, "left_days": 14 }
Сценарій 2: Оформлення відпустки через задачу¶
-
Перевірити залишки перед створенням задачі:
Переконатися, щоGET /api/public/v1/employee/{employee_id}/absencesleft_days >= кількість днів відпустки -
Створити задачу "Відпустка":
Система автоматично зменшитьPOST /api/public/v1/vacationleft_daysта збільшить обчислюванеused -
Перевірити оновлені залишки:
GET /api/public/v1/employee/{employee_id}/absences
Сценарій 3: Коригування балансу¶
Якщо потрібно додати або зменшити кількість днів (наприклад, помилка або нарахування додаткових днів):
PATCH /api/public/v1/employee/{employee_id}/absences/{absence_id}
{
"allowed_days": 28, // Було 24, додаємо 4 дні
"left_days": 18 // Було 14, додаємо 4 дні
}
Щоб вручну виправити кількість використаних за період днів (наприклад, синхронізувати з зовнішньою системою обліку), передайте used — абсолютне значення:
PATCH /api/public/v1/employee/{employee_id}/absences/{absence_id}
{
"used": 5 // Встановити «використано за період» рівним 5 (не змінює left_days)
}
Сценарій 4: Новий період (новий рік)¶
На початку нового періоду (робочого або календарного року) потрібно створити новий запис:
POST /api/public/v1/employee/{employee_id}/absences
{
"absence_type": "annual",
"date_start": "2025-01-15", // Початок нового робочого року
"allowed_days": 24,
"left_days": 24
}
Старий запис залишається в системі для історії.
Сценарій 5: Регулярна синхронізація з обліковою системою¶
Облікова система (УВП) щоночі передає повний стан залишків по всіх співробітниках і типах відпусток:
для кожної пачки по 200 записів:
POST /api/public/v1/absences/bulk → результат по кожному рядку
зібрати рядки зі status = "error" за row_ref
виправити дані на своєму боці й надіслати лише невдалі рядки повторно
Рядки, стан яких не змінився з минулої ночі, повертаються як skipped і не породжують записів в історії.
Важливі моменти¶
-
Автоматичне списання - при створенні задачі "Відпустка" система автоматично зменшує
left_daysу відповідному записі залишків. Ручне оновлення через PATCH не потрібне. -
usedтаleft_daysнезалежні - ручне коригуванняused(через POST/PATCH) записує запис у історію використання, але не змінюєleft_days. І навпаки — змінаleft_daysне змінюєused. Автоматичне списання за задачею "Відпустка" оновлює обидва (додає рядок уusagesі зменшуєleft_days). -
Дробові дні - кількість днів може бути дробовою (наприклад, 24.5), що дозволяє враховувати неповні дні відпустки.
-
Декілька періодів - один співробітник може мати декілька записів одного типу для різних періодів (наприклад, щорічна відпустка за 2024 та 2025 роки).
-
Валідація при створенні відпустки - система перевіряє наявність достатніх залишків перед створенням задачі "Відпустка". Якщо
left_daysменше за запитувану кількість днів, задача не буде створена. -
Користувацькі типи - для типу
customобов'язково вказуватиtask_template_identifier, який відповідає ID шаблону відпустки, створеного через веб-інтерфейс. -
Історія використання - старі записи не видаляються, що дозволяє переглядати історію використання відпусток за минулі періоди.
-
Зв'язок з шаблонами відпусток - тип залишків (
absence_type) повинен відповідати типу шаблону відпустки. Див. документацію про відпустки для повної таблиці відповідностей.
Помилки та їх вирішення¶
| Помилка | Причина | Рішення |
|---|---|---|
task_template_identifier is required when absence_type is 'custom' |
Не вказано шаблон для типу custom |
Додати поле task_template_identifier в запит |