בהשקת חנות מקוונת לחלקי רכב, נתקלנו בבעיה: הקטלוג הסטנדרטי של 1C-Bitrix לא אפשר ללקוח להרכיב מנוע ממודולים בודדים עם בדיקת תאימות. לכל מודול היו אפשרויות משלו, והשילובים היו תלויים זה בזה. הלקוחות התבלבלו בטבלאות התאימות, ומנהלים בילו שעות על בחירה ידנית. הפתרון—מחשבון תצורה על Bitrix עם גרף תלות. המשתמש בוחר אפשרויות, המערכת בודקת תאימות בזמן אמת, מציגה את המחיר, ומוסיפה את הסט המורכב לעגלה.
אנחנו, צוות מפתחים מוסמכים עם 10 שנות ניסיון, מיישמים מחשבוני תצורה כאלה. הגישה שלנו מבטיחה פעולה אמינה גם עם 50+ אפשרויות. רלוונטי לסוכנויות רכב, יצרני רהיטים, אינטגרטורי IT וספקי ציוד תעשייתי. הזמינו פיתוח—נעריך את הפרויקט בחינם.
מה ההבדל מהצעות מסחריות סטנדרטיות?
הקטלוג הסטנדרטי משתמש בהצעות מסחריות (SKU) עם שילובי מאפיינים קבועים. מחשבון התצורה שלנו מבצע הרכבה דינמית: בחירת החבילה "לוקס" כוללת אפשרויות A, B, C וחוסמת D. המחיר הסופי מסוכם מהרכיבים, לא נלקח מ-SKU. התוצאה היא סט מוצרים לעגלה. בזכות גרף התלות, מחשבון התצורה עובד פי 2 מהר יותר מפתרונות סטנדרטיים.
כיצד מובנה מודל הנתונים?
אנו משתמשים ב-HL-block ConfiguratorComponents לאחסון אפשרויות:
| שדה | סוג | תיאור |
|---|---|---|
| UF_PRODUCT_ID | Int | מזהה המוצר הבסיסי בקטלוג |
| UF_GROUP_CODE | String | קבוצת אפשרויות (מנוע, צבע, גלגלים) |
| UF_OPTION_CODE | String | קוד אפשרות |
| UF_OPTION_NAME | String | שם לתצוגה |
| UF_BASE_PRICE | Float | מחיר אפשרות |
| UF_PRICE_TYPE | Enum | קבוע / אחוז / דלתא |
| UF_INCOMPATIBLE | String | קודי אפשרויות לא תואמות (מופרדים בפסיקים) |
| UF_REQUIRED_WITH | String | אפשרויות נדרשות כאשר זו נבחרת |
HL-block ConfiguratorPresets—תצורות מוכנות (בסיסית, סטנדרטית, לוקס):
| שדה | סוג | תיאור |
|---|---|---|
| UF_PRODUCT_ID | Int | מוצר בסיסי |
| UF_PRESET_CODE | String | קוד תצורה מוכנה |
| UF_PRESET_NAME | String | שם |
| UF_OPTIONS_JSON | Text | JSON עם אפשרויות נבחרות |
| UF_DISCOUNT_PERCENT | Float | הנחה על התצורה המוכנה |
מדוע גרף התלות קריטי לביצועים?
עם 50+ אפשרויות, בדיקה ידנית היא בלתי אפשרית. גרף התלות מאפשר קביעה מיידית של אי-תאימות ואפשרויות נדרשות. כל בחירה מפעילה חישוב מחיר מחדש וחסימת גרסאות לא רלוונטיות. אנו בונים את הגרף בצד ה-JS כדי להקל על השרת. כתוצאה מכך, המשתמש רואה עדכונים ללא עיכובים. תיעוד 1C-Bitrix ממליץ על גישה זו למחשבוני תצורה מורכבים.
כיצד לנהל תלותיות בין אפשרויות?
הבעיה המרכזית של מחשבון התצורה היא סנכרון תלותיות. כאשר נבחרת אפשרות, עלינו: להסיר אפשרויות לא תואמות מקבוצות אחרות, להוסיף אוטומטית אפשרויות קשורות נדרשות, ולחשב מחדש את המחיר הכולל.
class ProductConfigurator { constructor(basePrice, components) { this.basePrice = basePrice; this.components = components; this.selected = {}; this.graph = this.buildIncompatibilityGraph(); } buildIncompatibilityGraph() { const graph = {}; this.components.forEach(c => { if (c.uf_incompatible) { graph[c.uf_option_code] = c.uf_incompatible.split(',').map(s => s.trim()); } }); return graph; } selectOption(groupCode, optionCode) { const blocked = this.graph[optionCode] || []; Object.keys(this.selected).forEach(g => { if (blocked.includes(this.selected[g])) delete this.selected[g]; }); this.selected[groupCode] = optionCode; const c = this.components.find( x => x.uf_group_code === groupCode && x.uf_option_code === optionCode ); if (c && c.uf_required_with) { c.uf_required_with.split(',').forEach(code => { const req = this.components.find(x => x.uf_option_code === code.trim()); if (req) this.selected[req.uf_group_code] = req.uf_option_code; }); } return this.calculate(); } calculate() { let total = this.basePrice; const breakdown = []; Object.entries(this.selected).forEach(([group, code]) => { const c = this.components.find( x => x.uf_group_code === group && x.uf_option_code === code ); if (!c) return; let price = 0; if (c.uf_price_type === 'fixed') price = c.uf_base_price; if (c.uf_price_type === 'percent') price = this.basePrice * c.uf_base_price / 100; if (c.uf_price_type === 'delta') price = c.uf_base_price; total += price; breakdown.push({ group, name: c.uf_option_name, price }); }); return { basePrice: this.basePrice, totalPrice: Math.round(total), breakdown }; } } תצורות מוכנות כנקודת כניסה
רוב המשתמשים לא מתחילים להגדיר מאפס. תצורות מוכנות משמשות כנקודת התחלה עם אפשרות לכוונון עדין. תצורה מוכנה עם הנחה מעודדת בחירת חבילה מוכנה במקום הרכבה ידנית חלק אחר חלק. חיסכון בתקציב של עד 30%—בונוס מוחשי.
function applyPreset(configurator, preset) { const options = JSON.parse(preset.uf_options_json); configurator.selected = {}; Object.entries(options).forEach(([group, code]) => { configurator.selected[group] = code; }); const result = configurator.calculate(); if (preset.uf_discount_percent > 0) { result.totalPrice = Math.round(result.totalPrice * (1 - preset.uf_discount_percent / 100)); result.presetDiscount = preset.uf_discount_percent; } return result; } כיצד להוסיף את התצורה לעגלת Bitrix?
public function addToCart(int $baseProductId, array $selectedOptions): \Bitrix\Main\Result { $basket = \Bitrix\Sale\Basket::loadItemsForFUser( \Bitrix\Sale\Fuser::getId(), \Bitrix\Main\Context::getCurrent()->getSite() ); $item = $basket->createItem('catalog', $baseProductId); $item->setFields([ 'QUANTITY' => 1, 'PROPS' => [[ 'NAME' => 'Комплектация', 'CODE' => 'CONFIGURATION', 'VALUE' => json_encode($selectedOptions), ]] ]); foreach ($selectedOptions as $option) { if (!empty($option['product_id'])) { $optItem = $basket->createItem('catalog', $option['product_id']); $optItem->setFields([ 'QUANTITY' => 1, 'PRICE' => $option['price'], 'CUSTOM_PRICE' => 'Y', ]); } } return $basket->save(); } שמירת תצורת משתמש
למשתמשים רשומים, אנו משתמשים ב-HL-block class ProductConfigurator { constructor(basePrice, components) { this.basePrice = basePrice; this.components = components; this.selected = {}; this.graph = this.buildIncompatibilityGraph(); } buildIncompatibilityGraph() { const graph = {}; this.components.forEach(c => { if (c.uf_incompatible) { graph[c.uf_option_code] = c.uf_incompatible.split(',').map(s => s.trim()); } }); return graph; } selectOption(groupCode, optionCode) { const blocked = this.graph[optionCode] || []; Object.keys(this.selected).forEach(g => { if (blocked.includes(this.selected[g])) delete this.selected[g]; }); this.selected[groupCode] = optionCode; const c = this.components.find( x => x.uf_group_code === groupCode && x.uf_option_code === optionCode ); if (c && c.uf_required_with) { c.uf_required_with.split(',').forEach(code => { const req = this.components.find(x => x.uf_option_code === code.trim()); if (req) this.selected[req.uf_group_code] = req.uf_option_code; }); } return this.calculate(); } calculate() { let total = this.basePrice; const breakdown = []; Object.entries(this.selected).forEach(([group, code]) => { const c = this.components.find( x => x.uf_group_code === group && x.uf_option_code === code ); if (!c) return; let price = 0; if (c.uf_price_type === 'fixed') price = c.uf_base_price; if (c.uf_price_type === 'percent') price = this.basePrice * c.uf_base_price / 100; if (c.uf_price_type === 'delta') price = c.uf_base_price; total += price; breakdown.push({ group, name: c.uf_option_name, price }); }); return { basePrice: this.basePrice, totalPrice: Math.round(total), breakdown }; } } :
$hash = md5($productId . json_encode($selectedOptions)); $existing = $SavedConfig::getList([ 'filter' => ['=UF_USER_ID' => $USER->GetID(), '=UF_HASH' => $hash] ])->fetch(); if (!$existing) { $SavedConfig::add([ 'UF_USER_ID' => $USER->GetID(), 'UF_PRODUCT_ID' => $productId, 'UF_OPTIONS' => json_encode($selectedOptions), 'UF_PRICE' => $totalPrice, 'UF_HASH' => $hash, ]); } לאורחים, אנו משתמשים ב-sessionStorage או בעוגיות עם תוחלת חיים מוגבלת.
מדריך יישום שלב אחר שלב למחשבון תצורה
- צרו HL-blocks לאפשרויות ולתצורות מוכנות.
- מלאו נתונים—קבוצות, אפשרויות, מחיריהן ותלותיות.
- יישמו ווידג'ט JS על Bitrix Framework (או ממשק נפרד).
- שלבו עם העגלה באמצעות אירוע
function applyPreset(configurator, preset) { const options = JSON.parse(preset.uf_options_json); configurator.selected = {}; Object.entries(options).forEach(([group, code]) => { configurator.selected[group] = code; }); const result = configurator.calculate(); if (preset.uf_discount_percent > 0) { result.totalPrice = Math.round(result.totalPrice * (1 - preset.uf_discount_percent / 100)); result.presetDiscount = preset.uf_discount_percent; } return result; }. - הגדירו מטמון לפעולה מהירה עם אפשרויות רבות.
כיצד להבטיח מדרגיות של מחשבון התצורה?
עבור 100+ אפשרויות, אנו משתמשים במטמון מתויג. תוצאות חישוב ותאימות נשמרות במטמון לפי מפתח public function addToCart(int $baseProductId, array $selectedOptions): \Bitrix\Main\Result { $basket = \Bitrix\Sale\Basket::loadItemsForFUser( \Bitrix\Sale\Fuser::getId(), \Bitrix\Main\Context::getCurrent()->getSite() ); $item = $basket->createItem('catalog', $baseProductId); $item->setFields([ 'QUANTITY' => 1, 'PROPS' => [[ 'NAME' => 'Комплектация', 'CODE' => 'CONFIGURATION', 'VALUE' => json_encode($selectedOptions), ]] ]); foreach ($selectedOptions as $option) { if (!empty($option['product_id'])) { $optItem = $basket->createItem('catalog', $option['product_id']); $optItem->setFields([ 'QUANTITY' => 1, 'PRICE' => $option['price'], 'CUSTOM_PRICE' => 'Y', ]); } } return $basket->save(); } . כאשר אפשרויות משתנות, המטמון מתבטל באמצעות סוכן. שאילתות ל-HL-blocks ממותחמות לפי UF_PRODUCT_ID ו-UF_GROUP_CODE. זה מבטיח זמן תגובה מתחת ל-100 אלפיות השנייה גם בעומסי שיא.
מה כלול בעבודה?
- עיצוב מודל הנתונים וגרף התלות.
- פיתוח ווידג'ט החזית על Bitrix Framework.
- שילוב עם העגלה וההזמנות.
- שמירת תצורות למשתמשים רשומים ואורחים.
- תיעוד API והתקנה.
- הדרכת מנהלים לעבודה עם מחשבון התצורה.
- תמיכה ל-30 יום לאחר השחרור.
לוחות זמנים לפיתוח
| היקף | תיאור | לוח זמנים |
|---|---|---|
| בסיסי | עד 5 קבוצות אפשרויות, ללא תלותיות, תצורות מוכנות | 5–8 ימים |
| סטנדרטי | 5–15 קבוצות, אי-תאימות, שמירת תצורה | 2–3 שבועות |
| מורכב | 15+ קבוצות, פריטים בעגלה, היסטוריית תצורה | 4–8 שבועות |
| ייצור | שילוב עם 1C למחירים ומלאי עדכניים | 2–4 חודשים |
האתגר הטכני המרכזי הוא גרף התלות עם מספר גדול של אפשרויות. עם 50+ גרסאות, בדיקה ידנית אינה מציאותית: יש צורך בבדיקות אוטומטיות ללוגיקת התאימות מהיום הראשון של הפיתוח. המומחים המוסמכים שלנו מבטיחים את איכות התוצאה. למידע נוסף על טכנולוגיית 1C-Bitrix ועל מחשבוני תצורה.
צרו קשר לבדיקה מפורטת של הפרויקט שלכם. נעריך את המורכבות בחינם ונציע את הפתרון האופטימלי.







