הטמעת תעדוף ספקים (מחיר/מלאי) לקטלוג רכב
עם קטלוג של 50,000 מוצרים ו-200 ספקים, ניהול עדיפויות ידני מוביל לכאוס: מחירים כפולים, תוכן מיושן ופערי מלאי. ללא אלגוריתמים, אתה מפסיד עד 30% מהמכירות עקב מחירים לא מעודכנים. המערכת מבוססת הכללים שלנו פותרת בעיות אלה על ידי בחירה אוטומטית של הספק הטוב ביותר על פי מחיר, מלאי ואיכות תוכן. היא נבחנה בשטח בפרויקטים עם 50,000+ מוצרים — זמן העיבוד הממוצע למוצר ירד ב-40%, ועלויות ניהול הספקים ירדו ב-30%. עבור קטלוג טיפוסי, זה שווה ערך לחיסכון שנתי של $15,000. זה גם מתרגם לשיפור ממוצע של 2% במרווחי הרווח. עלות ההטמעה מתחילה ב-$4,500 עבור הגדרה סטנדרטית.
בניגוד לבחירת מחיר מינימלי פשוטה, המערכת שלנו מתחשבת בדירוג הספק, שלמות התוכן וזמן האספקה. לדוגמה, אם ספק אחד זול ב-10% אך אינו מספק תיאורים או תמונות, בעוד ספק אחר יקר ב-15% אך מציע חבילה מלאה, המערכת יכולה לבחור בשני כמקור התוכן תוך ביצוע ההזמנה עם הראשון. תעדוף זה מקצר את זמן עיבוד המוצר ב-40%.
רמות עדיפות
התעדוף פועל במספר רמות בו-זמנית. כל רמה פותרת משימה ספציפית: מבחירת תוכן ועד חישוב מלאי.
| רמה | מה היא מגדירה | דוגמה |
|---|---|---|
| תוכן | של מי השם, התיאור והתמונות לשימוש | לספק א' יש את התוכן הטוב ביותר |
| מחיר | איזה מחיר להציג לקונה | המינימלי מבין הספקים עם מלאי |
| הזמנה | היכן לבצע בפועל את ההזמנה | הספק הזול ביותר, ולאחר מכן גיבוי |
| מלאי | כיצד לחשב את סך המלאי | סכום, או רק הספק הראשי |
כיצד להגדיר תעדוף ספקים
ההגדרה מתחילה בהגדרת כללים ברמת הקטגוריה או המותג. בלוח הניהול, אתה בוחר את ההיקף, מדרג ספקים ומגדיר אסטרטגיות. כל השינויים מיושמים ללא פריסה. הגדרה טיפוסית לקטגוריה אחת אורכת שעה.
מודל נתונים להגדרת עדיפות
אנו מאחסנים כללים באמצעות סכמת 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);
}
} אסטרטגיות בחירת מקור תוכן
ניתן לקחת תוכן מהספק הראשון עם הדגל 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) ); או על ידי ניקוד שלמות. הניקוד שלנו מעריך נוכחות של תיאורים, תמונות, מותג ומפרטים.
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;
}
} יישום כללים בזמן אמת
סנכרון המוצרים פועל אוטומטית בעת ייבוא או ידנית. 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); } } שלנו מחיל את הכלל על כל מוצר, מעדכן מחיר, מלאי ומקור תוכן. המערכת יכולה לעבד עד 10,000 מוצרים בשעה — פי 10 מהר יותר ממילוי קטלוג ידני.
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).
- אינטגרציה עם מערכת הייבוא והסנכרון שלך.
- בדיקה על מדגם מוצרים והדרכת מפעילים.
- פריסה וניטור.
צור קשר כדי לקבל הגדרה מותאמת אישית לפרויקט שלך. אנו מבטיחים יציבות ותיעוד מלא.
מה כלול בהטמעה
- סכמת נתונים ומודלים — יום אחד
- PriceResolver ו-ContentSourceResolver — 1–2 ימים
- ProductSyncService וטריגרי סנכרון — יום אחד
- ממשק לוח ניהול לניהול כללים — יומיים
- בדיקות, תיעוד והדרכת מפעילים — יום אחד
סה"כ: 6–7 ימי עבודה במפתח.
אנו צוות עם מומחיות באוטומציה של מסחר אלקטרוני, לאחר שסיפקנו 50+ פרויקטים. אנו מבטיחים יציבות מערכת ותיעוד מלא. הזמן הטמעת תעדוף ספקים — צור קשר להערכה חינמית של הפרויקט שלך.







