Створення нового словника
Для переходу на новий класифікатор ЄДРАТО потрібно створити новий словник.
Словник додаємо в наш сервіс класифікаторів: dictionaries and classifiers.
За посиланням https://procedure-sandbox.prozorro.sale/api/classifiers потрібно додати новий словник ЄДРАТО:
"edrato": {
"legalNameUa": "Єдиний державний реєстр адміністративно-територіальних одиниць",
"legalNameEn": "Unified State Register of Administrative-Territorial Units"
}
Словник ЄДРАТО повинен бути доступний за посиланням: https://procedure-sandbox.prozorro.sale/api/classifiers/edrato
Опис словника: словник повинен мати пласку структуру. В словник додаємо наступні рівні класифікатора:
UA.ATU.U.00000 - Україна
UA.ATU.K - Місто із спеціальним статусом
UA.ATU.O - Область
UA.ATU.P - Район в області
UA.ATU.H - Тер громада
UA.ATU.M - Населений пункт
- UA.ATU.B - Район в місті
Нижчі рівні (адреси) додавати не потрібно. Код кожного об'єкта є унікальним. По коду неможливо визначити, яке місто належить якій області.
Наповнення словника
Для наповнення словника ЄДРАТО можна використати ендпоінт EdraHistory.
Для того, щоб отримати повний перелік всіх актуальних обʼєктів в Україні, виконуємо наступний запит:
curl --location 'https://cabinet.address.gov.ua/api-user/edraHistory/json?id=00000&depth=atu&reload=1' \ --header 'Authorization: Bearer TOKEN' \ --header 'Cookie: assets_hash=232a743b0218d7d6'
Перший запит, який буде виконано, перерахує всі обʼєкти, які є (їх близько 32 тис записів). Після чого буде сформовано .zip архів. Для того, щоб скачати цей архів, потрібно повторно виконати той самий запит, лише додавши output:
curl --location "https://cabinet.address.gov.ua/api-user/edraHistory/json?id=00000&depth=atu&reload=1" \ --header "Authorization: Bearer TOKEN" \ --header "Cookie: assets_hash=232a743b0218d7d6" \ --output "edra_history_00000.zip"
Даний запит скачає .zip архів, в якому міститься набір .json файлів. Кожен файл є обʼєктом ЄДРАТО, актуальним на даний момент. Ось приклад структури такого обʼєкту:
[
{
"atu_id": "3321463584180929964",
"external_id": "94485",
"name": "Куничі",
"type": 504,
"level": 5,
"namespace": "UA.ATU.M",
"parent": "3321463571136645029",
"version": "001",
"date_from": "2020-11-26 00:00:00",
"date_to": null,
"katottg": "UA26040070090094485",
"koatuu": "2621285702",
"comment": null,
"status": 3,
"occupation_status": null,
"cdate": "2024-03-11 17:02:14.466188",
"editor_date": null,
"adm_center": null,
"statut_communities": null,
"is_last_version": true,
"nais_atu_id": "925379",
"atu_id_from": null,
"atu_id_to": null,
"change_type": null,
"nais_ach_type": null
}
]
Для наповнення словника нас цікавлять наступні параметри: "namespace": "UA.ATU.M", "external_id": "94485", "name": "Куничі".
Для того, щоб переконатись в тому, що ми не додаємо в словник неактуальні версії, можна додати валідацію на "is_last_version": true, тобто додавати тільки останні версії НП в словник.
Таким чином, словник необхідно наповнити тільки актуальними записами.
В словник потрібно записати значення в наступному форматі:
"namespace.external_id": "name"
Приклад архіву, який було отримано станом на 20.05.2026:
В словник також обовʼязково потрібно додати Україну: "UA.ATU.U.00000": "Україна"
Автоматичне оновлення словника
Для автоматичного оновлення словника потрібно використовувати ендпоінт EdraHistory.
Логіка роботи ендпоінта:
URL: [host]/api-user/edraHistory/[type]?id=[id]&from=[from]&to=[to]&nocache=[nocache]&depth=[depth]
Метод: GET
Вхідні параметри
type: Формат даних всередині архіву (json або xml).
id: Ідентифікатор батьківського об'єкта АТУ (наприклад, 00000 — вся Україна).
depth: Глибина вивантаження. Для нашого словника використовуємо atu (тільки адміністративні одиниці: області, райони, громади, населені пункти).
from / to: Часовий проміжок для історії змін.
reload: Прапор примусового оновлення. Якщо 1 — файл генерується заново негайно.
Алгоритм обробки запиту
Ініціалізація та перевірка кешу:
Система перевіряє, чи існує вже згенерований архів для вказаної комбінації параметрів (
id,depth,from,to).Якщо файл існує і він створений менше 7 днів тому, система віддає його користувачу.
Якщо файл відсутній, старіший за 7 днів або передано параметр
reload=1, запускається процес генерації.
Формування результату:
Система збирає об'єкти АТУ згідно з заданою глибиною (
depth=atu).Дані пакуються в архів.
Через
eventStreamкористувач отримує статус процесу (наприклад,Finish:finish export).
Приклад коду, який згенерує файл з усіма об'єктами в Україні, зміненими з 2022 рік по 2026 рік:
curl --location 'https://cabinet.address.gov.ua/api-user/edraHistory/json?from=2026-01-01&to=2026-05-20&id=00000&depth=atu&reload=1' \ --header 'Authorization: Bearer TOKEN' \ --header 'Cookie: assets_hash=232a743b0218d7d6'
В цьому випадку команда теж спочатку перебирає повністю всі обʼєкти, наявні в Україні, а далі видає .zip архів, в якому буде збережено лише ті обʼєкти, які було змінено. Зміну обʼєкта зафіксовано в полі cdate: Дата створення запису. Тобто навіть якщо запис створено сьогодні, але його актуальність (поле date_from проставлена заднім числом (наприклад, запис було оновлено в ЄДРАТО сьогодні, але запис згідно нормативних актів є актуальним з 2015 року), ми не пропустимо такий обʼєкт і оновимо його тоді, коли його буде оновлено в ЄДРАТО).
Після того, як буде сформовано архів, його необхідно так само скачати, повторно виконавши команду з вказанням --output:
curl --location "https://cabinet.address.gov.ua/api-user/edraHistory/json?from=2022-01-01&to=2026-05-20&id=00000&depth=atu&reload=1" \ --header "Authorization: Bearer TOKEN" \ --header "Cookie: assets_hash=232a743b0218d7d6" \ --output "edra_history_00000_period.zip"
Ось приклад архіву, який було викачано за цим запитом:
Для того, щоб оновити записи в словнику, необхідно розпарсити кожен JSON, і в залежності від сценарію виконати одну з наступних дій:
- Створення нового запису в словнику:
- Якщо немає повного збігу по комбінації "namespace.external_id" - в такому випадку необхідно створити новий запис, з новим "namespace.external_id" та вказати "name".
Оновлення існуючого запису (історичність та перейменування).
Якщо об'єкт із таким atu_id вже існує в нашому словнику, ми перевіряємо його актуальність за полями історичності (is_last_version та status):
Якщо is_last_version: true - це поточна актуальна версія об'єкта. Ми порівнюємо назву. Якщо назва змінилася (відбулося перейменування), ми оновлюємо значення поля name в нашому словнику на нове актуальне.
Якщо is_last_version: false - цей запис став історичним через часові рамки в полях date_from / date_to. Оскільки наш словник відображає лише актуальний адміністративно-територіальний устрій, ми ігноруємо такі записи або видаляємо їх у себе як неактивні, щоб користувачі не могли обрати застарілу назву.
Механізм синхронізації:
- Налаштувати CronJob, яка періодично звертається до першоджерела (ЄДРА), скачує актуальний файл (JSON/ZIP).
Оновлення локального файлу словника має відбуватися через автоматичне створення Merge Request для подальшого апруву.
- Періодичність звернення до АРІ ЄДРАТО для створення Merge Request потрібно синхронізувати з релізами, тобто кожні два тижні. У випадку, якщо нових записів не знайдено, створювати merge request не потрібно.
Якщо в результаті виконання команди пошуку оновлень в словнику ми отримуємо "No changes found", не потрібно виконувати ніяких дій або створювати MR. Приклад запиту, який не знайде жодної зміни та не сформує .zip архів:
curl --location 'https://cabinet.address.gov.ua/api-user/edraHistory/json?from=2026-05-20&to=2026-05-20&id=00000&depth=atu&reload=1' \ --header 'Authorization: Bearer TOKEN' \ --header 'Cookie: assets_hash=232a743b0218d7d6'
Зміна об'єктів
Як перший єтап необхідно внести зміни в модельку base.AddressIdentifier
В scheme додаємо нове значення edrato:
| scheme* | string x-legalNameUa: Класифікатор об’єктів адміністративно-територіального устрою України x-legalNameEn: Type of objects of administrative-territorial organization of Ukraine обирається зі словника https://procedure-sandbox.prozorro.sale/api/classifiers/koatuu [ koatuu, edrato ] |
Якщо обрано значення edrato, в такому випадку для id необхідно додати наступну логіку:
- Приймати значення лише зі словника https://procedure-sandbox.prozorro.sale/api/classifiers/edrato
- Додати RegExp валідацію на id, наприклад
^UA\.ATU\.[UKOPHMB]\.\d{5}$
| id* | string x-dictionaries: List [ "koatuu, edrato" ] x-legalNameUa: Код адміністративно-територіальних об’єктів України x-legalNameEn: ID of objects of administrative-territorial organization of Ukraine |
Після того, як майданчики повністю перейдуть на новий класифікатор ЄДРАТО, буде реалізовано задачу на заборону обирати КОАТУУ як класифікатор (наприклад, через x-exclude)
В усіх об'єктах у нас використовуються виключно базова моделька base.AddressIdentifier.
Дана модель присутня в наступних сервісах:
- Procedure
- Jobber
- Registry
Міграції
Під час міграції кожного конкретного об’єкта в базі даних необхідно забезпечити зміну структури ідентифікатора адреси:
Значення поля
schemeобов’язково змінюється зkoatuuнаedrato.Значення поля
idповністю замінюється на новий код.Старе значення КОАТУУ не зберігається в основному полі; відбувається повна заміна даних.
Для кожного запису система повинна послідовно виконати наступні кроки до моменту отримання валідного коду:
Прямий мапінг
Використовується таблиця відповідності кодів КОАТУУ → ЄДРАТО (таблицю буде зроблено перед початком задачі мапінгу)
Якщо прямої відповідності немає
Запускається, якщо прямий мапінг не дав результату.
Логіка присвоєння значення відбувається за наступною логікою:
Зробити пошук по ЄДРАТО за комбінацією двох параметрів:
- Назва населеного пункту (
locality). Назва/код регіону (
region).
Якщо населений пункт не знайдено за пошуком
Записується ідентифікатор рівня області (O), до якої належав об’єкт (region)
Вимоги до майданчиків
Логіка каскадного вибору
Майданчик має реалізувати вибір адреси, де словник закінчується на населеному пункті:
Рівні ієрархії зі словника:
- Україна
- Область
Район
Територіальна громада (ТГ)
Населений пункт (місто, селище, село)
- Для окремих міст - райони
м. Київ та м. Севастополь знаходяться на рівні областей!
Пошук та вибір працюють лише для цих 4-х рівнів. Майданчик не повинен намагатися шукати вулиці через API ЄДРАТО, оскільки ЦБД їх не віддаватиме.
У полі addressId.id майданчик зобов'язаний передати ID населеного пунку зі словника. ЦБД не буде приймати інших значень.
Якщо користувач ввів навіть населений пункт вручну, майданчик має передати id Територіальної громади (як останнього валідного рівня), а все інше — текстом.
Ввод адреси (вулиця та будинок)
Оскільки вулиці та будинки не є частиною словника, користувачеві необхідно надати можливість вводити дані значення в ручному режимі (так само, як це реалізовано зараз)
Візуалізація для користувача
Майданчик має збирати повну назву адреси для прев'ю. Наприклад: Київська обл., Вишгородський р-н, Пірнівська ТГ, с. Пірнове, вул. Центральна, 12.
Перші 4 елементи беруться з об'єкта словника, останні два - з того, що юзер ввів руками.
Використання словника ЄДРАТО
Для пошуку об'єктів можна використовувати безпосередньо АРІ, які надає ЄДРАТО, але слід зауважити, що ЦБД буде приймати виключно ті id, які присутні в словнику. Словник буде оновлюватись разом з релізами на продакшен у випадку наявності змін за період попередньо оновлення.
Корисні посилання:
ТЗ: https://eef.org.ua/wp-content/uploads/2023/06/TV_YEDRA_YEDRATO_1_0-1.pdf
АРІ ЄДРАТО: https://cdn.softpro.ua/apidocs/edra.html#tag/restApi
