Розробка калькулятора комплектації на 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка калькулятора комплектації на 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка калькулятора комплектації на 1С-Бітрікс

Калькулятор комплектації вирішує завдання, яке стандартний каталог 1С-Бітрікс не закриває: користувач конфігурує продукт із набору взаємозалежних опцій, бачить підсумкову ціну в реальному часі та додає в кошик уже зібрану комплектацію. Актуально для автомобільних дилерів, виробників меблів, IT-інтеграторів, постачальників промислового обладнання.

Різниця між варіантами товару та комплектацією

У стандартному каталозі Бітрікс торговельні пропозиції (SKU) покривають фіксовані комбінації атрибутів. Комплектація — динамічне складання, де:

  • опції взаємозалежні: вибір пакету «Люкс» включає опції A, B, C і робить недоступною опцію D
  • підсумкова ціна — сума компонентів, а не фіксована ціна SKU
  • результат — набір товарів для кошика, а не один товар

Модель даних

HL-блок ConfiguratorComponents:

Поле Тип Опис
UF_PRODUCT_ID Int ID базового товару в каталозі
UF_GROUP_CODE String Група опцій (engine, color, wheels)
UF_OPTION_CODE String Код опції
UF_OPTION_NAME String Назва для відображення
UF_BASE_PRICE Float Ціна опції
UF_PRICE_TYPE Enum fixed / percent / delta
UF_INCOMPATIBLE String Коди несумісних опцій (через кому)
UF_REQUIRED_WITH String Обов'язкові при виборі цієї опції

HL-блок ConfiguratorPresets — готові комплектації (Базова, Стандарт, Люкс):

Поле Тип Опис
UF_PRODUCT_ID Int Базовий товар
UF_PRESET_CODE String Код пресету
UF_PRESET_NAME String Назва
UF_OPTIONS_JSON Text JSON з обраними опціями
UF_DISCOUNT_PERCENT Float Знижка на пресет

Ядро конфігуратора: управління залежностями

Центральна проблема конфігуратора — синхронізація залежностей. При виборі опції потрібно: зняти несумісні з інших груп, автоматично додати обов'язкові пов'язані, перерахувати підсумкову ціну.

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 };
    }
}

Пресети як точка входу

Більшість користувачів не конфігурують із нуля. Готові пресети служать відправною точкою з можливістю донастройки. Пресет зі знижкою стимулює обрати готовий пакет замість ручного складання по частинах:

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;
}

Додавання комплектації в кошик Бітрікс

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-блок SavedConfigurations:

$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 або куки з обмеженим терміном дії.

Терміни розробки

Масштаб Опис Термін
Базовий До 5 груп опцій, без залежностей, пресети 5–8 днів
Стандартний 5–15 груп, несумісності, збереження конфігурації 2–3 тижні
Складний 15+ груп, позиції в кошику, історія конфігурацій 4–8 тижнів
Виробничий Інтеграція з 1С для актуальних цін і залишків 2–4 місяці

Головний технічний виклик — граф залежностей при великій кількості опцій. При 50+ варіантах ручне тестування нереально: автоматизовані тести для логіки сумісності потрібні з першого дня розробки.