Настройка массовой привязки торговых предложений 1С-Битрикс
Торговые предложения в Битрикс — это элементы отдельного инфоблока, каждый из которых привязан к родительскому товару через поле CML2_LINK (хранится в b_iblock_element_property). Когда у вас тысячи SKU и эта привязка нарушена — предложения не отображаются в карточке товара, цены и остатки не показываются. Задача массовой привязки возникает при миграции с другой платформы, импорте из 1С с ошибками или ручном создании базы.
Как работает связь товар — торговое предложение
Инфоблок каталога содержит основные товары. Для торговых предложений создаётся связанный инфоблок — его ID указывается в поле OFFERS_IBLOCK_ID в b_iblock.ID. Связь объявляется через b_iblock_fields или через интерфейс: Инфоблоки → Типы инфоблоков → Торговый каталог → Редактировать.
Каждое торговое предложение имеет свойство с кодом CML2_LINK (тип «Привязка к элементам») — значение в b_iblock_element_property.VALUE соответствует ID родительского товара.
-- Проверить, у скольких предложений не заполнена привязка:
SELECT COUNT(*)
FROM b_iblock_element ie
WHERE ie.IBLOCK_ID = {offers_iblock_id}
AND NOT EXISTS (
SELECT 1 FROM b_iblock_element_property iep
INNER JOIN b_iblock_property ip ON ip.ID = iep.IBLOCK_PROPERTY_ID
WHERE iep.IBLOCK_ELEMENT_ID = ie.ID
AND ip.CODE = 'CML2_LINK'
AND iep.VALUE IS NOT NULL
);
Нахождение ID свойства CML2_LINK
$offersIblockId = 11; // ID инфоблока торговых предложений
$propRes = \Bitrix\Iblock\PropertyTable::getList([
'filter' => ['IBLOCK_ID' => $offersIblockId, 'CODE' => 'CML2_LINK'],
'select' => ['ID'],
])->fetch();
$linkPropertyId = $propRes['ID'];
Массовая привязка по XML_ID
Самый распространённый сценарий: у товаров и предложений в b_iblock_element.XML_ID хранится артикул из 1С. Привязка делается по совпадению артикула или по маппинговой таблице.
// Пример: привязываем предложения к товарам по первым 8 символам XML_ID
// Товар: XML_ID = 'PROD-001', Предложения: XML_ID = 'PROD-001-S', 'PROD-001-M', 'PROD-001-L'
$catalogIblockId = 10;
$offersIblockId = 11;
// Собираем карту товаров: XML_ID → ID элемента
$productMap = [];
$productsRes = \CIBlockElement::GetList([], ['IBLOCK_ID' => $catalogIblockId], false, false, ['ID', 'XML_ID']);
while ($row = $productsRes->Fetch()) {
$productMap[$row['XML_ID']] = $row['ID'];
}
// Обрабатываем предложения пакетами
$offersRes = \CIBlockElement::GetList([], ['IBLOCK_ID' => $offersIblockId], false, false, ['ID', 'XML_ID']);
$batch = [];
while ($row = $offersRes->Fetch()) {
// Определяем XML_ID родителя: берём первые N символов или парсим по разделителю
$parentXmlId = substr($row['XML_ID'], 0, 8); // настройте под свою схему
if (!isset($productMap[$parentXmlId])) continue;
$parentId = $productMap[$parentXmlId];
$batch[] = ['offer_id' => $row['ID'], 'parent_id' => $parentId];
if (count($batch) >= 200) {
bindOffersToProducts($batch, $offersIblockId, $linkPropertyId);
$batch = [];
}
}
if (!empty($batch)) {
bindOffersToProducts($batch, $offersIblockId, $linkPropertyId);
}
function bindOffersToProducts(array $batch, int $iblockId, int $propId): void
{
foreach ($batch as $item) {
// Проверяем, есть ли уже запись
$existing = \Bitrix\Iblock\ElementPropertyTable::getList([
'filter' => [
'IBLOCK_ELEMENT_ID' => $item['offer_id'],
'IBLOCK_PROPERTY_ID' => $propId,
],
'select' => ['ID'],
])->fetch();
if ($existing) {
\Bitrix\Iblock\ElementPropertyTable::update($existing['ID'], [
'VALUE' => $item['parent_id'],
]);
} else {
\Bitrix\Iblock\ElementPropertyTable::add([
'IBLOCK_ELEMENT_ID' => $item['offer_id'],
'IBLOCK_PROPERTY_ID' => $propId,
'VALUE' => $item['parent_id'],
]);
}
}
}
Привязка через CSV-маппинг
Если логика определения родителя сложнее — используйте внешнюю карту:
offer_xml_id,parent_xml_id
SKU-001-RED,PROD-001
SKU-001-BLUE,PROD-001
SKU-002-S,PROD-002
$map = parseCsv('/import/offers_mapping.csv'); // [['offer_xml_id' => ..., 'parent_xml_id' => ...]]
foreach ($map as $row) {
$offerId = getElementIdByXmlId($offersIblockId, $row['offer_xml_id']);
$parentId = getElementIdByXmlId($catalogIblockId, $row['parent_xml_id']);
if ($offerId && $parentId) {
\CIBlockElement::SetPropertyValues($offerId, $offersIblockId, $parentId, 'CML2_LINK');
}
}
Проверка результата и инвалидация кэша
После массовой привязки обязательно:
- Проверьте несколько товаров в публичной части — предложения должны отображаться в карточке
- Сбросьте кэш инфоблоков для обоих инфоблоков
- Переиндексируйте фасетный фильтр, если предложения участвуют в фильтрации
\Bitrix\Iblock\InformationBlock::cleanTagCache($catalogIblockId);
\Bitrix\Iblock\InformationBlock::cleanTagCache($offersIblockId);
\Bitrix\Iblock\PropertyIndex\Manager::markIblockToReindex($offersIblockId);
Сроки выполнения
| Объём | Время |
|---|---|
| До 1 000 предложений | 2–4 часа (анализ + скрипт + проверка) |
| 1 000–20 000 предложений | 1 день |
| 20 000+ предложений | 2–3 дня (включая отладку маппинга и верификацию) |







