Інтеграція з Horoshop
Головна ідея: розробити Python-сервіс, який інтегрує K2 ERP / CRM / WMS з інтернет-магазином на платформі Horoshop / Хорошоп для синхронізації товарів, категорій, характеристик, цін, залишків, клієнтів, замовлень, статусів, оплат, доставок і журналу помилок.
Критично важливо: інтеграція не повинна створювати дублікати товарів, клієнтів або замовлень. Кожен товар, замовлення, клієнт, зміна ціни, залишку, статусу та API-запит повинні мати внутрішній ID, external_id, журнал подій і захист від повторної обробки.
Важливо: Horoshop API прив'язаний до домену конкретного магазину. Тому для кожного сайту потрібно зберігати власний base_url, логін, пароль, статус інтеграції, правила синхронізації та технічний журнал.
Технічний стек: Python 3.11+, FastAPI, PostgreSQL, SQLAlchemy, Alembic, httpx, Pydantic, Celery/RQ/APScheduler, Redis, Docker.
Управлінський результат: менеджер і керівник повинні бачити, які товари синхронізовані з Horoshop, які мають помилки, які замовлення імпортовані, які статуси передані, які залишки не оновились, які товари не мають фото, категорії або характеристик.
1. Мета
Метою задачі є створення Python-сервісу для інтеграції K2 ERP / CRM / WMS з інтернет-магазином на платформі Horoshop.
Сервіс повинен забезпечити:
- підключення до Horoshop API;
- перевірку доступності API;
- синхронізацію категорій;
- синхронізацію товарів;
- синхронізацію модифікацій / варіантів товарів;
- синхронізацію характеристик;
- синхронізацію фото;
- синхронізацію цін;
- синхронізацію знижок і акцій, якщо підтримується API;
- синхронізацію залишків;
- імпорт замовлень;
- імпорт клієнтів;
- оновлення статусів замовлень;
- передачу ТТН / ЕН у замовлення;
- синхронізацію оплат і доставок;
- журналювання API-запитів;
- контроль помилок;
- dashboard для менеджерів і керівника.
2. Область застосування
Інтеграція призначена для:
- інтернет-магазинів на Horoshop;
- компаній, які ведуть облік у K2 ERP;
- компаній, які мають CRM і хочуть імпортувати замовлення з сайту;
- складів, які мають оновлювати залишки на сайті;
- менеджерів продажів;
- e-commerce відділів;
- компаній із кількома інтернет-магазинами;
- компаній, які продають через сайт і маркетплейси одночасно.
3. Основні можливості інтеграції
| Напрям | Опис | Джерело істини | Коментар |
|---|---|---|---|
| Товари | Створення та оновлення товарів на Horoshop. | K2 ERP | ERP є головним джерелом каталогу. |
| Категорії | Синхронізація структури каталогу. | K2 ERP або Horoshop | Визначається правилами проєкту. |
| Характеристики | Передача атрибутів товарів. | K2 ERP | Потрібні для фільтрів і SEO. |
| Ціни | Оновлення базових, акційних, роздрібних цін. | K2 ERP | Важливо для комерційного контролю. |
| Залишки | Оновлення доступної кількості. | K2 ERP / WMS | Критичний процес. |
| Замовлення | Імпорт замовлень із Horoshop. | Horoshop | Замовлення створюються покупцями на сайті. |
| Клієнти | Імпорт або оновлення клієнтів. | Horoshop / K2 ERP | Залежить від бізнес-логіки. |
| Статуси | Оновлення статусів замовлень на сайті. | K2 ERP | ERP керує виконанням замовлень. |
| Доставка | Передача служби доставки, адреси, ТТН. | K2 ERP / служби доставки | Нова пошта, Укрпошта тощо. |
| Оплата | Статус і тип оплати. | Horoshop / платіжний сервіс / K2 ERP | Потребує мапінгу. |
4. Передумови
Для реалізації задачі необхідно отримати:
- домен сайту Horoshop;
- API base URL;
- API login;
- API password;
- доступ до адмінпанелі Horoshop;
- список API-методів, які доступні на конкретному тарифі / сайті;
- актуальну документацію API;
- правила мапінгу категорій;
- правила мапінгу характеристик;
- список статусів замовлень Horoshop;
- список статусів замовлень K2 ERP;
- список типів оплат;
- список типів доставок;
- правила синхронізації цін;
- правила синхронізації залишків;
- правила обробки замовлень;
- правила передачі ТТН.
Критично важливо: логін і пароль API Horoshop потрібно зберігати тільки у secret storage або в зашифрованому вигляді. Заборонено зберігати їх у коді, Git, frontend-змінних або відкритих логах.
5. Основні бізнес-сценарії
5.1. Передача товарів з K2 ERP у Horoshop
K2 ERP є головним джерелом товарного каталогу.
Python-сервіс повинен:
- отримати товар із K2 ERP;
- перевірити обов'язкові поля;
- визначити категорію;
- підготувати назву;
- підготувати опис;
- підготувати характеристики;
- підготувати фото;
- підготувати ціну;
- підготувати залишок;
- створити або оновити товар у Horoshop;
- зберегти external_product_id;
- записати результат у журнал.
5.2. Оновлення цін
Коли ціна змінюється в K2 ERP, Python-сервіс повинен:
- отримати подію зміни ціни;
- визначити канали продажу;
- перевірити активність товару;
- застосувати правила округлення;
- застосувати правила акційної ціни;
- передати нову ціну в Horoshop;
- зберегти історію зміни.
5.3. Оновлення залишків
Коли змінюється залишок на складі, Python-сервіс повинен:
- отримати залишок із K2 ERP або WMS;
- врахувати резерви;
- врахувати мінімальний страховий залишок;
- сформувати доступний залишок для сайту;
- передати залишок у Horoshop;
- приховати товар, якщо залишок 0, якщо це передбачено правилами;
- відновити товар, якщо залишок з'явився.
5.4. Імпорт замовлень
Horoshop є джерелом замовлень, які створюють покупці на сайті.
Python-сервіс повинен:
- регулярно отримувати нові замовлення;
- перевіряти, чи замовлення вже імпортоване;
- створювати замовлення в K2 ERP;
- створювати або оновлювати клієнта;
- зберігати товари замовлення;
- зберігати оплату;
- зберігати доставку;
- зберігати коментар покупця;
- резервувати товар;
- змінювати статус замовлення в Horoshop, якщо потрібно.
5.5. Оновлення статусу замовлення
K2 ERP є головним джерелом операційного статусу.
Python-сервіс повинен:
- отримати статус замовлення з K2 ERP;
- замапити його у статус Horoshop;
- передати статус у Horoshop;
- передати ТТН / ЕН, якщо є;
- передати коментар менеджера, якщо підтримується;
- записати подію в журнал.
5.6. Обмін клієнтами
Система повинна підтримувати:
- створення нового клієнта в K2 ERP на основі замовлення Horoshop;
- пошук клієнта за телефоном;
- пошук клієнта за email;
- оновлення ПІБ;
- оновлення адреси доставки;
- історію замовлень клієнта;
- мапінг customer_id Horoshop ↔ customer_id K2 ERP.
6. Основні сутності
| Сутність | Опис |
|---|---|
| Horoshop Integration | Налаштування підключення до конкретного сайту. |
| Product | Товар у K2 ERP. |
| Horoshop Product | Товар на сайті Horoshop. |
| Category | Категорія товарів. |
| Attribute | Характеристика товару. |
| Product Variant | Варіант / модифікація товару. |
| Product Image | Фото товару. |
| Price | Ціна товару. |
| Stock | Залишок товару. |
| Customer | Покупець. |
| Order | Замовлення. |
| Order Item | Рядок замовлення. |
| Payment | Оплата. |
| Delivery | Доставка. |
| API Event | Технічна подія інтеграції. |
7. User Story
7.1. Менеджер товарного каталогу
Як менеджер товарного каталогу, я хочу вести товари в K2 ERP, щоб вони автоматично з'являлися та оновлювалися на сайті Horoshop.
7.2. Склад
Як комірник або WMS, я хочу, щоб залишки автоматично оновлювалися на сайті, щоб покупці не замовляли товар, якого немає.
7.3. Менеджер продажів
Як менеджер продажів, я хочу отримувати замовлення з Horoshop у K2 ERP, щоб обробляти всі продажі в єдиній системі.
7.4. Керівник
Як керівник, я хочу бачити dashboard інтеграції, щоб контролювати помилки, продажі, синхронізацію товарів і проблемні замовлення.
7.5. Адміністратор
Як адміністратор, я хочу бачити журнал API-запитів, щоб швидко знаходити технічні помилки інтеграції.
8. Напрями синхронізації
| Об'єкт | Напрям | Джерело | Призначення |
|---|---|---|---|
| Категорії | K2 ERP → Horoshop або Horoshop → K2 ERP | Залежить від проєкту | Єдина структура каталогу. |
| Товари | K2 ERP → Horoshop | K2 ERP | Публікація каталогу. |
| Ціни | K2 ERP → Horoshop | K2 ERP | Актуальні ціни на сайті. |
| Залишки | K2 ERP/WMS → Horoshop | K2 ERP / WMS | Актуальна наявність. |
| Замовлення | Horoshop → K2 ERP | Horoshop | Обробка продажів. |
| Клієнти | Horoshop → K2 ERP | Horoshop | Створення клієнтів із замовлень. |
| Статуси замовлень | K2 ERP → Horoshop | K2 ERP | Покупець бачить актуальний статус. |
| ТТН / ЕН | K2 ERP → Horoshop | K2 ERP / доставка | Інформування покупця про доставку. |
9. Статуси товарів
| Статус | Код | Опис | Колір |
|---|---|---|---|
| Чернетка | DRAFT | Товар створений у K2 ERP, але ще не готовий до передачі. | Сірий |
| Очікує валідації | PENDING_VALIDATION | Товар очікує перевірки. | Жовтий |
| Потребує виправлення | NEEDS_CORRECTION | Не вистачає категорії, ціни, фото, опису або характеристик. | Помаранчевий |
| Готовий до синхронізації | READY_TO_SYNC | Товар можна передавати. | Блакитний |
| Синхронізується | SYNCING | Виконується API-запит до Horoshop. | Блакитний |
| Синхронізовано | SYNCED | Товар успішно передано або оновлено. | Зелений |
| Опубліковано | PUBLISHED | Товар доступний на сайті. | Зелений |
| Приховано | HIDDEN | Товар прихований на сайті. | Сірий |
| Помилка синхронізації | SYNC_ERROR | API повернув помилку або товар не передано. | Червоний |
10. Статуси замовлень
| Статус K2 ERP | Код | Опис | Колір |
|---|---|---|---|
| Нове | NEW | Замовлення отримано з Horoshop. | Жовтий |
| Імпортується | IMPORTING | Створюється замовлення в K2 ERP. | Блакитний |
| Імпортовано | IMPORTED | Замовлення створено в K2 ERP. | Зелений |
| Потребує перевірки | NEEDS_REVIEW | Не знайдено товар, клієнта, оплату або доставку. | Помаранчевий |
| Підтверджено | CONFIRMED | Менеджер підтвердив замовлення. | Зелений |
| Комплектується | PROCESSING | Замовлення збирається на складі. | Блакитний |
| Очікує оплати | WAITING_PAYMENT | Замовлення очікує оплату. | Жовтий |
| Оплачено | PAID | Оплата отримана. | Зелений |
| Відправлено | SHIPPED | Замовлення передано в доставку. | Блакитний |
| Виконано | COMPLETED | Замовлення завершено. | Зелений |
| Скасовано | CANCELLED | Замовлення скасовано. | Сірий |
| Помилка | ERROR | Помилка імпорту або оновлення. | Червоний |
11. Єдина логіка кольорів
| Колір | HTML | Значення | Де використовується |
|---|---|---|---|
| Зелений | #c8e6c9 | Успішно: синхронізовано, опубліковано, імпортовано, виконано. | Dashboard, товари, замовлення. |
| Блакитний | #bbdefb | Операція виконується. | API-запити, черги, обробка. |
| Жовтий | #fff9c4 | Очікування дії або оплати. | Нові замовлення, очікування оплати. |
| Помаранчевий | #ffcc80 | Потрібна дія менеджера. | Помилки мапінгу, неповні товари, review. |
| Червоний | #ef9a9a | Критична помилка. | API error, sync error. |
| Фіолетовий | #f3e5f5 | Ручна перевірка або нестандартний сценарій. | Manual review. |
| Сірий | #eeeeee | Чернетка, приховано, скасовано або архів. | Draft, hidden, cancelled. |
12. Архітектура рішення
12.1. Загальна схема
K2 ERP / CRM / WMS
|
| 1. Товари, ціни, залишки, статуси
v
Python Horoshop Integration Service
|
| 2. Валідація, мапінг, черги, дедублікація
v
Horoshop API Client
|
| 3. HTTP/HTTPS API Horoshop
v
Інтернет-магазин Horoshop
|
| 4. Замовлення, клієнти, статуси
v
Order Import Worker
|
| 5. Імпорт у K2 ERP
v
K2 ERP / Dashboard / Менеджери
12.2. Основні компоненти Python-сервісу
| Компонент | Опис |
|---|---|
| API Layer | REST API для команд із K2 ERP. |
| Horoshop Client | Python-клієнт для Horoshop API. |
| Product Mapper | Перетворює товар K2 ERP у формат Horoshop. |
| Order Mapper | Перетворює замовлення Horoshop у формат K2 ERP. |
| Category Mapper | Мапить категорії. |
| Attribute Mapper | Мапить характеристики. |
| Price Engine | Формує ціни для сайту. |
| Stock Engine | Формує доступний залишок. |
| Product Sync Worker | Передає товари. |
| Price Sync Worker | Передає ціни. |
| Stock Sync Worker | Передає залишки. |
| Order Import Worker | Імпортує замовлення. |
| Status Export Worker | Передає статуси назад у Horoshop. |
| Audit Logger | Запити, відповіді, помилки, статуси. |
| Dashboard API | Дані для контролю інтеграції. |
13. Horoshop API Client
13.1. Призначення
Horoshop API Client — це Python-клас або пакет, який інкапсулює роботу з API конкретного сайту Horoshop.
13.2. Загальні принципи
API-запити виконуються до шлюзу:
http(s)://<DOMAIN>/api/
Функції API викликаються через URL:
http(s)://<DOMAIN>/api/<FUNCTION>/
Це відповідає офіційній документації Horoshop API, де вказано, що шлюз має формат `http(s)://<DOMAIN>/api/`, а функції передаються через адресний рядок. :contentReference[oaicite:2]{index=2}
13.3. Основні методи клієнта
class HoroshopClient:
def check_connection(self) -> "ConnectionStatus":
pass
def authenticate(self) -> "AuthResult":
pass
def call_api(self, function: str, payload: dict | None = None) -> dict:
pass
def get_orders(self, filters: dict) -> "OrderListResponse":
pass
def get_order(self, order_id: str) -> "OrderResponse":
pass
def update_order_status(self, order_id: str, payload: dict) -> "OrderStatusResponse":
pass
def create_or_update_product(self, payload: dict) -> "ProductResponse":
pass
def update_product_price(self, product_id: str, payload: dict) -> "PriceResponse":
pass
def update_product_stock(self, product_id: str, payload: dict) -> "StockResponse":
pass
def create_or_update_category(self, payload: dict) -> "CategoryResponse":
pass
def get_customers(self, filters: dict) -> "CustomerListResponse":
pass
Важливо: назви методів Python-клієнта є внутрішньою абстракцією. Реальні назви функцій Horoshop API потрібно брати з актуальної документації конкретного магазину.
14. Конфігурація клієнта
from pydantic_settings import BaseSettings
class HoroshopSettings(BaseSettings):
base_url: str
api_login: str
api_password: str
timeout_seconds: int = 30
retry_count: int = 3
retry_backoff_seconds: int = 5
verify_ssl: bool = True
language: str = "ua"
Приклад `.env`:
HOROSHOP_BASE_URL=https://example.com/api/ HOROSHOP_API_LOGIN=integration_user HOROSHOP_API_PASSWORD=******** HOROSHOP_TIMEOUT_SECONDS=30 HOROSHOP_RETRY_COUNT=3 HOROSHOP_RETRY_BACKOFF_SECONDS=5 HOROSHOP_LANGUAGE=ua
15. API Python-сервісу
15.1. Створення інтеграції
POST /api/v1/horoshop/integrations
15.2. Перевірка підключення
POST /api/v1/horoshop/integrations/{integration_id}/check-connection
15.3. Синхронізація категорій
POST /api/v1/horoshop/categories/sync
15.4. Синхронізація товару
POST /api/v1/horoshop/products/{product_id}/sync
15.5. Масова синхронізація товарів
POST /api/v1/horoshop/products/sync-batch
15.6. Оновлення цін
POST /api/v1/horoshop/prices/sync
15.7. Оновлення залишків
POST /api/v1/horoshop/stocks/sync
15.8. Імпорт замовлень
POST /api/v1/horoshop/orders/import
15.9. Оновлення статусу замовлення
POST /api/v1/horoshop/orders/{order_id}/update-status
15.10. Передача ТТН / ЕН
POST /api/v1/horoshop/orders/{order_id}/tracking-number
15.11. Dashboard
GET /api/v1/horoshop/dashboard?date_from=2026-05-01&date_to=2026-05-31
16. Приклад запиту на синхронізацію товару
{
"product_id": "K2-PRODUCT-000123",
"sku": "SKU-001",
"name": "Смартфон Example X 128GB",
"description": "Опис товару для сайту.",
"category_id": "phones",
"horoshop_category_id": "h-category-001",
"brand": "Example",
"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 місяців"
},
"is_active": true
}
17. Приклад замовлення з Horoshop
{
"external_order_id": "HS-ORDER-000123",
"order_number": "000123",
"order_date": "2026-05-07T12:10:00+03:00",
"customer": {
"external_customer_id": "HS-CUSTOMER-001",
"name": "Іван Петренко",
"phone": "+380671112233",
"email": "client@example.com"
},
"items": [
{
"sku": "SKU-001",
"name": "Смартфон Example X 128GB",
"quantity": 1,
"price": 12999.00,
"amount": 12999.00
}
],
"payment": {
"type": "online_card",
"status": "paid",
"amount": 12999.00
},
"delivery": {
"service": "nova_poshta",
"city": "Київ",
"warehouse": "Відділення №1",
"address": null
},
"comment": "Зателефонувати перед відправкою",
"total_amount": 12999.00,
"status": "new"
}
18. Валідація товарів
Перед передачею товару в Horoshop система повинна перевірити:
- SKU заповнений;
- назва заповнена;
- опис заповнений;
- категорія визначена;
- категорія Horoshop замаплена;
- ціна більша за 0;
- залишок не від'ємний;
- фото доступні;
- головне фото визначене;
- характеристики заповнені;
- бренд заповнений, якщо потрібен;
- штрихкод валідний, якщо використовується;
- товар не дублюється за SKU;
- товар не дублюється за external_product_id;
- текст опису не містить небезпечного HTML;
- активність товару відповідає правилам публікації.
Критично важливо: товар без SKU, категорії, ціни або головного фото не повинен автоматично передаватися в Horoshop. Він повинен потрапляти у статус NEEDS_CORRECTION.
19. Валідація замовлень
Перед створенням замовлення в K2 ERP система повинна перевірити:
- external_order_id;
- номер замовлення;
- дату замовлення;
- телефон покупця;
- список товарів;
- SKU кожного товару;
- кількість;
- ціну;
- суму;
- тип оплати;
- статус оплати;
- тип доставки;
- адресу або відділення;
- чи не імпортоване замовлення раніше;
- чи існують товари в K2 ERP;
- чи достатньо залишку для резервування.
20. Дедублікація
20.1. Дедублікація товарів
| Ключ | Призначення |
|---|---|
| sku | Основний артикул. |
| k2_product_id | ID товару в K2 ERP. |
| horoshop_product_id | ID товару в Horoshop. |
| barcode | Штрихкод. |
| product_hash | Hash назви, опису, ціни, залишку, характеристик. |
20.2. Дедублікація замовлень
| Ключ | Призначення |
|---|---|
| external_order_id | ID замовлення Horoshop. |
| order_number | Номер замовлення. |
| horoshop_site_id | ID сайту, якщо магазинів декілька. |
| idempotency_key | Ключ повторної обробки. |
21. Черги синхронізації
21.1. Логіка синхронізації товарів
1. K2 ERP змінює товар. 2. Python-сервіс отримує подію. 3. Товар проходить валідацію. 4. Створюється задача Product Sync. 5. Worker викликає Horoshop API. 6. Результат зберігається. 7. K2 ERP отримує статус.
21.2. Логіка оновлення залишків
1. WMS або K2 ERP змінює залишок. 2. Python-сервіс формує доступну кількість. 3. Враховуються резерви та страховий залишок. 4. Створюється задача Stock Sync. 5. Worker передає залишок у Horoshop. 6. Результат записується в журнал.
21.3. Логіка імпорту замовлень
1. Worker регулярно запитує нові замовлення Horoshop. 2. Перевіряє external_order_id. 3. Якщо замовлення нове — створює його в K2 ERP. 4. Якщо товар не знайдено — статус NEEDS_REVIEW. 5. Якщо все коректно — статус IMPORTED. 6. Резервує товар у K2 ERP. 7. За потреби оновлює статус у Horoshop.
21.4. Пріоритети задач
| Задача | Пріоритет | Коментар |
|---|---|---|
| Імпорт замовлень | Критичний | Впливає на продажі. |
| Оновлення залишків | Критичний | Захищає від продажу відсутнього товару. |
| Оновлення цін | Високий | Впливає на маржу. |
| Оновлення статусів замовлень | Високий | Впливає на клієнтський досвід. |
| Синхронізація товарів | Середній | Каталог. |
| Синхронізація категорій | Низький | Довідник. |
22. Модель даних
22.1. horoshop_integrations
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID інтеграції. |
| name | varchar | Назва інтеграції. |
| site_domain | varchar | Домен сайту. |
| base_url | varchar | URL API. |
| api_login_encrypted | text | Зашифрований API login. |
| api_password_encrypted | text | Зашифрований API password. |
| sync_products_enabled | boolean | Синхронізація товарів. |
| sync_prices_enabled | boolean | Синхронізація цін. |
| sync_stocks_enabled | boolean | Синхронізація залишків. |
| sync_orders_enabled | boolean | Імпорт замовлень. |
| sync_statuses_enabled | boolean | Оновлення статусів. |
| is_active | boolean | Активність. |
| created_at | timestamp | Дата створення. |
| updated_at | timestamp | Дата оновлення. |
22.2. horoshop_products
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID запису. |
| k2_product_id | uuid | ID товару K2 ERP. |
| horoshop_product_id | varchar | ID товару Horoshop. |
| sku | varchar | Артикул. |
| status | varchar | Статус синхронізації. |
| horoshop_category_id | varchar | Категорія Horoshop. |
| last_synced_price | numeric | Остання передана ціна. |
| last_synced_stock | numeric | Останній переданий залишок. |
| product_hash | varchar | Hash даних товару. |
| raw_response | jsonb | Відповідь API. |
| error_message | text | Остання помилка. |
| updated_at | timestamp | Дата оновлення. |
22.3. horoshop_orders
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID замовлення. |
| external_order_id | varchar | ID замовлення Horoshop. |
| order_number | varchar | Номер замовлення. |
| k2_order_id | uuid | ID замовлення K2 ERP. |
| customer_name | varchar | Покупець. |
| customer_phone | varchar | Телефон. |
| customer_email | varchar | Email. |
| total_amount | numeric | Сума. |
| payment_type | varchar | Тип оплати. |
| payment_status | varchar | Статус оплати. |
| delivery_service | varchar | Служба доставки. |
| delivery_address | text | Адреса доставки. |
| tracking_number | varchar | ТТН / ЕН. |
| status | varchar | Статус K2 ERP. |
| horoshop_status | varchar | Оригінальний статус Horoshop. |
| raw_payload | jsonb | Дані замовлення. |
| created_at | timestamp | Дата створення. |
22.4. horoshop_order_items
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID рядка. |
| order_id | uuid | Замовлення. |
| sku | varchar | Артикул. |
| k2_product_id | uuid | ID товару K2 ERP. |
| name | varchar | Назва товару. |
| quantity | numeric | Кількість. |
| price | numeric | Ціна. |
| amount | numeric | Сума. |
22.5. horoshop_category_mappings
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID мапінгу. |
| k2_category_id | varchar | Категорія K2 ERP. |
| horoshop_category_id | varchar | Категорія Horoshop. |
| is_active | boolean | Активність. |
22.6. horoshop_status_mappings
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID мапінгу. |
| k2_status | varchar | Статус K2 ERP. |
| horoshop_status | varchar | Статус Horoshop. |
| direction | varchar | IMPORT або EXPORT. |
| is_active | boolean | Активність. |
22.7. horoshop_events
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID події. |
| entity_type | varchar | product, order, price, stock, category, customer. |
| entity_id | uuid | ID сутності. |
| event_type | varchar | Тип події. |
| old_status | varchar | Старий статус. |
| new_status | varchar | Новий статус. |
| payload | jsonb | Технічні дані. |
| created_at | timestamp | Дата. |
23. Приклад Python-логіки
23.1. Базовий Horoshop Client
import httpx
class HoroshopApiError(Exception):
pass
class HoroshopClient:
def __init__(self, base_url: str, api_login: str, api_password: str, timeout_seconds: int = 30):
self.base_url = base_url.rstrip("/")
self.api_login = api_login
self.api_password = api_password
self.timeout_seconds = timeout_seconds
async def call_api(self, function: str, payload: dict | None = None) -> dict:
url = f"{self.base_url}/{function.strip('/')}/"
request_payload = payload or {}
request_payload["login"] = self.api_login
request_payload["password"] = self.api_password
async with httpx.AsyncClient(timeout=self.timeout_seconds) as client:
response = await client.post(url, json=request_payload)
if response.status_code >= 400:
raise HoroshopApiError(response.text)
data = response.json()
if data.get("status") in ["error", "failed"]:
raise HoroshopApiError(str(data))
return data
Важливо: приклад авторизації є архітектурним. Точну схему передачі login/password, token або session потрібно звірити з актуальною документацією Horoshop API для конкретного методу.
23.2. Синхронізація товару
async def sync_product_to_horoshop(product_id: 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
integration = horoshop_integration_repository.get_active(db)
client = horoshop_client_factory.create(integration)
payload = horoshop_product_mapper.to_payload(product)
hs_product = horoshop_product_repository.get_or_create(
db=db,
k2_product_id=product.id,
sku=product.sku,
)
try:
hs_product.status = "SYNCING"
db.commit()
response = await client.call_api(
function="catalog/import/",
payload=payload,
)
hs_product.horoshop_product_id = response.get("product_id") or hs_product.horoshop_product_id
hs_product.status = "SYNCED"
hs_product.raw_response = response
hs_product.last_synced_price = product.price
hs_product.last_synced_stock = product.available_stock
audit_logger.log(
entity_type="product",
entity_id=hs_product.id,
event_type="HOROSHOP_PRODUCT_SYNCED",
new_status="SYNCED",
payload=response,
)
except Exception as exc:
hs_product.status = "SYNC_ERROR"
hs_product.error_message = str(exc)
finally:
db.commit()
23.3. Імпорт замовлень
async def import_horoshop_orders(db: "Session") -> None:
integration = horoshop_integration_repository.get_active(db)
client = horoshop_client_factory.create(integration)
response = await client.call_api(
function="orders/get/",
payload={
"status": "new",
"date_from": sync_state_repository.get_last_order_sync_date(db),
},
)
orders = response.get("orders", [])
for order_payload in orders:
external_order_id = str(order_payload["id"])
existing = horoshop_order_repository.get_by_external_id(
db=db,
external_order_id=external_order_id,
)
if existing:
continue
try:
k2_order = k2_order_service.create_from_horoshop(order_payload)
horoshop_order_repository.create(
db=db,
data={
"external_order_id": external_order_id,
"order_number": order_payload.get("number"),
"k2_order_id": k2_order.id,
"customer_name": order_payload.get("customer", {}).get("name"),
"customer_phone": order_payload.get("customer", {}).get("phone"),
"customer_email": order_payload.get("customer", {}).get("email"),
"total_amount": order_payload.get("total"),
"payment_type": order_payload.get("payment", {}).get("type"),
"payment_status": order_payload.get("payment", {}).get("status"),
"delivery_service": order_payload.get("delivery", {}).get("service"),
"status": "IMPORTED",
"horoshop_status": order_payload.get("status"),
"raw_payload": order_payload,
},
)
except Exception as exc:
horoshop_order_repository.create_error_record(
db=db,
external_order_id=external_order_id,
payload=order_payload,
error=str(exc),
)
db.commit()
23.4. Оновлення залишку
async def sync_stock_to_horoshop(product_id: str, db: "Session") -> None:
product = product_repository.get_by_id(db, product_id)
integration = horoshop_integration_repository.get_active(db)
client = horoshop_client_factory.create(integration)
available_stock = stock_engine.calculate_available_stock(
product_id=product.id,
warehouse_id=product.default_warehouse_id,
)
payload = {
"sku": product.sku,
"quantity": available_stock,
}
try:
response = await client.call_api(
function="catalog/update_stock/",
payload=payload,
)
horoshop_product_repository.update_stock_sync_result(
db=db,
k2_product_id=product.id,
stock=available_stock,
response=response,
)
except Exception as exc:
horoshop_product_repository.set_sync_error(
db=db,
k2_product_id=product.id,
error=str(exc),
)
db.commit()
23.5. Оновлення статусу замовлення
async def export_order_status_to_horoshop(k2_order_id: str, db: "Session") -> None:
order = k2_order_repository.get_by_id(db, k2_order_id)
hs_order = horoshop_order_repository.get_by_k2_order_id(db, k2_order_id)
if not hs_order:
return
horoshop_status = status_mapping_repository.map_k2_to_horoshop(
db=db,
k2_status=order.status,
)
payload = {
"order_id": hs_order.external_order_id,
"status": horoshop_status,
"tracking_number": order.tracking_number,
"comment": order.manager_comment,
}
integration = horoshop_integration_repository.get_active(db)
client = horoshop_client_factory.create(integration)
response = await client.call_api(
function="orders/update_status/",
payload=payload,
)
audit_logger.log(
entity_type="order",
entity_id=hs_order.id,
event_type="HOROSHOP_ORDER_STATUS_EXPORTED",
payload=response,
)
db.commit()
24. Обробка помилок
| Тип помилки | Опис | Дія системи |
|---|---|---|
| AuthError | Невірний API login або password. | Зупинити інтеграцію, повідомити адміністратора. |
| ConnectionError | API Horoshop недоступне. | Retry, dashboard warning. |
| ValidationError | Некоректний товар або замовлення. | NEEDS_CORRECTION / NEEDS_REVIEW. |
| CategoryMappingError | Не знайдено категорію Horoshop. | Передати менеджеру каталогу. |
| AttributeMappingError | Не замаплені характеристики. | Передати менеджеру каталогу. |
| ImageError | Фото недоступне. | Заблокувати передачу товару. |
| PriceError | Некоректна ціна. | Заблокувати синхронізацію ціни. |
| StockError | Некоректний залишок. | Заблокувати синхронізацію залишку. |
| DuplicateOrderError | Замовлення вже імпортовано. | Повернути існуюче замовлення. |
| UnknownStatusError | Статус не замаплений. | Перевести в ручну перевірку. |
25. Retry-логіка
Retry дозволений для:
- timeout;
- HTTP 429;
- HTTP 500;
- HTTP 502;
- HTTP 503;
- HTTP 504;
- тимчасової недоступності API;
- тимчасової помилки імпорту замовлень;
- тимчасової помилки оновлення ціни;
- тимчасової помилки оновлення залишку;
- тимчасової помилки оновлення статусу.
Retry заборонений для:
- неправильного API login/password;
- товару без SKU;
- товару без категорії;
- товару без ціни;
- товару без фото;
- незамапленого статусу;
- дубліката замовлення;
- помилки бізнес-логіки K2 ERP.
26. Dashboard керівника
26.1. Основні KPI
| KPI | Опис | Колір |
|---|---|---|
| Товарів у синхронізації | Кількість товарів, що передаються на сайт. | Інформація |
| Синхронізовано товарів | Товари успішно передані. | Норма |
| Помилки товарів | Товари з помилками валідації або API. | Критично |
| Оновлено цін | Кількість успішних оновлень цін. | Норма |
| Оновлено залишків | Кількість успішних оновлень залишків. | Норма |
| Нові замовлення | Нові замовлення з Horoshop. | Увага |
| Імпортовано замовлень | Замовлення створені в K2 ERP. | Норма |
| Потребують перевірки | Замовлення з невідомими товарами або даними. | Потрібна дія |
| Помилки API | Технічні помилки інтеграції. | Критично |
26.2. Приклад dashboard
| Показник | Значення | Стан |
|---|---|---|
| Активні товари | 12 450 | Інформація |
| Синхронізовано | 12 100 | Норма |
| Потребують виправлення | 180 | Потрібна дія |
| Помилки синхронізації | 24 | Критично |
| Оновлено цін сьогодні | 820 | Норма |
| Оновлено залишків сьогодні | 1 540 | Норма |
| Нові замовлення | 42 | Увага |
| Імпортовано замовлень | 39 | Норма |
| Замовлення на перевірці | 3 | Потрібна дія |
26.3. Проблемні товари
| SKU | Назва | Статус | Помилка | Дія |
|---|---|---|---|---|
| SKU-001 | Смартфон Example X | Потребує виправлення | Не замаплена категорія | Заповнити мапінг |
| SKU-002 | Навушники Example Air | Помилка | Фото недоступне | Перевірити URL фото |
| SKU-003 | Кавоварка Example | Потребує виправлення | Немає ціни | Заповнити ціну |
26.4. Проблемні замовлення
| Дата | Замовлення | Клієнт | Статус | Причина | Дія |
|---|---|---|---|---|---|
| 07.05.2026 | HS-ORDER-001 | Іван Петренко | Потребує перевірки | Не знайдено SKU | Прив'язати товар |
| 07.05.2026 | HS-ORDER-002 | Олена Сидоренко | Помилка | Не створено клієнта | Перевірити телефон/email |
| 07.05.2026 | HS-ORDER-003 | ТОВ «Альфа» | Потребує перевірки | Невідомий тип доставки | Заповнити мапінг доставки |
27. Безпека
Система повинна забезпечити:
- зберігання API login/password тільки у secret storage;
- заборону логування паролів;
- HTTPS для всіх API-запитів;
- перевірку SSL;
- рольову модель доступу;
- окремі права на синхронізацію товарів;
- окремі права на зміну цін;
- окремі права на зміну залишків;
- окремі права на імпорт замовлень;
- окремі права на оновлення статусів;
- журнал усіх дій;
- маскування персональних даних покупців;
- захист від дублювання замовлень;
- контроль доступу до raw API payload.
28. Логування та аудит
Система повинна логувати:
| Подія | Що зберігати |
|---|---|
| Перевірка підключення | URL, результат, час, без пароля. |
| Синхронізація товару | SKU, статус, відповідь API. |
| Зміна ціни | Стара ціна, нова ціна, дата. |
| Зміна залишку | Старий залишок, новий залишок, склад. |
| Імпорт замовлення | external_order_id, номер, статус. |
| Створення клієнта | Телефон/email у замаскованому вигляді. |
| Оновлення статусу | Старий статус, новий статус. |
| Передача ТТН | Номер замовлення, ТТН, служба доставки. |
| Помилка API | Код, текст, raw-відповідь без секретів. |
| Retry | Причина, кількість спроб, результат. |
29. Acceptance Criteria
29.1. Інтеграція
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-1 | Адміністратор створює інтеграцію Horoshop. | Інтеграція зберігається в системі. |
| AC-2 | Адміністратор перевіряє підключення. | Система повертає успішний або помилковий статус. |
| AC-3 | Login/password неправильні. | Система показує AuthError і не виконує синхронізацію. |
29.2. Товари
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-4 | Товар має всі обов'язкові поля. | Він передається в Horoshop. |
| AC-5 | Товар не має SKU. | Він переходить у NEEDS_CORRECTION. |
| AC-6 | Товар не має категорії. | Він не передається і показується менеджеру. |
| AC-7 | Товар успішно передано. | Статус стає SYNCED. |
29.3. Ціни та залишки
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-8 | У K2 ERP змінилась ціна. | Система передає нову ціну в Horoshop. |
| AC-9 | У K2 ERP змінився залишок. | Система передає новий залишок. |
| AC-10 | Залишок став 0. | Товар приховується або позначається як недоступний за правилом. |
29.4. Замовлення
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-11 | На сайті з'явилось нове замовлення. | Воно імпортується в K2 ERP. |
| AC-12 | Замовлення вже імпортоване. | Дубль не створюється. |
| AC-13 | Товар у замовленні не знайдено. | Замовлення переходить у NEEDS_REVIEW. |
| AC-14 | Замовлення імпортовано. | Статус стає IMPORTED. |
29.5. Статуси та ТТН
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-15 | K2 ERP змінила статус замовлення. | Статус передається в Horoshop. |
| AC-16 | У K2 ERP з'явилась ТТН. | ТТН передається в Horoshop. |
| AC-17 | Статус не замаплений. | Замовлення потрапляє в ручну перевірку. |
29.6. Dashboard
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-18 | Керівник відкриває dashboard. | Він бачить товари, замовлення, ціни, залишки, помилки. |
| AC-19 | Є помилки синхронізації. | Вони підсвічуються червоним. |
| AC-20 | Є товари без мапінгу. | Вони підсвічуються помаранчевим. |
| AC-21 | Є нові замовлення. | Вони підсвічуються жовтим. |
30. MVP
До MVP входить:
- створення інтеграції Horoshop;
- перевірка підключення;
- синхронізація категорій або мапінг категорій;
- синхронізація товарів;
- синхронізація цін;
- синхронізація залишків;
- імпорт замовлень;
- імпорт клієнтів із замовлень;
- оновлення статусів замовлень;
- передача ТТН;
- дедублікація;
- retry-механізм;
- журнал подій;
- dashboard API;
- unit-тести;
- mock Horoshop client.
До MVP не входить:
- повна підтримка всіх API-методів Horoshop;
- складна SEO-оптимізація товарів;
- автоматичне створення маркетингових акцій;
- складний UI керування каталогом;
- інтеграція з усіма маркетплейсами;
- автоматичне виправлення товарних даних;
- ML-мапінг категорій і характеристик.
31. Етапи реалізації
Етап 1. Аналіз API Horoshop
- отримати доступ до API;
- створити API login/password в адмінпанелі;
- перевірити base_url;
- перевірити тестовий запит;
- визначити методи товарів;
- визначити методи замовлень;
- визначити методи статусів;
- визначити методи цін і залишків;
- визначити обмеження API.
Етап 2. Базовий Python-сервіс
- створити FastAPI-проєкт;
- налаштувати PostgreSQL;
- створити моделі інтеграцій, товарів, замовлень, мапінгів, подій;
- налаштувати Alembic;
- реалізувати healthcheck.
Етап 3. Horoshop Client
- реалізувати call_api;
- реалізувати check_connection;
- реалізувати get_orders;
- реалізувати update_order_status;
- реалізувати create_or_update_product;
- реалізувати update_price;
- реалізувати update_stock;
- реалізувати обробку помилок.
Етап 4. Товари
- реалізувати Product Mapper;
- реалізувати Category Mapper;
- реалізувати Attribute Mapper;
- реалізувати валідацію;
- реалізувати Product Sync Worker.
Етап 5. Ціни та залишки
- реалізувати Price Engine;
- реалізувати Stock Engine;
- реалізувати чергу оновлення;
- реалізувати історію змін.
Етап 6. Замовлення
- реалізувати Order Import Worker;
- реалізувати дедублікацію;
- реалізувати створення замовлення в K2 ERP;
- реалізувати створення клієнта;
- реалізувати резервування товарів.
Етап 7. Статуси та доставка
- реалізувати мапінг статусів;
- реалізувати Status Export Worker;
- реалізувати передачу ТТН;
- реалізувати журнал статусів.
Етап 8. Dashboard та аудит
- реалізувати dashboard API;
- реалізувати список проблемних товарів;
- реалізувати список проблемних замовлень;
- реалізувати список помилок API;
- реалізувати експорт, якщо потрібно.
Етап 9. Production hardening
- додати rate limiting;
- додати retry policy;
- додати dead letter queue;
- додати alerting;
- додати моніторинг;
- додати резервне копіювання;
- додати безпечне зберігання secret-ів.
32. Ризики
| Ризик | Опис | Як зменшити |
|---|---|---|
| Невірні API-доступи | Інтеграція не працюватиме. | Check-connection і alert адміністратору. |
| Зміна API | Методи або поля можуть змінитись. | Версіонування клієнта і contract-тести. |
| Дублювання замовлень | Повторний імпорт може створити дубль. | Unique constraint по external_order_id + site_id. |
| Продаж відсутнього товару | Залишки не оновились вчасно. | Високий пріоритет Stock Sync. |
| Некоректний товар | Товар не публікується. | Валідація і статус NEEDS_CORRECTION. |
| Незамаплений статус | Статус не передається. | Таблиця status_mappings і ручна перевірка. |
| Недоступність API | Синхронізація зупинена. | Retry, queue, dashboard помилок. |
| Персональні дані в логах | Ризик витоку даних. | Маскування телефону/email. |
33. Відкриті питання
- Чи K2 ERP є головним джерелом товарів?
- Чи потрібно створювати категорії в Horoshop автоматично?
- Чи потрібно імпортувати категорії з Horoshop?
- Які поля товару обов'язкові для MVP?
- Чи є модифікації / варіанти товарів?
- Чи потрібно синхронізувати фото?
- Чи потрібно синхронізувати SEO-поля?
- Чи потрібно синхронізувати акційні ціни?
- Чи потрібно синхронізувати декілька складів?
- Чи потрібно приховувати товар при нульовому залишку?
- Як часто імпортувати замовлення?
- Як часто оновлювати ціни?
- Як часто оновлювати залишки?
- Чи потрібно передавати ТТН у Horoshop?
- Чи потрібно підтримувати кілька сайтів Horoshop?
- Які статуси Horoshop потрібно замапити?
- Які типи оплат і доставок потрібно підтримати?
34. Джерела
- Офіційна документація Horoshop API.
- Центр допомоги Horoshop.
- Сторінка Horoshop API.
- Документація K2 ERP щодо товарів, залишків, цін і замовлень.
- Технічні вимоги конкретного сайту Horoshop.