Setting up product bundles in 1C-Bitrix

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

Product Bundle Configuration in 1C-Bitrix

A store wants to sell a laptop together with a mouse and bag as a single item with a 10% discount. At first glance — simply a new catalog element. In practice, this breaks inventory tracking, 1C integration, and margin calculations if implemented naively as an ordinary product.

Two approaches to bundle implementation

Approach 1: Bundle as a separate product. A catalog element is created with separate inventory tracking. Simple, but problematic: selling a bundle doesn't reduce the stock of component items. A customer can buy a mouse separately and as part of a bundle, leading to overselling.

Approach 2: Bundle as a virtual product with child items. The bundle in the catalog is an aggregate. Its "stock" is the minimum quantity among component items. When sold, component inventory is reduced accordingly. This is the correct approach.

Bitrix supports the second approach through the TYPE_SET = 4 product type in b_catalog_product. The TYPE field in b_catalog_product accepts values: 1 — simple product, 2 — product with trade offers, 4 — set/bundle.

Bundle storage structure

The "bundle → component" relationship is stored in the b_catalog_product_set table:

  • SET_ID — bundle ID (element ID of the infoblox with TYPE = 4)
  • ITEM_ID — component product ID
  • QUANTITY — quantity in the bundle
  • SORT — display order
  • IS_REQUIRED — mandatory or optional (0/1)

Creating a bundle via API:

// Create SET-type product
$elementId = $iblock->Add([
    'NAME'       => 'Laptop + Mouse + Bag',
    'IBLOCK_ID'  => CATALOG_IBLOCK_ID,
    'ACTIVE'     => 'Y',
]);

// Set SET type
\Bitrix\Catalog\ProductTable::update($elementId, ['TYPE' => \Bitrix\Catalog\ProductTable::TYPE_SET]);

// Add components
\Bitrix\Catalog\ProductSetTable::add([
    'SET_ID'      => $elementId,
    'ITEM_ID'     => $laptopId,
    'QUANTITY'    => 1,
    'SORT'        => 10,
    'IS_REQUIRED' => 1,
]);

\Bitrix\Catalog\ProductSetTable::add([
    'SET_ID'      => $elementId,
    'ITEM_ID'     => $mouseId,
    'QUANTITY'    => 1,
    'SORT'        => 20,
    'IS_REQUIRED' => 1,
]);

Bundle inventory and price calculation

Available bundle quantity is the minimum among components:

$components = \Bitrix\Catalog\ProductSetTable::getList([
    'filter' => ['SET_ID' => $bundleId],
    'select' => ['ITEM_ID', 'QUANTITY'],
])->fetchAll();

$available = PHP_INT_MAX;
foreach ($components as $c) {
    $stock = \Bitrix\Catalog\ProductTable::getByPrimary(
        $c['ITEM_ID'], ['select' => ['QUANTITY']]
    )->fetch();
    $available = min($available, floor((float)$stock['QUANTITY'] / $c['QUANTITY']));
}

Bundle price can be set separately (bundle discount) or calculated as the sum of component prices. In Bitrix, bundle price is stored in b_catalog_price like a regular product. For automatic N% discount on the bundle — use cart rules through b_sale_discount, not manual pricing.

Inventory deduction on sale

When a bundle is added to the cart via the standard component bitrix:sale.basket.basket, Bitrix automatically expands the bundle: lines are created in b_sale_basket for each component with the SET_PARENT_ID flag referring to the bundle row. When the order is placed, inventory is deducted from each component separately.

Problems arise with direct addition through \Bitrix\Sale\Basket::create() — without passing TYPE = TYPE_SET and child items, expansion won't occur. Always use \Bitrix\Catalog\Product\Bundle::add() instead of direct cart manipulation.

What to configure

  • Creating products of type TYPE_SET and populating b_catalog_product_set
  • Calculating available quantity as the minimum among components
  • Cart rule for bundle discount via b_sale_discount
  • Correct bundle addition to cart via Bundle::add()
  • Bundle management interface in the admin panel
  • Verification of 1C synchronization: SET type should transfer correctly during exchange