Інтеграція з Prom, Rozetka, Hotline
Головна ідея: розробити Python-сервіс, який інтегрує K2 ERP / CRM / інтернет-магазин із маркетплейсами та прайс-агрегаторами Prom.ua, Rozetka Marketplace і Hotline для синхронізації товарів, цін, залишків, замовлень, статусів, фідів, ставок, помилок і аналітики.
Критично важливо: інтеграція не повинна створювати дублікати товарів, замовлень або фідів. Кожен товар, замовлення, зміна ціни, залишку, статусу, фід, XML-файл, API-запит і помилка повинні мати внутрішній ID, external_id, журнал подій і захист від повторної обробки.
Важливо: Prom, Rozetka і Hotline мають різну логіку інтеграції. Prom та Rozetka мають API для продавців, а Hotline у базовому сценарії працює через товарні фіди, вимоги до прайсів і окремий API керування ставками.
Технічний стек: Python 3.11+, FastAPI, PostgreSQL, SQLAlchemy, Alembic, httpx, Pydantic, Celery/RQ/APScheduler, Redis, Docker, XML/YML generator.
Управлінський результат: менеджер і керівник повинні бачити, які товари синхронізовані, які мають помилки, які замовлення отримані, які ціни та залишки передані, які товари не пройшли модерацію, які фіди сформовані, які ставки Hotline активні та які канали продажів дають результат.
1. Мета
Метою задачі є створення Python-сервісу для інтеграції з трьома зовнішніми каналами продажів і просування:
- Prom.ua;
- Rozetka Marketplace;
- Hotline.
Сервіс повинен забезпечити:
- централізовану картку інтеграції по кожному каналу;
- синхронізацію товарів;
- синхронізацію цін;
- синхронізацію залишків;
- отримання замовлень;
- оновлення статусів замовлень;
- передачу номерів ТТН / ЕН;
- генерацію товарних фідів;
- валідацію товарів перед передачею;
- контроль помилок API;
- контроль помилок фідів;
- журнал усіх запитів і відповідей;
- dashboard по маркетплейсах;
- захист від дублювання товарів і замовлень.
2. Область застосування
Інтеграція призначена для:
- інтернет-магазинів;
- ERP;
- CRM;
- WMS;
- торгових компаній;
- дистриб'юторів;
- компаній, які продають одночасно на декількох маркетплейсах;
- компаній, які ведуть товари, ціни, залишки і замовлення в K2 ERP.
3. Канали інтеграції
| Канал | Основний тип інтеграції | Основні сценарії | Коментар |
|---|---|---|---|
| Prom.ua | Public API | Товари, замовлення, статуси, ціни, залишки. | API-токени керуються в кабінеті компанії. |
| Rozetka | Seller API + товарний фід | Замовлення через API, товари/ціни/залишки через API або XML-фід залежно від сценарію. | Потрібно звірити актуальний API продавця. |
| Hotline | XML/YML/CSV/XLS/TXT фіди + API ставок | Передача товарів через фід, контроль акцій, керування ставками. | Це більше price-comparison / рекламний канал, ніж класичний marketplace. |
Prom.ua офіційно має публічне API для віддаленого керування даними в кабінеті компанії, а API-токени можна керувати в кабінеті компанії у розділі налаштувань. :contentReference[oaicite:1]{index=1}
Rozetka Marketplace має API для інтеграції продавців: офіційна довідка описує API як набір інструментів для інтеграції системи продавця з ROZETKA Маркетплейс, а API-документація вказує можливість роботи із замовленнями, товарами, листуванням і контролем процесів. :contentReference[oaicite:2]{index=2}
Hotline офіційно описує вимоги до товарних фідів, зокрема Hotline XML і YML, а також має API для керування ставками по пропозиціях магазину. :contentReference[oaicite:3]{index=3}
4. Передумови
Для реалізації задачі необхідно отримати:
- доступ до кабінету продавця Prom.ua;
- API token Prom.ua;
- доступ до кабінету продавця Rozetka;
- API credentials Rozetka;
- актуальну API-документацію Rozetka Seller API;
- вимоги Rozetka до товарного фіду, якщо використовується фід;
- доступ до кабінету Hotline;
- вимоги Hotline до XML/YML/CSV-фідів;
- auth token Hotline для API ставок, якщо використовується;
- список категорій товарів;
- правила мапінгу категорій K2 ERP → Prom / Rozetka / Hotline;
- правила мапінгу характеристик;
- правила округлення цін;
- правила синхронізації залишків;
- правила резервування товару після отримання замовлення;
- правила оновлення статусів замовлень;
- правила передачі ТТН.
Критично важливо: доступи до Prom, Rozetka і Hotline потрібно зберігати тільки в secret storage або в зашифрованому вигляді. Заборонено зберігати API token, логіни, паролі або auth token у коді, Git, frontend-змінних або відкритих логах.
5. Основні бізнес-сценарії
5.1. Передача товарів
K2 ERP є головним джерелом товарів.
Python-сервіс повинен:
- отримувати товари з K2 ERP;
- перевіряти обов'язкові поля;
- мапити категорії;
- мапити характеристики;
- формувати опис;
- перевіряти фото;
- формувати ціну;
- формувати залишок;
- передавати товар у Prom / Rozetka через API або фід;
- включати товар у фід Hotline;
- зберігати зовнішні ID товарів.
5.2. Синхронізація цін
K2 ERP передає нові ціни.
Python-сервіс повинен:
- визначити активні канали для товару;
- застосувати правила націнки;
- застосувати промо-ціну, якщо є;
- застосувати мінімальну ціну;
- сформувати зміну ціни;
- передати зміну в Prom;
- передати зміну в Rozetka;
- оновити XML/YML-фід Hotline;
- зберегти історію зміни ціни.
5.3. Синхронізація залишків
K2 ERP передає залишки по складах.
Python-сервіс повинен:
- отримати актуальний залишок;
- врахувати резерви;
- врахувати мінімальний страховий залишок;
- визначити доступну кількість для продажу;
- передати залишок у канали;
- приховати товар, якщо залишок 0;
- відновити товар, якщо залишок з'явився.
5.4. Отримання замовлень
Prom і Rozetka можуть бути джерелами замовлень.
Python-сервіс повинен:
- регулярно отримувати нові замовлення;
- перевіряти, чи замовлення вже імпортовано;
- створювати замовлення в K2 ERP;
- створювати покупця або прив'язувати до існуючого;
- створювати резерв товарів;
- зберігати джерело замовлення;
- зберігати external_order_id;
- передавати статус обробки назад у маркетплейс.
5.5. Оновлення статусів замовлень
K2 ERP є джерелом операційних статусів.
Python-сервіс повинен:
- отримувати статус із K2 ERP;
- мапити статус у статус маркетплейса;
- передавати статус у Prom / Rozetka;
- передавати номер ТТН / ЕН;
- зберігати історію статусів;
- логувати помилки оновлення.
5.6. Hotline як фід і канал просування
Hotline у MVP розглядається як канал передачі товарних пропозицій через фід.
Python-сервіс повинен:
- формувати XML/YML-фід;
- включати тільки активні товари;
- передавати актуальні ціни;
- передавати актуальні залишки;
- передавати URL товару;
- передавати фото;
- передавати характеристики;
- формувати фід акцій, якщо потрібно;
- керувати ставками через API ставок, якщо підключено.
6. Основні сутності
| Сутність | Опис |
|---|---|
| Marketplace Integration | Налаштування інтеграції з Prom, Rozetka або Hotline. |
| Product | Товар у K2 ERP. |
| Marketplace Product | Товар у конкретному каналі. |
| Category Mapping | Мапінг категорій K2 ERP до категорій каналу. |
| Attribute Mapping | Мапінг характеристик. |
| Price Rule | Правило формування ціни. |
| Stock Rule | Правило формування доступного залишку. |
| Order | Замовлення з маркетплейса. |
| Order Status | Статус замовлення. |
| Feed | XML/YML/CSV-фід для Hotline або Rozetka. |
| Bid | Ставка Hotline по товарній пропозиції. |
| API Event | Подія інтеграції. |
7. User Story
7.1. Товари
Як менеджер товарного каталогу, я хочу один раз вести товар у K2 ERP, щоб він автоматично передавався в Prom, Rozetka і Hotline.
7.2. Ціни
Як комерційний менеджер, я хочу змінити ціну в K2 ERP, щоб вона автоматично оновилась у всіх підключених каналах.
7.3. Залишки
Як склад, я хочу, щоб залишки з K2 ERP автоматично оновлювались на маркетплейсах, щоб не продавати товар, якого немає.
7.4. Замовлення
Як менеджер продажів, я хочу отримувати замовлення з Prom і Rozetka в K2 ERP, щоб обробляти їх в єдиній системі.
7.5. Hotline
Як маркетолог, я хочу автоматично формувати фід для Hotline, щоб товари були актуальні за ціною, наявністю і характеристиками.
7.6. Dashboard керівника
Як керівник, я хочу бачити продажі, помилки, статуси товарів і ефективність каналів, щоб контролювати якість інтеграції.
8. Функціональні вимоги
8.1. Налаштування інтеграції
| Поле | Тип | Обов'язковість | Опис |
|---|---|---|---|
| integration_name | string | Так | Назва інтеграції. |
| marketplace | enum | Так | PROM, ROZETKA, HOTLINE. |
| base_url | string | Так | URL API або endpoint фіду. |
| api_token | secret | Ні | Token для API. |
| login | secret | Ні | Логін, якщо потрібен. |
| password | secret | Ні | Пароль, якщо потрібен. |
| feed_public_url | string | Ні | URL фіду для Hotline/Rozetka. |
| sync_products_enabled | boolean | Так | Чи синхронізувати товари. |
| sync_prices_enabled | boolean | Так | Чи синхронізувати ціни. |
| sync_stocks_enabled | boolean | Так | Чи синхронізувати залишки. |
| sync_orders_enabled | boolean | Так | Чи імпортувати замовлення. |
| is_active | boolean | Так | Ознака активності. |
8.2. Товар для синхронізації
| Поле | Тип | Опис |
|---|---|---|
| sku | string | Артикул товару. |
| external_product_id | string | ID товару в маркетплейсі. |
| name | string | Назва. |
| description | text | Опис. |
| category_id | string | Категорія K2 ERP. |
| marketplace_category_id | string | Категорія маркетплейса. |
| price | numeric | Базова ціна. |
| old_price | numeric | Стара ціна, якщо є. |
| stock_quantity | numeric | Залишок. |
| brand | string | Бренд. |
| images | array | Фото. |
| attributes | object | Характеристики. |
| barcode | string | Штрихкод. |
| warranty | string | Гарантія. |
| is_active | boolean | Активність. |
8.3. Замовлення з маркетплейса
| Поле | Тип | Опис |
|---|---|---|
| marketplace | enum | PROM або ROZETKA. |
| external_order_id | string | ID замовлення в маркетплейсі. |
| order_number | string | Номер замовлення. |
| order_date | datetime | Дата замовлення. |
| customer_name | string | ПІБ покупця. |
| customer_phone | string | Телефон покупця. |
| customer_email | string | Email. |
| delivery_service | string | Нова пошта, Укрпошта тощо. |
| delivery_address | text | Адреса або відділення. |
| payment_type | string | Оплата. |
| total_amount | numeric | Сума замовлення. |
| status | string | Статус. |
| items | array | Товари замовлення. |
9. Статуси товарів
| Статус | Код | Опис | Колір |
|---|---|---|---|
| Чернетка | DRAFT | Товар створений у K2 ERP, але ще не готовий до передачі. | Сірий |
| Очікує валідації | PENDING_VALIDATION | Товар очікує перевірки. | Жовтий |
| Потребує виправлення | NEEDS_CORRECTION | Не вистачає фото, категорії, характеристик або опису. | Помаранчевий |
| Готовий до синхронізації | READY_TO_SYNC | Товар готовий до передачі. | Блакитний |
| Синхронізується | SYNCING | Виконується API-запит або генерація фіду. | Блакитний |
| Передано | SYNCED | Товар передано в канал. | Зелений |
| На модерації | MODERATION | Товар очікує перевірки маркетплейса. | Жовтий |
| Опубліковано | PUBLISHED | Товар доступний покупцям. | Зелений |
| Відхилено | REJECTED | Товар відхилено каналом. | Червоний |
| Помилка синхронізації | SYNC_ERROR | API або фід повернув помилку. | Червоний |
| Приховано | HIDDEN | Товар приховано через залишок, правило або вручну. | Сірий |
10. Статуси замовлень
| Статус K2 ERP | Код | Опис | Колір |
|---|---|---|---|
| Нове | NEW | Замовлення отримано з маркетплейса. | Жовтий |
| Імпортується | IMPORTING | Створюється в K2 ERP. | Блакитний |
| Імпортовано | IMPORTED | Замовлення створено в K2 ERP. | Зелений |
| Потребує перевірки | NEEDS_REVIEW | Не знайдено товар, клієнта або адресу. | Помаранчевий |
| Підтверджено | CONFIRMED | Замовлення прийнято в роботу. | Зелений |
| Комплектується | PROCESSING | Замовлення збирається. | Блакитний |
| Відправлено | SHIPPED | Передано ТТН / ЕН. | Блакитний |
| Виконано | COMPLETED | Замовлення завершено. | Зелений |
| Скасовано | CANCELLED | Замовлення скасовано. | Сірий |
| Помилка | ERROR | Помилка імпорту або оновлення. | Червоний |
11. Єдина логіка кольорів
| Колір | HTML | Значення | Де використовується |
|---|---|---|---|
| Зелений | #c8e6c9 | Успішно: синхронізовано, опубліковано, імпортовано, виконано. | Dashboard, список товарів, список замовлень. |
| Блакитний | #bbdefb | Операція виконується або дані в процесі передачі. | Черги, API-запити, статуси обробки. |
| Жовтий | #fff9c4 | Очікування дії або модерації. | Модерація, нові замовлення, очікування. |
| Помаранчевий | #ffcc80 | Потрібна дія менеджера. | Помилки валідації, невідомі товари, retry. |
| Червоний | #ef9a9a | Критична помилка або відхилення. | API error, rejected, sync error. |
| Фіолетовий | #f3e5f5 | Спеціальний або ручний сценарій. | Ручна перевірка, нестандартний фід. |
| Сірий | #eeeeee | Чернетка, приховано, скасовано або архів. | Архів, hidden, cancelled. |
12. Архітектура рішення
12.1. Загальна схема
K2 ERP / CRM / WMS
|
| 1. Товари, ціни, залишки, статуси
v
Python Marketplace Integration Service
|
| 2. Валідація, мапінг, черги, дедублікація
v
Marketplace Adapters
|
| 3. Prom API / Rozetka API / Hotline Feed + Bids API
v
Prom.ua / Rozetka / Hotline
|
| 4. Замовлення, статуси, модерація, фіди
v
Python Sync Workers
|
| 5. Оновлення K2 ERP
v
K2 ERP / Dashboard / Менеджери
12.2. Основні компоненти Python-сервісу
| Компонент | Опис |
|---|---|
| API Layer | REST API для команд із K2 ERP. |
| Product Validator | Перевіряє товари перед передачею. |
| Category Mapper | Мапить категорії K2 ERP до категорій каналів. |
| Attribute Mapper | Мапить характеристики. |
| Price Engine | Формує ціну по правилах. |
| Stock Engine | Формує доступний залишок. |
| Prom Adapter | Робота з API Prom.ua. |
| Rozetka Adapter | Робота з API Rozetka. |
| Hotline Feed Generator | Генерація XML/YML/CSV-фідів Hotline. |
| Hotline Bid Client | API керування ставками Hotline. |
| Order Import Worker | Імпорт замовлень. |
| Status Export Worker | Передача статусів і ТТН. |
| Feed Publisher | Публікація фідів за публічним URL. |
| Audit Logger | Запити, відповіді, помилки, статуси. |
| Dashboard API | Статистика по товарах, замовленнях, помилках і каналах. |
13. Marketplace Client Interface
13.1. Загальний інтерфейс
from abc import ABC, abstractmethod
class MarketplaceClient(ABC):
@abstractmethod
async def check_connection(self) -> dict:
pass
@abstractmethod
async def sync_product(self, product: dict) -> dict:
pass
@abstractmethod
async def update_price(self, external_product_id: str, price: dict) -> dict:
pass
@abstractmethod
async def update_stock(self, external_product_id: str, stock: dict) -> dict:
pass
@abstractmethod
async def import_orders(self, filters: dict) -> list[dict]:
pass
@abstractmethod
async def update_order_status(self, external_order_id: str, status: dict) -> dict:
pass
13.2. Prom Client
class PromClient(MarketplaceClient):
def __init__(self, base_url: str, api_token: str):
self.base_url = base_url
self.api_token = api_token
async def check_connection(self) -> dict:
pass
async def sync_product(self, product: dict) -> dict:
pass
async def update_price(self, external_product_id: str, price: dict) -> dict:
pass
async def update_stock(self, external_product_id: str, stock: dict) -> dict:
pass
async def import_orders(self, filters: dict) -> list[dict]:
pass
async def update_order_status(self, external_order_id: str, status: dict) -> dict:
pass
13.3. Rozetka Client
class RozetkaClient(MarketplaceClient):
def __init__(self, base_url: str, username: str | None = None, password: str | None = None, token: str | None = None):
self.base_url = base_url
self.username = username
self.password = password
self.token = token
async def authenticate(self) -> dict:
pass
async def check_connection(self) -> dict:
pass
async def sync_product(self, product: dict) -> dict:
pass
async def update_price(self, external_product_id: str, price: dict) -> dict:
pass
async def update_stock(self, external_product_id: str, stock: dict) -> dict:
pass
async def import_orders(self, filters: dict) -> list[dict]:
pass
async def update_order_status(self, external_order_id: str, status: dict) -> dict:
pass
13.4. Hotline Client
class HotlineClient:
def __init__(self, feed_storage_url: str, bid_api_token: str | None = None):
self.feed_storage_url = feed_storage_url
self.bid_api_token = bid_api_token
async def generate_product_feed(self, products: list[dict], format: str = "xml") -> bytes:
pass
async def publish_feed(self, feed: bytes, filename: str) -> str:
pass
async def generate_action_feed(self, actions: list[dict]) -> bytes:
pass
async def get_bids(self) -> list[dict]:
pass
async def update_bid(self, offer_id: str, bid_value: int) -> dict:
pass
14. API Python-сервісу
14.1. Створення інтеграції
POST /api/v1/marketplaces/integrations
14.2. Перевірка підключення
POST /api/v1/marketplaces/integrations/{integration_id}/check-connection
14.3. Синхронізація товару
POST /api/v1/marketplaces/products/{product_id}/sync
14.4. Масова синхронізація товарів
POST /api/v1/marketplaces/products/sync-batch
14.5. Оновлення цін
POST /api/v1/marketplaces/prices/sync
14.6. Оновлення залишків
POST /api/v1/marketplaces/stocks/sync
14.7. Імпорт замовлень
POST /api/v1/marketplaces/orders/import
14.8. Оновлення статусу замовлення
POST /api/v1/marketplaces/orders/{order_id}/update-status
14.9. Генерація фіду Hotline
POST /api/v1/marketplaces/hotline/feed/generate
14.10. Публікація фіду Hotline
POST /api/v1/marketplaces/hotline/feed/publish
14.11. Оновлення ставок Hotline
POST /api/v1/marketplaces/hotline/bids/update
14.12. Dashboard
GET /api/v1/marketplaces/dashboard?date_from=2026-05-01&date_to=2026-05-31
15. Приклад запиту на синхронізацію товару
{
"product_id": "K2-PRODUCT-000123",
"sku": "SKU-001",
"name": "Смартфон Example X 128GB",
"description": "Опис товару без HTML-помилок та заборонених символів.",
"brand": "Example",
"category_id": "phones",
"marketplace_categories": {
"prom": "prom-category-ref",
"rozetka": "rozetka-category-id",
"hotline": "hotline-category-code"
},
"price": 12999.00,
"old_price": 13999.00,
"stock_quantity": 12,
"barcode": "4820000000000",
"images": [
"https://example.com/images/sku-001-1.jpg",
"https://example.com/images/sku-001-2.jpg"
],
"attributes": {
"memory": "128GB",
"color": "Black",
"warranty": "12 місяців"
},
"channels": ["PROM", "ROZETKA", "HOTLINE"]
}
16. Приклад XML-фіду Hotline
<price>
<date>2026-05-07 12:00</date>
<firmName>Example Store</firmName>
<firmId>12345</firmId>
<categories>
<category>
<id>phones</id>
<name>Смартфони</name>
</category>
</categories>
<items>
<item>
<id>K2-PRODUCT-000123</id>
<code>SKU-001</code>
<categoryId>phones</categoryId>
<vendor>Example</vendor>
<name>Смартфон Example X 128GB</name>
<description>Опис товару</description>
<url>https://example.com/product/sku-001</url>
<image>https://example.com/images/sku-001-1.jpg</image>
<priceRUAH>12999</priceRUAH>
<stock>В наявності</stock>
<guarantee>12 місяців</guarantee>
</item>
</items>
</price>
Важливо: структура XML вище є прикладом. Production-фід потрібно формувати за актуальною специфікацією Hotline XML або YML.
17. Валідація товарів
Перед передачею товару система повинна перевірити:
- SKU заповнений;
- назва заповнена;
- опис заповнений;
- категорія K2 ERP визначена;
- категорія каналу замаплена;
- бренд заповнений, якщо обов'язковий;
- ціна більша за 0;
- залишок не від'ємний;
- фото доступні за URL;
- характеристики заповнені;
- штрихкод валідний, якщо потрібен;
- гарантія заповнена, якщо потрібна;
- товар не заборонений правилами каналу;
- назва не містить HTML або службового сміття;
- опис не містить заборонених тегів;
- для Hotline є URL товару на сайті магазину;
- для Rozetka є категорія й характеристики згідно з вимогами категорії;
- для Prom є коректний статус публікації.
Критично важливо: товар без категорії, фото, ціни або обов'язкових характеристик не повинен автоматично передаватись у канали. Він повинен потрапляти в статус NEEDS_CORRECTION.
18. Дедублікація
Система повинна не допускати дублювання товарів і замовлень.
18.1. Дедублікація товарів
| Ключ | Призначення |
|---|---|
| sku | Основний артикул. |
| product_id | ID товару K2 ERP. |
| marketplace_product_id | ID товару в каналі. |
| barcode | Штрихкод. |
| product_hash | Hash назви, ціни, залишку, характеристик. |
18.2. Дедублікація замовлень
| Ключ | Призначення |
|---|---|
| marketplace | Джерело замовлення. |
| external_order_id | ID замовлення в маркетплейсі. |
| order_number | Номер замовлення. |
| idempotency_key | Ключ повторної обробки. |
19. Черги синхронізації
19.1. Логіка синхронізації товарів
1. K2 ERP змінює товар. 2. Python-сервіс отримує подію. 3. Товар проходить валідацію. 4. Для кожного активного каналу створюється sync task. 5. Worker передає товар через API або додає у фід. 6. Результат зберігається. 7. K2 ERP отримує статус.
19.2. Логіка імпорту замовлень
1. Worker регулярно запитує нові замовлення Prom / Rozetka. 2. Перевіряє external_order_id. 3. Якщо замовлення нове — створює його в K2 ERP. 4. Якщо товар не знайдено — статус NEEDS_REVIEW. 5. Якщо все коректно — статус IMPORTED. 6. Резервує товар у K2 ERP.
19.3. Пріоритети задач
| Задача | Пріоритет | Коментар |
|---|---|---|
| Імпорт замовлень | Критичний | Впливає на продажі. |
| Оновлення залишків | Критичний | Захищає від продажу відсутнього товару. |
| Оновлення цін | Високий | Впливає на маржу. |
| Оновлення статусів замовлень | Високий | Впливає на клієнтський досвід. |
| Синхронізація товарів | Середній | Каталог. |
| Генерація фідів | Середній | Hotline / Rozetka feed. |
| Оновлення ставок Hotline | Середній | Маркетинг. |
20. Модель даних
20.1. marketplace_integrations
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID інтеграції. |
| marketplace | varchar | PROM, ROZETKA, HOTLINE. |
| name | varchar | Назва. |
| base_url | varchar | URL API. |
| api_token_encrypted | text | Зашифрований token. |
| login_encrypted | text | Зашифрований login. |
| password_encrypted | text | Зашифрований password. |
| feed_public_url | varchar | URL фіду. |
| sync_products_enabled | boolean | Синхронізація товарів. |
| sync_prices_enabled | boolean | Синхронізація цін. |
| sync_stocks_enabled | boolean | Синхронізація залишків. |
| sync_orders_enabled | boolean | Імпорт замовлень. |
| is_active | boolean | Активність. |
20.2. marketplace_products
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID запису. |
| product_id | uuid | ID товару K2 ERP. |
| marketplace | varchar | PROM, ROZETKA, HOTLINE. |
| external_product_id | varchar | ID товару в каналі. |
| sku | varchar | Артикул. |
| status | varchar | Статус синхронізації. |
| marketplace_category_id | varchar | Категорія каналу. |
| last_synced_price | numeric | Остання передана ціна. |
| last_synced_stock | numeric | Останній переданий залишок. |
| raw_response | jsonb | Відповідь API. |
| error_message | text | Остання помилка. |
| updated_at | timestamp | Дата оновлення. |
20.3. marketplace_orders
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID замовлення. |
| marketplace | varchar | PROM або ROZETKA. |
| external_order_id | varchar | ID замовлення в каналі. |
| order_number | varchar | Номер. |
| k2_order_id | uuid | ID замовлення K2 ERP. |
| customer_name | varchar | Покупець. |
| customer_phone | varchar | Телефон. |
| total_amount | numeric | Сума. |
| status | varchar | Статус K2 ERP. |
| marketplace_status | varchar | Оригінальний статус. |
| delivery_service | varchar | Служба доставки. |
| tracking_number | varchar | ТТН / ЕН. |
| raw_payload | jsonb | Дані замовлення. |
| created_at | timestamp | Дата створення. |
20.4. marketplace_feeds
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID фіду. |
| marketplace | varchar | HOTLINE або ROZETKA. |
| feed_type | varchar | products, actions, prices. |
| format | varchar | XML, YML, CSV. |
| public_url | varchar | URL фіду. |
| file_id | uuid | Файл. |
| status | varchar | Статус генерації. |
| products_count | integer | Кількість товарів. |
| errors_count | integer | Кількість помилок. |
| generated_at | timestamp | Дата генерації. |
20.5. marketplace_events
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID події. |
| marketplace | varchar | PROM, ROZETKA, HOTLINE. |
| entity_type | varchar | product, order, feed, price, stock, bid. |
| entity_id | uuid | ID сутності. |
| event_type | varchar | Тип події. |
| old_status | varchar | Старий статус. |
| new_status | varchar | Новий статус. |
| payload | jsonb | Технічні дані. |
| created_at | timestamp | Дата. |
21. Приклад Python-логіки
21.1. Синхронізація товару
async def sync_product_to_marketplaces(product_id: str, channels: list[str], db: "Session") -> None:
product = product_repository.get_by_id(db, product_id)
validation_result = product_validator.validate(product)
if not validation_result.is_valid:
product_status_service.set_status(
product_id=product_id,
status="NEEDS_CORRECTION",
errors=validation_result.errors,
db=db,
)
return
for channel in channels:
sync_queue.enqueue(
task_name="sync_product_to_channel",
payload={
"product_id": product_id,
"marketplace": channel,
},
)
21.2. Worker передачі товару
async def sync_product_to_channel(product_id: str, marketplace: str, db: "Session") -> None:
product = product_repository.get_by_id(db, product_id)
integration = integration_repository.get_active(db, marketplace)
client = marketplace_client_factory.create(integration)
payload = marketplace_mapper.to_payload(product, marketplace)
mp_product = marketplace_product_repository.get_or_create(
db=db,
product_id=product.id,
marketplace=marketplace,
)
try:
mp_product.status = "SYNCING"
db.commit()
response = await client.sync_product(payload)
mp_product.external_product_id = response.get("external_product_id")
mp_product.status = "SYNCED"
mp_product.raw_response = response
mp_product.last_synced_price = product.price
mp_product.last_synced_stock = product.available_stock
audit_logger.log(
marketplace=marketplace,
entity_type="product",
entity_id=mp_product.id,
event_type="PRODUCT_SYNCED",
new_status="SYNCED",
payload=response,
)
except Exception as exc:
mp_product.status = "SYNC_ERROR"
mp_product.error_message = str(exc)
finally:
db.commit()
21.3. Імпорт замовлень
async def import_marketplace_orders(marketplace: str, db: "Session") -> None:
integration = integration_repository.get_active(db, marketplace)
client = marketplace_client_factory.create(integration)
orders = await client.import_orders(filters={"status": "new"})
for order_payload in orders:
external_order_id = order_payload["external_order_id"]
existing = marketplace_order_repository.get_by_external_id(
db=db,
marketplace=marketplace,
external_order_id=external_order_id,
)
if existing:
continue
try:
k2_order = k2_order_service.create_from_marketplace(order_payload)
marketplace_order_repository.create(
db=db,
data={
"marketplace": marketplace,
"external_order_id": external_order_id,
"order_number": order_payload.get("order_number"),
"k2_order_id": k2_order.id,
"customer_name": order_payload.get("customer_name"),
"customer_phone": order_payload.get("customer_phone"),
"total_amount": order_payload.get("total_amount"),
"status": "IMPORTED",
"marketplace_status": order_payload.get("status"),
"raw_payload": order_payload,
},
)
except Exception as exc:
marketplace_order_repository.create_error_record(
db=db,
marketplace=marketplace,
external_order_id=external_order_id,
payload=order_payload,
error=str(exc),
)
db.commit()
22. Обробка помилок
| Тип помилки | Опис | Дія системи |
|---|---|---|
| AuthError | Невірний token, login або password. | Зупинити інтеграцію, повідомити адміністратора. |
| ValidationError | Некоректний товар або замовлення. | Перевести в NEEDS_CORRECTION / NEEDS_REVIEW. |
| CategoryMappingError | Не знайдено категорію каналу. | Передати менеджеру каталогу. |
| AttributeMappingError | Не замаплені характеристики. | Передати менеджеру каталогу. |
| ImageError | Фото недоступне або некоректне. | Заблокувати передачу товару. |
| PriceError | Ціна некоректна. | Заблокувати синхронізацію ціни. |
| StockError | Залишок некоректний. | Заблокувати синхронізацію залишку. |
| ApiError | API повернув помилку. | Зберегти raw-відповідь. |
| FeedGenerationError | Помилка генерації XML/YML. | Не публікувати фід. |
| DuplicateOrderError | Замовлення вже імпортовано. | Повернути існуюче замовлення. |
| UnknownStatusError | Канал повернув невідомий статус. | Перевести в ручну перевірку. |
23. Retry-логіка
Retry дозволений для:
- timeout;
- HTTP 429;
- HTTP 500;
- HTTP 502;
- HTTP 503;
- HTTP 504;
- тимчасової недоступності API;
- тимчасової помилки імпорту замовлень;
- тимчасової помилки оновлення ціни;
- тимчасової помилки оновлення залишку;
- тимчасової помилки генерації фіду.
Retry заборонений для:
- неправильного API token;
- помилок категорії;
- помилок характеристик;
- товару без фото;
- товару без ціни;
- дубліката замовлення;
- відхиленого товару без виправлення даних.
24. Dashboard керівника
24.1. Основні KPI
| KPI | Опис | Колір |
|---|---|---|
| Активні товари | Кількість товарів, активних у каналах. | Інформація |
| Синхронізовано | Товари успішно передані. | Норма |
| На модерації | Товари очікують перевірки. | Увага |
| Відхилено | Товари відхилені каналом. | Критично |
| Помилки синхронізації | Товари або замовлення з помилками. | Критично |
| Нові замовлення | Отримані з Prom/Rozetka. | Увага |
| Імпортовано замовлень | Успішно створено в K2 ERP. | Норма |
| Фіди сформовані | XML/YML-фіди згенеровані. | Норма |
| Проблемні фіди | Помилки XML/YML. | Критично |
24.2. Приклад dashboard
| Показник | Prom | Rozetka | Hotline | Стан |
|---|---|---|---|---|
| Активні товари | 12 450 | 8 210 | 10 980 | Інформація |
| Синхронізовано | 12 100 | 7 940 | 10 900 | Норма |
| На модерації | 120 | 180 | - | Увага |
| Відхилено | 35 | 52 | - | Критично |
| Помилки | 18 | 24 | 6 | Критично |
| Нові замовлення | 42 | 28 | - | Увага |
| Імпортовано замовлень | 39 | 27 | - | Норма |
24.3. Проблемні товари
| SKU | Назва | Канал | Статус | Помилка | Дія |
|---|---|---|---|---|---|
| SKU-001 | Смартфон Example X | Rozetka | Потребує виправлення | Не замаплена характеристика «Пам'ять» | Заповнити мапінг |
| SKU-002 | Навушники Example Air | Prom | Помилка | Фото недоступне | Перевірити URL фото |
| SKU-003 | Кавоварка Example | Hotline | Помилка фіду | Некоректний XML-символ в описі | Очистити опис |
25. Безпека
Система повинна забезпечити:
- зберігання API token і credentials тільки у secret storage;
- заборону логування secrets;
- HTTPS для всіх API-запитів;
- перевірку SSL;
- рольову модель доступу;
- окремі права на синхронізацію товарів;
- окремі права на зміну цін;
- окремі права на зміну залишків;
- окремі права на оновлення ставок Hotline;
- журнал усіх дій;
- маскування персональних даних покупців;
- контроль доступу до замовлень;
- захист від дублювання.
26. Логування та аудит
Система повинна логувати:
| Подія | Що зберігати |
|---|---|
| Синхронізація товару | SKU, канал, статус, відповідь. |
| Зміна ціни | Стара ціна, нова ціна, канал. |
| Зміна залишку | Старий залишок, новий залишок, канал. |
| Імпорт замовлення | Канал, external_order_id, статус. |
| Оновлення статусу | Старий статус, новий статус, канал. |
| Генерація фіду | Канал, формат, кількість товарів, помилки. |
| Оновлення ставки | Hotline offer ID, стара ставка, нова ставка. |
| Помилка API | Код, текст, raw-відповідь. |
| Retry | Хто або який worker запустив, причина, результат. |
27. Acceptance Criteria
27.1. Інтеграції
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-1 | Адміністратор створює інтеграцію Prom. | Інтеграція зберігається і проходить check_connection. |
| AC-2 | Адміністратор створює інтеграцію Rozetka. | Інтеграція зберігається і проходить check_connection. |
| AC-3 | Адміністратор створює інтеграцію Hotline. | Система формує тестовий фід або перевіряє налаштування. |
| AC-4 | Token неправильний. | Система показує AuthError і не виконує синхронізацію. |
27.2. Товари
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-5 | Товар має всі обов'язкові поля. | Він передається в активні канали. |
| AC-6 | Товар не має фото. | Він переходить у NEEDS_CORRECTION. |
| AC-7 | Категорія не замаплена. | Товар не передається і показується менеджеру. |
| AC-8 | Товар успішно передано. | Статус стає SYNCED або PUBLISHED. |
27.3. Ціни та залишки
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-9 | У K2 ERP змінилась ціна. | Система передає нову ціну в канали. |
| AC-10 | У K2 ERP змінився залишок. | Система передає новий залишок. |
| AC-11 | Залишок став 0. | Товар приховується або передається як недоступний за правилом. |
27.4. Замовлення
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-12 | Prom має нове замовлення. | Воно імпортується в K2 ERP. |
| AC-13 | Rozetka має нове замовлення. | Воно імпортується в K2 ERP. |
| AC-14 | Замовлення вже імпортоване. | Дубль не створюється. |
| AC-15 | Товар у замовленні не знайдено. | Замовлення переходить у NEEDS_REVIEW. |
27.5. Hotline
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-16 | Система генерує Hotline XML. | Фід доступний за public URL. |
| AC-17 | У товарі некоректний XML-символ. | Фід не публікується або товар виключається за правилом. |
| AC-18 | Користувач оновлює ставку Hotline. | Ставка передається через API ставок і логуються. |
27.6. Dashboard
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-19 | Керівник відкриває dashboard. | Він бачить товари, замовлення, фіди, помилки по Prom/Rozetka/Hotline. |
| AC-20 | Є відхилені товари. | Вони підсвічуються червоним. |
| AC-21 | Є товари без мапінгу. | Вони підсвічуються помаранчевим. |
28. MVP
До MVP входить:
- створення інтеграцій Prom, Rozetka, Hotline;
- перевірка підключення;
- мапінг категорій;
- мапінг характеристик;
- синхронізація товарів;
- синхронізація цін;
- синхронізація залишків;
- імпорт замовлень з Prom;
- імпорт замовлень з Rozetka;
- оновлення статусів замовлень;
- передача ТТН;
- генерація Hotline XML/YML-фіду;
- публікація фіду за URL;
- базове керування ставками Hotline, якщо є token;
- дедублікація;
- retry-механізм;
- журнал подій;
- dashboard API;
- unit-тести;
- mock clients для Prom/Rozetka/Hotline.
До MVP не входить:
- повна підтримка всіх API-методів Prom;
- повна підтримка всіх API-методів Rozetka;
- повна автоматизація модерації;
- автоматичне виправлення характеристик;
- складна аналітика реклами;
- повна інтеграція з кабінетами маркетплейсів;
- ML-мапінг категорій;
- автоматичне створення рекламних кампаній.
29. Етапи реалізації
Етап 1. Аналіз API та вимог
- отримати Prom API token;
- отримати Rozetka API credentials;
- отримати Hotline feed specs;
- отримати Hotline bid API token, якщо потрібно;
- перевірити тестові запити;
- визначити список категорій;
- визначити правила мапінгу.
Етап 2. Базовий Python-сервіс
- створити FastAPI-проєкт;
- налаштувати PostgreSQL;
- створити моделі інтеграцій, товарів, замовлень, фідів, подій;
- налаштувати Alembic;
- реалізувати healthcheck.
Етап 3. Marketplace Adapters
- реалізувати PromClient;
- реалізувати RozetkaClient;
- реалізувати HotlineFeedGenerator;
- реалізувати HotlineBidClient;
- реалізувати client factory;
- реалізувати обробку помилок.
Етап 4. Товари
- реалізувати product mapper;
- реалізувати category mapper;
- реалізувати attribute mapper;
- реалізувати валідацію;
- реалізувати sync workers.
Етап 5. Ціни та залишки
- реалізувати price engine;
- реалізувати stock engine;
- реалізувати чергу оновлення;
- реалізувати історію змін.
Етап 6. Замовлення
- реалізувати order import workers;
- реалізувати дедублікацію;
- реалізувати створення замовлення в K2 ERP;
- реалізувати status export;
- реалізувати передачу ТТН.
Етап 7. Фіди Hotline / Rozetka
- реалізувати XML/YML generator;
- реалізувати валідацію XML;
- реалізувати публікацію файлу;
- реалізувати журнал генерацій;
- реалізувати фід акцій, якщо потрібно.
Етап 8. Dashboard та аудит
- реалізувати dashboard API;
- реалізувати список проблемних товарів;
- реалізувати список проблемних замовлень;
- реалізувати список помилок фідів;
- реалізувати експорт, якщо потрібно.
Етап 9. Production hardening
- додати rate limiting;
- додати retry policy;
- додати dead letter queue;
- додати alerting;
- додати моніторинг;
- додати резервне копіювання;
- додати безпечне зберігання secret-ів.
30. Ризики
| Ризик | Опис | Як зменшити |
|---|---|---|
| Зміна API | Prom або Rozetka можуть змінити методи. | Версіонування клієнтів і contract-тести. |
| Помилки категорій | Товари не проходять модерацію. | Мапінг категорій і dashboard проблем. |
| Помилки характеристик | Rozetka часто потребує точних характеристик категорій. | Attribute mapping і валідація. |
| Дублювання замовлень | Повторний імпорт може створити дубль. | external_order_id + marketplace unique constraint. |
| Продаж відсутнього товару | Залишки не оновились вчасно. | Високий пріоритет stock sync. |
| Некоректний XML | Hotline не прийме фід. | XML validation перед публікацією. |
| Token недійсний | API не працює. | Check-connection і alert адміністратору. |
| Невідомий статус | Канал повернув новий статус. | UNKNOWN_STATUS і ручний мапінг. |
31. Відкриті питання
- Чи K2 ERP є головним джерелом товарів?
- Які категорії товарів потрібно підтримати в MVP?
- Чи потрібна повна синхронізація товарів у Prom через API?
- Чи Rozetka товари приймає через API, фід або змішано?
- Який формат фіду Rozetka потрібен?
- Який формат Hotline використовуємо: XML, YML, CSV?
- Чи потрібно керувати ставками Hotline?
- Чи потрібно імпортувати замовлення з Prom?
- Чи потрібно імпортувати замовлення з Rozetka?
- Чи потрібно оновлювати статуси замовлень у маркетплейсах?
- Чи потрібно передавати ТТН Нової пошти / Укрпошти?
- Чи потрібна підтримка декількох магазинів Prom?
- Чи потрібна підтримка декількох кабінетів Rozetka?
- Чи потрібні окремі правила цін для кожного каналу?
- Чи потрібні окремі склади для залишків по каналах?
- Чи потрібно автоматично приховувати товари без залишку?
- Як часто оновлювати ціни?
- Як часто оновлювати залишки?
- Як часто імпортувати замовлення?
32. Джерела
- https://public-api.docs.prom.ua/
- https://support.prom.ua/hc/uk/articles/360005438678-Чи-є-у-сервіса-публічне-API
- https://support.prom.ua/hc/uk/articles/360020350478-Управління-API-токенами-в-кабінеті-компанії
- https://api-seller.rozetka.com.ua/apidoc/
- https://sellerhelp.rozetka.com.ua/p282-api-start.html
- https://hotline.ua/ua/about/pricelists_specs/
- https://hotline.ua/ua/about/pricelists_specs_xml_action/
- https://hotline.ua/ua/about/api_auctions/