Setting up mandatory and optional products in the 1C-Bitrix package

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
    1212
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815
  • 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
    565
  • 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
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    980

Configuration of Mandatory and Optional Products in 1C-Bitrix Bundle

A "Gaming PC" bundle must be sold with a system unit, but keyboard, mouse, and headset are customer's choice — to include or not. This is not just a database flag: different configurations result in different prices, different inventory levels, different order lines. The standard b_catalog_product_set mechanism has an IS_REQUIRED field, but frontend option selection logic isn't implemented out of the box.

IS_REQUIRED field in b_catalog_product_set table

The IS_REQUIRED field accepts values 0 (optional) and 1 (mandatory). This is the only division in the standard structure. For more complex scenarios — for example, "select exactly one item from a group" — the schema needs extension.

We add a GROUP_ID field to b_catalog_product_set via a custom field or via table extension (if update policies allow). Alternative without changing table structure: store grouping in a separate table bl_bundle_option_group:

CREATE TABLE bl_bundle_option_group (
    id           SERIAL PRIMARY KEY,
    bundle_id    INT NOT NULL,
    group_code   VARCHAR(100) NOT NULL,
    group_name   VARCHAR(255),
    min_select   SMALLINT DEFAULT 0,  -- minimum selected from group
    max_select   SMALLINT DEFAULT 1,  -- maximum selected from group
    item_ids     INT[] NOT NULL,      -- array of ITEM_ID from b_catalog_product_set
    UNIQUE (bundle_id, group_code)
);

Frontend selection logic

The bundle page displays mandatory items as non-removable (checked without option to uncheck) and optional items as checkboxes or radio buttons (if max_select = 1 in the group).

When selection changes, the script recalculates the total price. Each optional component price is taken from b_catalog_price by its PRODUCT_ID. It's important to account for user-specific pricing: if a buyer has a price group, check b_catalog_price.CATALOG_GROUP_ID.

function recalcBundle(selectedItems) {
    const basePrice = bundleData.basePrice;  // price of mandatory items
    let optionsTotal = 0;
    selectedItems.forEach(itemId => {
        optionsTotal += bundleData.items[itemId].price;
    });
    document.getElementById('bundle-total').textContent =
        formatPrice(basePrice + optionsTotal);
}

Adding to cart with selected options

When adding to cart, you must submit the bundle composition — only selected items. Via AJAX POST send bundle_id and array selected_items[]. On the server:

// Form composition for cart addition
$bundle = new \Bitrix\Sale\BasketItem();
// Always take mandatory items
$requiredItems = \Bitrix\Catalog\ProductSetTable::getList([
    'filter' => ['SET_ID' => $bundleId, 'IS_REQUIRED' => 1],
])->fetchAll();

// Optional items — only from $_POST['selected_items']
$optionalItems = array_intersect(
    array_column($requiredItems, 'ITEM_ID'),
    (array)$_POST['selected_items']
);

The final composition is passed to \Bitrix\Catalog\Product\Bundle::add(). Each component creates a separate row in b_sale_basket with the SET_PARENT_ID flag.

Inventory with optional items

Bundle availability with optional items: check inventory only for mandatory components. Optional items — separately, for each show "N pieces available" next to the checkbox. If an optional item is out of stock — checkbox is disabled, but the bundle without it can be purchased.

Bundle price with discount

Bundle discount applies only when all optional items are selected (complete configuration) — a common business case. Implemented via cart rule: discount triggers when the set of items in cart matches the full bundle composition. Rule condition: all ITEM_ID from b_catalog_product_set are present in cart with the correct quantities.

What to configure

  • bl_bundle_option_group table for grouping options with min_select/max_select parameters
  • Bundle display component template with checkboxes and price recalculation
  • AJAX handler for cart addition with selected options submission
  • Logic for checking inventory of mandatory and optional items separately
  • Cart rule for full configuration discount