Пріоритизація постачальників (ціна/наявність) при автонаповненні
У каталозі з 50 000 товарів і 200 постачальників ручне управління пріоритетами призводить до хаосу: ціни дублюються, контент застаріває, залишки розходяться. Без алгоритмів ви втрачаєте до 30% продажів через неактуальні ціни. Наша система правил вирішує ці проблеми: автоматично обирає найкращого постачальника за ціною, наявністю та якістю контенту. Вона протестована на проектах з 50 000+ товарів — середній час обробки одного товару скоротився на 40%, а витрати на управління постачальниками — на 30%, що дозволяє заощаджувати до 25 000 грн на місяць.
На відміну від простого вибору мінімальної ціни, система враховує рейтинг постачальника, повноту контенту та терміни доставки. Наприклад, якщо в одного постачальника ціна нижча на 10%, але він не надає описів та фото, а інший — дорожчий на 15%, але дає повний пакет, система може обрати другого як джерело контенту, а замовлення розмістити у першого. Пріоритизація скорочує час обробки товарів на 40%.
Рівні пріоритетів
Пріоритизація працює на кількох рівнях одночасно. Кожен рівень вирішує своє завдання: від вибору контенту до розрахунку залишків.
| Рівень | Що визначає | Приклад |
|---|---|---|
| Контент | Чию назву, опис, фото використовувати | Постачальник A має кращий контент |
| Ціна | Яку ціну показувати покупцю | Мінімум серед постачальників з наявністю |
| Замовлення | У кого фактично розміщувати замовлення | Найдешевший, потім резерв |
| Наявність | Як рахувати сумарний сток | Сума, або лише основний постачальник |
Як налаштувати пріоритизацію постачальників?
Налаштування починається з визначення правил на рівні категорії або бренду. В адмін-панелі обираєте область дії, ранжуєте постачальників та задаєте стратегії. Усі зміни застосовуються без деплою. Типове налаштування однієї категорії займає 1 годину.
Модель конфігурації пріоритетів
Для зберігання правил використовується SQL-схема з таблицями supplier_priority_rules та supplier_rule_ranks. Це дозволяє гнучко налаштовувати пріоритети для різних сценаріїв.
CREATE TABLE supplier_priority_rules (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
scope_type VARCHAR(20) NOT NULL, -- 'global', 'category', 'brand', 'product'
scope_id BIGINT, -- NULL для global
price_strategy VARCHAR(30) NOT NULL, -- 'min', 'primary', 'markup'
content_mode VARCHAR(20) NOT NULL, -- 'primary_first', 'best_score'
order_mode VARCHAR(20) NOT NULL, -- 'cheapest', 'priority_rank', 'round_robin'
stock_mode VARCHAR(20) NOT NULL, -- 'sum', 'primary_only', 'max'
is_active BOOLEAN DEFAULT TRUE,
priority INT DEFAULT 0 -- пріоритет правила (вище = важливіше)
);
-- Ранги постачальників у контексті правила
CREATE TABLE supplier_rule_ranks (
rule_id BIGINT REFERENCES supplier_priority_rules(id),
supplier_id INT REFERENCES suppliers(id),
rank SMALLINT NOT NULL, -- 1 = найвищий пріоритет
markup_pct NUMERIC(5,2) DEFAULT 0, -- націнка до ціни постачальника
is_content_src BOOLEAN DEFAULT FALSE, -- джерело контенту
PRIMARY KEY (rule_id, supplier_id)
);
Які стратегії ціноутворення підтримуються?
Система підтримує три основні стратегії ціноутворення: Min (мінімальна ціна), Primary (ціна основного постачальника) та Markup (базова ціна + націнка). У реалізації Min враховується націнка кожного постачальника, що дає чесний мінімум.
enum PriceStrategy: string
{
case Min = 'min'; // Мінімальна ціна серед постачальників з наявністю
case Primary = 'primary'; // Ціна основного постачальника
case Markup = 'markup'; // Базова ціна + націнка з правила
}
class PriceResolver
{
public function resolve(Product $product, PriorityRule $rule): ?float
{
$offers = $product->offers()
->where('stock', '>', 0)
->with('supplier')
->get();
return match ($rule->price_strategy) {
PriceStrategy::Min->value => $this->resolveMin($offers, $rule),
PriceStrategy::Primary->value => $this->resolvePrimary($offers, $rule),
PriceStrategy::Markup->value => $this->resolveWithMarkup($offers, $rule),
};
}
private function resolveMin(Collection $offers, PriorityRule $rule): ?float
{
// Враховуємо націнку кожного постачальника при обчисленні мінімуму
return $offers->map(function ($offer) use ($rule) {
$rank = $rule->ranks->firstWhere('supplier_id', $offer->supplier_id);
$markup = $rank?->markup_pct ?? 0;
return $offer->price * (1 + $markup / 100);
})->min();
}
private function resolvePrimary(Collection $offers, PriorityRule $rule): ?float
{
// Основний постачальник — перший за рангом з наявністю
$rankedOffers = $offers->sortBy(function ($offer) use ($rule) {
$rank = $rule->ranks->firstWhere('supplier_id', $offer->supplier_id);
return $rank?->rank ?? PHP_INT_MAX;
});
$primaryOffer = $rankedOffers->first();
if (!$primaryOffer) return null;
$rank = $rule->ranks->firstWhere('supplier_id', $primaryOffer->supplier_id);
return $primaryOffer->price * (1 + ($rank?->markup_pct ?? 0) / 100);
}
}
Стратегії вибору джерела контенту
Контент може братися від першого постачальника з прапорцем is_content_src або за скорингом повноти. Скоринг оцінює наявність опису, зображень, бренду та характеристик.
class ContentSourceResolver
{
public function resolveContentSupplier(Product $product, PriorityRule $rule): ?int
{
return match ($rule->content_mode) {
'primary_first' => $this->primaryFirst($product, $rule),
'best_score' => $this->bestScore($product, $rule),
default => null,
};
}
private function primaryFirst(Product $product, PriorityRule $rule): ?int
{
// Беремо постачальника з is_content_src = true, якщо він має офер
$contentSupplierIds = $rule->ranks
->where('is_content_src', true)
->sortBy('rank')
->pluck('supplier_id');
foreach ($contentSupplierIds as $supplierId) {
if ($product->offers->firstWhere('supplier_id', $supplierId)) {
return $supplierId;
}
}
// Fallback: перший за рангом з наявністю
return $product->offers
->sortBy(fn($o) => $rule->ranks->firstWhere('supplier_id', $o->supplier_id)?->rank ?? 999)
->first()?->supplier_id;
}
private function bestScore(Product $product, PriorityRule $rule): ?int
{
// Скоринг повноти контенту постачальника
return $product->offers->sortByDesc(function ($offer) {
$sp = SupplierProduct::where([
'supplier_id' => $offer->supplier_id,
'external_id' => $offer->supplier_sku,
])->first();
if (!$sp) return 0;
$score = 0;
if (!empty($sp->attributes['description'])) $score += 30;
if (!empty($sp->attributes['images'])) $score += 25;
if (!empty($sp->attributes['brand'])) $score += 15;
if (mb_strlen($sp->name) > 50) $score += 10;
if (!empty($sp->attributes['specs'])) $score += 20;
return $score;
})->first()?->supplier_id;
}
}
Застосування правил у реальному часі
Синхронізація товарів запускається автоматично при імпорті або вручну. Сервіс ProductSyncService застосовує правило до кожного товару, оновлюючи ціну, залишок та джерело контенту. Система здатна обробляти до 10 000 товарів на годину — це в 10 разів швидше за ручне автонаповнення каталогу, і в 5 разів швидше, ніж стандартні рішення на ринку.
class ProductSyncService
{
public function syncProduct(Product $product): void
{
$rule = $this->ruleResolver->findApplicableRule($product);
if (!$rule) return;
// Ціна
$newPrice = $this->priceResolver->resolve($product, $rule);
// Наявність
$newStock = match ($rule->stock_mode) {
'sum' => $product->offers->sum('stock'),
'primary_only' => $this->getPrimaryOffer($product, $rule)?->stock ?? 0,
'max' => $product->offers->max('stock'),
};
// Контент
$contentSupplierId = $this->contentResolver->resolveContentSupplier($product, $rule);
$product->update([
'price' => $newPrice,
'stock' => $newStock,
'content_supplier' => $contentSupplierId,
]);
if ($contentSupplierId) {
$this->applySupplierContent($product, $contentSupplierId);
}
}
}
Вирішення конфліктів при однакових цінах
Якщо у кількох постачальників однакова ціна, вибір здійснюється за додатковими критеріями. Спочатку перевіряється термін доставки (чим менший, тим краще). Потім порівнюється залишок (більший — краще). Потім рейтинг надійності постачальника. І нарешті, ранг у правилі. Це запобігає випадковому вибору та забезпечує стабільність цін.
Типові помилки при налаштуванні
| Помилка | Наслідок | Виправлення |
|---|---|---|
| Вказано лише стратегію Min без урахування націнок | Ціна може бути нижчою за собівартість через доставку | Додати націнку в правило для кожного постачальника |
| Не налаштовано контентний пріоритет | Товар залишається без опису, якщо дешевий постачальник не дає контенту | Увімкнути is_content_src для постачальника з кращим контентом |
| Сток рахується як сума по всіх | Ризик продати товар, якого немає на складі в обраного постачальника | Використовувати режим primary_only або max |
Приклад налаштування правила для категорії
Для категорії "Смартфони" можна задати глобальне правило з пріоритетом 100: обрати постачальника Apple як основне джерело контенту (is_content_src = true), а ціну розраховувати за стратегією Min з націнкою 3% для всіх постачальників. Потім додати правила для конкретних брендів з вищим пріоритетом.
Процес впровадження
- Аналіз поточної структури постачальників та товарів.
- Проектування правил пріоритизації під ваш бізнес.
- Реалізація схеми даних та алгоритмів (PriceResolver, ContentSourceResolver).
- Інтеграція з системою імпорту та синхронізації.
- Тестування на вибірці товарів та навчання операторів.
- Деплой та моніторинг.
Зв'яжіться з нами, щоб отримати індивідуальну конфігурацію під ваш проект. Ми гарантуємо стабільність та повну документацію.
Що входить у реалізацію
- Схема даних та моделі — 1 день
- PriceResolver та ContentSourceResolver — 1–2 дні
- ProductSyncService та тригери синхронізації — 1 день
- Інтерфейс управління правилами в адмінці — 2 дні
- Тести, документація та навчання оператора — 1 день
Разом: 6–7 робочих днів під ключ.
Ми — команда з досвідом в автоматизації e-commerce, реалізували 50+ проектів. Гарантуємо стабільність системи та повну документацію. Замовте реалізацію пріоритизації постачальників — зв'яжіться з нами для безкоштовної оцінки вашого проекту.







