שגיאה בחישוב הנחה יכולה לעלות לחנות עד 10% מההכנסות — הלקוח רואה מחיר שגוי ומאבד אמון. פיתחנו מנוע שמבטל מצבים כאלה. במשך 5 שנים יישמנו יותר מ-30 פרויקטים עם תמחור מורכב: ממחירים מדורגים ל-B2B ועד מבצעי BOGO לקמעונאות. המתודולוגיה שלנו מבטיחה חישוב מרג'ין שקוף וללא התנגשויות חוקים. פיתוח מערכת הנחות אורך בין 5 ל-9 ימי עבודה — לוח הזמנים תלוי במערך המבצעים ובגודל קטלוג המוצרים. עלות יישום טיפוסית נעה בין $5,000 ל-$15,000, ולקוחות רואים לעיתים קרובות עלייה של 20% בהמרות לאחר ההשקה.
יישום לא תקין של הנחות מוביל לכאוס: מבצע אחד יכול לבטל אחר, הנחה קטגורית עשויה שלא לחול על תת-קטגוריה, וקוד קופון מצטבר במקום שלא צריך. כדי להימנע מכך, אנו בונים היררכיה ברורה ומשתמשים בתבנית Visitor לחישוב.
כיצד להימנע מהתנגשויות בחוקי הנחות?
בכל קטלוג מוצרים קיימים כמה סוגי הנחות במקביל. קריטי להגדיר את סדר העדיפויות ואת חוקי השילוב. אנו משתמשים בסדר העדיפויות הבא:
| עדיפות | סוג הנחה | דוגמה | שילוב |
|---|---|---|---|
| 1 | מחיר מכירה ל-SKU | sale_price על מוצר |
מיושם תמיד |
| 2 | הנחת כמות | 5% עבור 3+ יחידות | ניתן לשילוב |
| 3 | הנחה קטגורית | 10% על מוצרי חשמל | בלעדי |
| 4 | הנחת פלח | 15% ללקוחות VIP | דורס |
| 5 | קוד קופון | WELCOME10 | תלוי בהגדרה |
חוק השילוב מוגדר ברמת המבצע: exclusive (לא מתחבר), stackable (מצטבר עם הקודם), override (מבטל את כל האחרים). זה מבטיח שהמנוע מקבל החלטה חד-משמעית.
מודל נתונים למבצעים
CREATE TABLE promotions (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(30) NOT NULL, -- 'product_discount', 'category_discount', 'volume_discount', 'bundle', 'bogo', 'tiered'
priority INT DEFAULT 0,
stackable BOOLEAN DEFAULT FALSE,
conditions JSONB DEFAULT '{}',
actions JSONB DEFAULT '{}',
starts_at TIMESTAMP,
ends_at TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE
);השדות CREATE TABLE promotions ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, type VARCHAR(30) NOT NULL, -- 'product_discount', 'category_discount', 'volume_discount', 'bundle', 'bogo', 'tiered' priority INT DEFAULT 0, stackable BOOLEAN DEFAULT FALSE, conditions JSONB DEFAULT '{}', actions JSONB DEFAULT '{}', starts_at TIMESTAMP, ends_at TIMESTAMP, is_active BOOLEAN DEFAULT TRUE ); ו-conditions בפורמט JSONB מאפשרים הגדרת חוקים שרירותיים ללא שינוי סכמה. דוגמה לתנאי למבצע "קנה 3 פריטים מהרשימה וקבל 15% הנחה":
{
"conditions": {
"min_qty": 3,
"product_ids": [101, 102, 103],
"user_segments": ["wholesale"]
},
"actions": {
"type": "percent_discount",
"value": 15,
"applies_to": "matching_items"
}
} למה JSONB מתאים לחוקי הנחות?
שדות JSONB conditions ו-actions מאפשרים אחסון חוקים שרירותיים ללא מיגרציות. זה מספק גמישות: ניתן להוסיף סוגי תנאים חדשים (למשל, גורמי מזג אוויר או LTV של לקוח) ללא שינוי סכמה. בנוסף, JSONB ניתן לאינדוקס — שאילתות לחוקים נשארות מהירות גם עם 10,000 מבצעים פעילים.
מנוע יישום הנחות
המחלקה actions עוברת ברצף על חוקים הממוינים לפי עדיפות. אנו משתמשים בתבנית Factory ליצירת אובייקטי חוקים.
class DiscountEngine {
/** @var PromotionRule[] */
private array $rules = [];
public function __construct(Collection $activePromotions) {
foreach ($activePromotions->sortByDesc('priority') as $promo) {
$this->rules[] = PromotionRuleFactory::make($promo);
}
}
public function apply(Cart $cart): DiscountResult {
$result = new DiscountResult($cart);
foreach ($this->rules as $rule) {
if (!$rule->matches($cart)) continue;
if (!$rule->isStackable() && $result->hasDiscount()) continue;
$result->addDiscount($rule->calculate($cart));
if ($rule->isExclusive()) break;
}
return $result;
}
}המנוע שלנו מעבד 1000 חוקים ב-200 אלפיות שנייה, מהיר פי 5 מיישום טיפוסי עם if-else מקונן. כפי שצוין ב-תיעוד Laravel, תבנית Factory מפשטת יצירת אובייקטים ללא תלות במחלקות קונקרטיות.
תמחור לפי כמות
תמחור מדורג הוא בקשה נפוצה ב-B2B וב-B2C. לכל כמות של מוצר מוקצה מחיר משלה, המאוחסן בטבלת { "conditions": { "min_qty": 3, "product_ids": [101, 102, 103], "user_segments": ["wholesale"] }, "actions": { "type": "percent_discount", "value": 15, "applies_to": "matching_items" } } . זה מיישם הנחת כמות שמתרחבת עם גודל ההזמנה.
class VolumePricingRule implements PromotionRule {
public function calculate(Cart $cart): array {
$discounts = [];
foreach ($cart->items as $item) {
$tier = $this->getTier($item->product_id, $item->quantity);
if ($tier) {
$discounts[] = [
'item_id' => $item->id,
'amount' => ($item->price - $tier->price) * $item->quantity,
'label' => "Скидка за объём (×{$item->quantity})",
];
}
}
return $discounts;
}
private function getTier(int $productId, int $qty): ?VolumeTier {
return VolumeTier::where('product_id', $productId)
->where('min_qty', '<=', $qty)
->orderByDesc('min_qty')
->first();
}
} מבצעי בזק עם טיימר ספירה לאחור והצגת הנחה
למבצעים "חמים", מוצג טיימר ספירה לאחור בחזית. מחיר המבצע מועבר דרך API יחד עם זמן הסיום. בעמוד המוצר ובקטלוג, אנו מציגים את המחיר המקורי עם קו חוצה ותגית עם אחוז ההנחה. הפילטר "רק מוזל" בודק את DiscountEngine.
כיצד מתבצע איפוס מטמון?
מבצעים מופעלים ומושבתים אוטומטית באמצעות Laravel Scheduler במרווח של דקה. בעת הפעלה או השבתה, מטמון המחירים מאופס. עבור קטלוגים עם יותר מ-10,000 SKU, אנו משתמשים בתור כדי למנוע עומס שיא על Redis.
תוצרים מסחריים
במסגרת הפיתוח, אנו מספקים:
- עיצוב מודל נתונים למגוון המוצרים שלך (עד 100,000 מוצרים);
- יישום מנוע הנחות עם תמיכה בחוקים שרירותיים דרך JSONB;
- פאנל ניהול למבצעים (CRUD);
- מתזמן אוטומטי להפעלה/השבתה;
- מנגנון איפוס מטמון מחירים;
- דוחות על יעילות מבצעים (מספר יישומים, סכומי הנחות, ערך הזמנה ממוצע);
- תיעוד API ומדריכי פקודות קונסול;
- הדרכת הצוות שלך לשימוש בפאנל הניהול.
שלבי פיתוח
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח חוקי תמחור | יום אחד | מפרט מבצעים |
| עיצוב מודל נתונים | 1-2 ימים | סכמת DB, מיגרציות |
| פיתוח מנוע הנחות | 2-3 ימים | DiscountEngine עובד |
| שילוב פאנל ניהול | 1-2 ימים | CRUD למבצעים |
| בדיקות וניפוי שגיאות | 1-2 ימים | כיסוי בדיקות של 90%+ |
| תיעוד והדרכה | 0.5 יום | תיעוד API, מדריך |
דוגמה לחוק מורכב: הנחה לפלח "סיטונאי" על קטגוריית "מוצרי חשמל" עם כמות מינימלית של 5 יחידות
{
"conditions": {
"user_segments": ["wholesale"],
"categories": ["electronics"],
"min_qty": 5
},
"actions": {
"type": "percent_discount",
"value": 12,
"applies_to": "matching_items"
}
}רוצים לדון במערכת ההנחות שלכם? כתבו לנו — נספק הערכה תוך יום עבודה אחד. הזמינו פיתוח של מערכת הנחות סוהר וקבלו חישוב מרג'ין שקוף.







