Development of a 1C-Bitrix product set designer

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Development of Product Bundle Constructor on 1C-Bitrix

A customer wants to assemble a gift basket from 5 items of their choice. Or build a computer from compatible components. Or select tires + wheels + TPMS as one order. None of the standard Bitrix components — neither bitrix:catalog.element nor trading offers — solves this task. You need a bundle constructor.

How Constructor Differs from Trading Offers

Trading offers (SKU) in Bitrix are variations of one product: size, color. Bundle constructor is a combination of different products from different categories, assembled into one order with a combined price. Sometimes with a fixed number of items per "slot", sometimes arbitrary choice.

Examples:

  • Gift set: choose 3 from 20 cosmetic options
  • PC constructor: choose processor + motherboard + RAM (with compatibility checking)
  • Combo set: burger + drink + side (one item from each group)

Data Structure

Bundle is a separate entity. Implementation option — infoblock "Bundles" with binding to catalog products.

Bundles infoblock (IBLOCK_SETS):

  • SET_NAME — bundle name
  • SET_DESCRIPTION — description
  • SLOTS — JSON describing bundle slots (multiple string property or separate table)
  • DISCOUNT_TYPE — bundle discount type (percent / fixed)
  • DISCOUNT_VALUE — discount amount

Slot structure in JSON:

{
  "slot_id": 1,
  "name": "Main course",
  "required": true,
  "min_qty": 1,
  "max_qty": 1,
  "product_filter": {
    "IBLOCK_ID": 5,
    "SECTION_ID": [12, 13]
  }
}

For complex projects (PC constructor), store slots in separate custom table:

CREATE TABLE b_set_slots (
    ID          SERIAL PRIMARY KEY,
    SET_ID      INT NOT NULL REFERENCES b_iblock_element(ID),
    SLOT_NAME   VARCHAR(255),
    REQUIRED    BOOLEAN DEFAULT TRUE,
    MIN_QTY     INT DEFAULT 1,
    MAX_QTY     INT DEFAULT 1,
    SORT        INT DEFAULT 100
);

CREATE TABLE b_set_slot_products (
    SLOT_ID     INT NOT NULL REFERENCES b_set_slots(ID),
    PRODUCT_ID  INT NOT NULL REFERENCES b_iblock_element(ID),
    PRIMARY KEY (SLOT_ID, PRODUCT_ID)
);

Selection Logic and Price Calculation

User interface — sequential steps or single page with vertical groups. Each step — product list for given slot.

Final price calculation happens client-side (fast, no server) and is verified server-side (when adding to cart):

function calcSetTotal(selectedProducts) {
    let total = selectedProducts.reduce((sum, p) => sum + p.price * p.qty, 0);
    // apply bundle discount
    if (setDiscount.type === 'percent') {
        total = total * (1 - setDiscount.value / 100);
    } else {
        total = total - setDiscount.value;
    }
    return Math.max(0, total);
}

Adding Bundle to Cart

Standard Bitrix cart API can't add "related" positions that make up a bundle. Custom logic needed:

use Bitrix\Sale\Basket;
use Bitrix\Sale\BasketItem;

$basket = Basket::loadItemsForFUser(CSaleBasket::GetBasketUserID(), SITE_ID);

$setBasketCode = 'set_' . uniqid(); // unique bundle identifier

foreach ($selectedProducts as $product) {
    $item = $basket->createItem('catalog', $product['PRODUCT_ID']);
    $item->setFields([
        'QUANTITY'       => $product['QTY'],
        'PRODUCT_PRICE_ID' => $product['PRICE_ID'],
        'CUSTOM_PRICE'   => 'Y',
        'PRICE'          => $product['PRICE'],
        // custom field for grouping bundle items
        'PROPS'          => [['NAME' => 'SET_CODE', 'VALUE' => $setBasketCode]],
    ]);
}
$basket->save();

In cart, bundle items display grouped by SET_CODE. Cart template (bitrix:sale.basket.basket) needs customization — add grouping by bundle property.

Compatibility Checking

For technical products (PC, auto parts, furniture to size) compatibility checking needed between selected items. Compatibility matrix stored in custom table:

CREATE TABLE b_compatibility_rules (
    ID              SERIAL PRIMARY KEY,
    PRODUCT_A_ID    INT NOT NULL,
    PRODUCT_B_ID    INT NOT NULL,
    COMPATIBLE      BOOLEAN DEFAULT TRUE,
    REASON          TEXT
);

Each product choice triggers AJAX request to PHP controller checking compatibility with already selected items and returns list of incompatible options for UI blocking:

public function checkCompatibilityAction(int $productId, array $selectedIds): array {
    $incompatible = [];
    foreach ($selectedIds as $id) {
        $rule = CompatibilityTable::getRow([
            'filter' => [
                ['PRODUCT_A_ID' => $productId, 'PRODUCT_B_ID' => $id],
                ['COMPATIBLE' => false],
            ]
        ]);
        if ($rule) {
            $incompatible[] = ['product_id' => $id, 'reason' => $rule['REASON']];
        }
    }
    return $incompatible;
}

Saved Configurations

User may want to save assembled bundle and return to it. Save configuration in user account:

CREATE TABLE b_user_set_configs (
    ID          SERIAL PRIMARY KEY,
    USER_ID     INT NOT NULL,
    SET_ID      INT NOT NULL,
    CONFIG_NAME VARCHAR(255),
    PRODUCTS    JSONB NOT NULL,
    DATE_CREATE TIMESTAMP DEFAULT NOW()
);

User sees their configurations in personal account and can add any to cart with one click.

Timeframes

Constructor Type What's Included Timeframe
Simple gift set UI + cart + bundle discount 2–3 weeks
Combo with groups + group slots, required items 3–5 weeks
Constructor with compatibility + compatibility matrix, checks 5–8 weeks
+ Saved configs + personal account, history, sharing +2–3 weeks

Bundle constructor increases average order value: user assembling a kit spends more than one buying a single product. Tool works better the simpler and clearer the selection interface.