Менеджер витрачає годину на ручний імпорт: завантажує XML від постачальника A, CSV від B, API від C, звіряє артикули, править ціни, видаляє дублі. Результат — товар з однаковим SKU висить двічі з різними залишками, ціна від одного постачальника, залишок від іншого — картка неконсистентна. Коли постачальників п'ять або десять, ручна обробка перетворюється на нескінченну рутину з неминучими помилками. Уявіть: 10 постачальників, 50 000 товарів, щодня нові ціни та залишки. Якщо завантажувати вручну, один співробітник витрачатиме 40 годин на тиждень тільки на імпорт, а помилки призведуть до втрат замовлень. Мультипостачальниковий імпорт вирішує це одним пайплайном: адаптери нормалізують дані, дедублікатор прибирає повтори, мержер збирає підсумкову картку за пріоритетами. Автоматизація скорочує час обробки до 5 хвилин на 10 000 товарів (згідно з дослідженням Invesp), а помилки знижуються з 5–10% до <0.1%. Економія операційних витрат — до 80% часу на кожному постачальнику, що при типовому навантаженні становить $500–5000 на місяць економії. Наш досвід — понад 5 років таких інтеграцій; ми реалізували пайплайни для каталогів від 10 000 до 500 000 товарів, виконали понад 50 проєктів. Реалізуємо під ключ із документацією та навчанням команди.
Чому мультипостачальниковий імпорт краще ручного?
Автоматизація краще ручного імпорту в 10 разів за швидкістю і в 50 разів за точністю. Автоматизація також виключає людський фактор при звірці артикулів, що особливо критично для товарів із безліччю варіацій. Порівняння ключових параметрів:
| Параметр | Ручний імпорт | Автоматичний імпорт |
|---|---|---|
| Час на 10 000 товарів | ~50 хвилин | ~5 хвилин |
| Частота помилок | 5–10% | <0.1% |
| Пропуск незмінених | Ні | Так (хешування) |
| Урахування пріоритетів постачальників | Вручну, з помилками | Автоматично за правилами |
Як побудувати пайплайн імпорту?
- Визначити постачальників та формати їх даних.
- Реалізувати адаптери, що реалізують
SupplierAdapterInterface. - Налаштувати нормалізацію: привести дані до єдиного DTO.
- Запустити дедублікацію за зовнішніми ID та SKU.
- Налаштувати правила мержу та пріоритетів.
- Підключити моніторинг та алерти.
Кожен етап типового проєкту займає від 1 до 2 днів, у сумі — 5–7 робочих днів для трьох постачальників. Гарантована якість: усі рішення сертифіковані та пройшли ретельне тестування.
Архітектура та реалізація
Постачальник A (XML) ─┐ Постачальник B (CSV) ─┤─► Normalizer ─► Deduplicator ─► Merger ─► Catalog DB Постачальник C (API) ─┘ Кожен постачальник — окремий адаптер, що реалізує SupplierAdapterInterface. Пайплайн уніфікований: нормалайзер приводить дані до DTO, дедублікатор звіряє за зовнішніми ID та SKU, мержер застосовує правила пріоритету.
Деталі дедублікації
Дедублікація виконується за зовнішнім ID та SKU. Якщо товар уже існує, але дані змінилися (перевірка за хешем), запис оновлюється. Якщо не змінилися — пропускається. Це дозволяє обробляти мільйони записів без перезапису.Механізм розв'язання конфліктів
Ключове рішення — окреме зберігання вихідних даних постачальника та підсумкової картки. Вихідні зберігаються в supplier_products з унікальним ключем (supplier_id, external_id). Підсумкові — в products з прив'язкою до первинного постачальника.
CREATE TABLE supplier_products ( id BIGSERIAL PRIMARY KEY, supplier_id INT NOT NULL REFERENCES suppliers(id), external_id VARCHAR(255) NOT NULL, sku VARCHAR(255), barcode VARCHAR(50), name TEXT NOT NULL, price NUMERIC(12,2), stock INT DEFAULT 0, attributes JSONB DEFAULT '{}', raw_data JSONB, imported_at TIMESTAMP NOT NULL, hash VARCHAR(64), UNIQUE(supplier_id, external_id) ); CREATE TABLE products ( id BIGSERIAL PRIMARY KEY, master_sku VARCHAR(255) UNIQUE, name TEXT, price NUMERIC(12,2), stock INT, primary_supplier_id INT REFERENCES suppliers(id), merged_from INT[], updated_at TIMESTAMP ); Приклад адаптера для XML
interface SupplierAdapterInterface { public function fetch(): Generator; public function getSupplierId(): int; } class SupplierProductDTO { public function __construct( public readonly string $externalId, public readonly string $name, public readonly float $price, public readonly int $stock, public readonly ?string $sku = null, public readonly ?string $barcode = null, public readonly array $attributes = [] ) {} } class XmlSupplierAdapter implements SupplierAdapterInterface { public function fetch(): Generator { $reader = new XMLReader(); $reader->open($this->feedUrl); while ($reader->read()) { if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'item') { $node = new SimpleXMLElement($reader->readOuterXML()); yield new SupplierProductDTO( externalId: (string) $node->id, name: (string) $node->name, price: (float) $node->price, stock: (int) $node->quantity, sku: (string) $node->article ?: null ); } } } public function getSupplierId(): int { return $this->supplierId; } } Імпортний сервіс та черги
class SupplierImportService { public function import(SupplierAdapterInterface $adapter): ImportResult { $supplierId = $adapter->getSupplierId(); $result = new ImportResult(); DB::transaction(function () use ($adapter, $supplierId, $result) { foreach ($adapter->fetch() as $dto) { $hash = hash('sha256', serialize($dto)); $existing = SupplierProduct::where(['supplier_id' => $supplierId, 'external_id' => $dto->externalId])->first(); if ($existing && $existing->hash === $hash) { $result->skipped++; continue; } SupplierProduct::updateOrCreate( ['supplier_id' => $supplierId, 'external_id' => $dto->externalId], ['name' => $dto->name, 'price' => $dto->price, 'stock' => $dto->stock, 'sku' => $dto->sku, 'imported_at' => now(), 'hash' => $hash] ); $result->upserted++; } SupplierProduct::where('supplier_id', $supplierId)->where('imported_at', '<', now()->subMinutes(30))->update(['stock' => 0]); }); return $result; } } class ImportSupplierJob implements ShouldQueue { public int $timeout = 1800; public int $tries = 3; public function handle(SupplierImportService $service): void { $adapter = SupplierAdapterFactory::make($this->supplier); $result = $service->import($adapter); Log::info("Supplier {$this->supplier->name} imported", $result->toArray()); if ($this->isLastActiveImport()) { MergeProductsJob::dispatch(); } } } Кожен постачальник запускається окремим job'ом за розкладом. Нормалізація використовує потокову обробку PHP Generators, що економить пам'ять при десятках тисяч товарів.
Моніторинг та алерти
Важливі метрики: кількість імпортованих/пропущених записів, відсоток цінових аномалій (зміна >20% — сигнал помилки постачальника), час виконання імпорту. При перевищенні порогу спрацьовує сповіщення. Ключові метрики: кількість нових, оновлених, видалених товарів; відсоток помилок при парсингу; час виконання імпорту; аномалії цін (зміна >20% за один імпорт). При перевищенні порогів надсилається алерт у Telegram або Slack.
Що ви отримуєте при впровадженні?
- Схема даних та список адаптерів.
- Робочі адаптери з нормалізацією для ваших постачальників.
- Логіка пріоритезації та злиття товарів.
- Черги, логування та алерти в Telegram/Slack.
- Повна документація з експлуатації.
- Навчання команди (до 3 годин).
- 30-денна безкоштовна підтримка.
Усе це — під ключ за 5–7 робочих днів. Кожен додатковий адаптер — від 0.5 до 2 днів. Оцініть економію для вашого бізнесу — зв'яжіться з нами для безкоштовної консультації. Напишіть нам, і ми оцінимо ваш проект безкоштовно. Замовте впровадження системи імпорту — забудьте про рутинне завантаження товарів.







