Перейти до змісту

Залишки відпусток (Employee Absences)

Залишки відпусток - це механізм управління балансом доступних і використаних днів відпустки для співробітників. Кожен запис про залишки прив'язаний до конкретного типу відпустки (щорічна, навчальна, тощо) та періоду (робочий рік або календарний рік).

Різниця між Залишками (Absences) та Задачами "Відпустка" (Vacation Tasks)

Важливо розуміти різницю між цими двома концепціями:

Аспект Залишки (Absences) Задача "Відпустка" (Vacation Task)
Призначення Управління балансом доступних днів відпустки Процес оформлення конкретної відпустки з підписанням документів
Що зберігає allowed_days (надано), left_days (залишок), used (використано — сума списань; обчислюється, але можна задати вручну) Деталі конкретної відпустки: дати, підписанти, документи
Період Робочий або календарний рік Конкретні дати початку і кінця відпустки
API операції POST, GET, PATCH для управління балансом; POST /absences/bulk для пакетної синхронізації POST для створення задачі, GET для відстеження статусу підписання
Взаємодія Автоматично оновлюється при створенні задачі "Відпустка" Використовує залишки для валідації і списання днів

Приклад взаємодії

  1. Створюється запис про залишки: співробітнику надається 24 дні щорічної відпустки на робочий рік

    {
      "absence_type": "annual",
      "allowed_days": 24,
      "left_days": 24,
      "date_start": "2024-01-15"
    }
    

  2. Створюється задача "Відпустка": співробітник йде у відпустку на 10 днів

  3. Система автоматично зменшує left_days з 24 до 14
  4. used збільшується з 0 до 10 (додається рядок списання в usages)

  5. Оновлення залишків: 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: Створення залишків при прийомі на роботу

При прийомі співробітника необхідно створити записи про залишки для різних типів відпусток:

  1. Щорічна основна відпустка (24 дні):

    POST /api/public/v1/employee/{employee_id}/absences
    {
      "absence_type": "annual",
      "date_start": "2024-01-15",  // Дата прийому на роботу
      "allowed_days": 24,
      "left_days": 24
    }
    

  2. Додаткова відпустка для учасника бойових дій (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: Оформлення відпустки через задачу

  1. Перевірити залишки перед створенням задачі:

    GET /api/public/v1/employee/{employee_id}/absences
    
    Переконатися, що left_days >= кількість днів відпустки

  2. Створити задачу "Відпустка":

    POST /api/public/v1/vacation
    
    Система автоматично зменшить left_days та збільшить обчислюване used

  3. Перевірити оновлені залишки:

    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 і не породжують записів в історії.

Важливі моменти

  1. Автоматичне списання - при створенні задачі "Відпустка" система автоматично зменшує left_days у відповідному записі залишків. Ручне оновлення через PATCH не потрібне.

  2. used та left_days незалежні - ручне коригування used (через POST/PATCH) записує запис у історію використання, але не змінює left_days. І навпаки — зміна left_days не змінює used. Автоматичне списання за задачею "Відпустка" оновлює обидва (додає рядок у usages і зменшує left_days).

  3. Дробові дні - кількість днів може бути дробовою (наприклад, 24.5), що дозволяє враховувати неповні дні відпустки.

  4. Декілька періодів - один співробітник може мати декілька записів одного типу для різних періодів (наприклад, щорічна відпустка за 2024 та 2025 роки).

  5. Валідація при створенні відпустки - система перевіряє наявність достатніх залишків перед створенням задачі "Відпустка". Якщо left_days менше за запитувану кількість днів, задача не буде створена.

  6. Користувацькі типи - для типу custom обов'язково вказувати task_template_identifier, який відповідає ID шаблону відпустки, створеного через веб-інтерфейс.

  7. Історія використання - старі записи не видаляються, що дозволяє переглядати історію використання відпусток за минулі періоди.

  8. Зв'язок з шаблонами відпусток - тип залишків (absence_type) повинен відповідати типу шаблону відпустки. Див. документацію про відпустки для повної таблиці відповідностей.

Помилки та їх вирішення

Помилка Причина Рішення
task_template_identifier is required when absence_type is 'custom' Не вказано шаблон для типу custom Додати поле task_template_identifier в запит