8 lines
17 KiB
JSON
8 lines
17 KiB
JSON
{
|
||
"index": 363,
|
||
"slug": "bitrix-api-создание-добавление-торгового-пре",
|
||
"title": "Bitrix API: как создать торговое предложение, связать его с товаром и проверить каталог",
|
||
"excerpt": "ID элемента ещё не делает SKU готовым к продаже. Разбираем связь предложения с товаром, параметры каталога, цену, идемпотентный импорт и проверку через тот же путь чтения, которым пользуется витрина.",
|
||
"contentHtml": "<p>Элемент торгового предложения появился в админке, метод <code>CIBlockElement::Add</code> вернул ID, но в карточке товара нет варианта. Цена ошибки — менеджер видит созданную запись, а покупатель не видит размер, цвет, цену или доступный остаток. Повторный запуск импорта может добавить ещё один такой же элемент.</p>\n<p>Причина обычно не в одном вызове API. Bitrix хранит предложение в инфоблоке SKU, связь с родительским товаром — в свойстве предложения, а цену и остаток — в данных каталога. Успешная запись одного слоя не подтверждает готовность остальных.</p>\n<h2>Тезис</h2>\n<p>Создавайте торговое предложение как последовательность проверяемых слоёв: элемент инфоблока, связь с товаром, товарные параметры, цена и чтение через публичный запрос. Останавливайте процесс на первом отрицательном результате. Такой порядок показывает, где именно данные потерялись.</p>\n<h2>Как устроена запись</h2>\n<p>Сначала определите инфоблок торговых предложений и его настройку в торговом каталоге. Для связки Bitrix хранит ID инфоблока товаров и ID свойства, которое соединяет предложение с товаром. Эти значения можно получить из настройки каталога через <code>CCatalog::GetByID</code>; не подставляйте <code>CML2_LINK</code> вслепую.</p>\n<p>У предложения должен быть родительский товар. Значение свойства связи — это ID товара, а не ID самого предложения, его символьный код или артикул. Артикул можно сохранить отдельным свойством, но он не заменяет связь SKU.</p>\n<p>После создания элемента каталогу нужны товарные параметры. Для старого API это запись через <code>CCatalogProduct::Add</code>. В ней можно указать количество, НДС и правила доступности. Цена хранится отдельно. Поэтому вызов <code>CPrice::SetBasePrice</code> — ещё один шаг, а не часть <code>CIBlockElement::Add</code>.</p>\n<p><code>CCatalogProduct::Add</code> устарел с версии 17.6.0, а <code>CPrice::SetBasePrice</code> — с версии 17.6.0. Для legacy-проекта эти вызовы нужно проверять по версии модулей. Для нового D7-кода используйте модели каталога: у цены должен быть существующий тип, а результат операции нужно проверить через <code>isSuccess()</code>. Пример ниже показывает границы старого API, а не рекомендуемый путь для новой интеграции.</p>\n<figure><img src=\"/assets/illustrations/bitrix-offer-api.svg\" alt=\"Слои торгового предложения в Bitrix: элемент, связь, цена и остаток\" /><figcaption>ID элемента — только начало цепочки. Витрина должна увидеть связь, товарные параметры и цену.</figcaption></figure>\n<h2>Учебный пример создания</h2>\n<p>Код ниже намеренно использует условные ID. <code>CML2_LINK</code> в нём — только имя-заглушка: в рабочем проекте подставьте реальный числовой ID свойства из <code>CCatalog::GetByID</code> и проверьте, что <code>$productId</code> принадлежит указанному инфоблоку товаров. Перед запуском сверяйте версию модулей, права и внешний ключ операции.</p>\n<pre><code><?php\n\nuse Bitrix\\Main\\Loader;\n\nif (!Loader::includeModule('iblock') || !Loader::includeModule('catalog')) {\n throw new RuntimeException('Не удалось подключить iblock или catalog');\n}\n\n$offerIblockId = 12; // учебное значение\n$productId = 345; // ID существующего товара\n$offerName = 'Кофе, упаковка 250 г';\n$offerPrice = 990.00;\n$currency = 'RUB';\n\n$element = new CIBlockElement();\n$offerId = $element->Add([\n 'IBLOCK_ID' => $offerIblockId,\n 'NAME' => $offerName,\n 'ACTIVE' => 'Y',\n 'PROPERTY_VALUES' => [\n 'CML2_LINK' => $productId, // проверьте код свойства в проекте\n 'ARTNUMBER' => 'COFFEE-250',\n ],\n]);\n\nif (!$offerId) {\n throw new RuntimeException(\n 'Не удалось создать предложение: ' . $element->LAST_ERROR\n );\n}\n\nif (!CCatalogProduct::Add([\n 'ID' => $offerId,\n 'VAT_INCLUDED' => 'Y',\n 'QUANTITY' => 10,\n])) {\n throw new RuntimeException('Не удалось записать параметры товара');\n}\n\nif (!CPrice::SetBasePrice($offerId, $offerPrice, $currency)) {\n throw new RuntimeException('Не удалось записать базовую цену');\n}\n\n// В production здесь нужна повторная выборка и проверка витрины.\necho 'Создано предложение: ' . $offerId;</code></pre>\n<p>В старом фрагменте статьи встречается переменная <code>$offersId</code>, хотя ID сохраняется в <code>$offerId</code>. Такая опечатка ломает запись товарных параметров. Вторая опасность — перепутанная логика <code>if</code>: код может выбросить исключение после успешной операции. Проверяйте каждый результат отдельным условием, как в примере.</p>\n<p>Не создавайте новый товар автоматически, если задача требует только предложения. Сначала найдите родительский товар по устойчивому ключу проекта и проверьте, что он принадлежит ожидаемому инфоблоку. Иначе импорт создаст «сиротское» SKU или свяжет вариант с похожим товаром.</p>\n<h2>Симптомы и диагностика</h2>\n<table>\n<thead><tr><th>Симптом</th><th>Причина</th><th>Проверка</th><th>Действие</th></tr></thead>\n<tbody>\n<tr><td>В админке есть ID, в карточке нет предложения</td><td>Неверный инфоблок, неактивный элемент или неправильная связь SKU</td><td>Выбрать элемент по ID и прочитать свойство связи тем же фильтром, что использует компонент</td><td>Исправить IBLOCK_ID или значение свойства; затем повторить чтение</td></tr>\n<tr><td>Вариант виден, но цена пустая</td><td>Цена не записана, неверен тип цены или валюта</td><td>Получить запись цены по ID предложения и нужному типу цены</td><td>Записать нужный тип цены и проверить результат метода</td></tr>\n<tr><td>Цена есть, но купить нельзя</td><td>Нет записи товарных параметров, нулевой остаток или запрещена покупка без остатка</td><td>Прочитать товарные параметры и правила доступности</td><td>Создать или обновить параметры, затем проверить итоговый флаг доступности</td></tr>\n<tr><td>Каждый импорт добавляет новый вариант</td><td>Нет идемпотентного ключа и поиска существующего SKU</td><td>Найти элемент по XML_ID или уникальному артикулу до вызова Add</td><td>Разделить ветки create и update; не использовать имя как единственный ключ</td></tr>\n<tr><td>Запись правильная, витрина показывает старое состояние</td><td>Кеш, индекс или отдельный слой чтения не обновился</td><td>Сравнить прямую выборку и ответ публичного компонента</td><td>Выполнить штатное обновление кеша или индекса после успешной проверки данных</td></tr>\n</tbody>\n</table>\n<h2>Порядок действий</h2>\n<ol>\n<li>Зафиксируйте версию Bitrix, ID инфоблока товаров и ID инфоблока предложений.</li>\n<li>Проверьте настройку каталога и найдите реальное свойство, которое связывает предложение с товаром.</li>\n<li>Найдите родительский товар по XML_ID, артикулу или другому уникальному ключу. Убедитесь, что найден ровно один элемент.</li>\n<li>Найдите существующее предложение по тому же ключу. Если оно найдено, используйте обновление. Не создавайте дубль.</li>\n<li>Создайте элемент через <code>CIBlockElement::Add</code> с активностью, названием и обязательными свойствами.</li>\n<li>При результате <code>false</code> запишите <code>LAST_ERROR</code> и остановите цепочку. Не пытайтесь ставить цену для неизвестного ID.</li>\n<li>Проверьте повторной выборкой ID товара в свойстве связи. Сверьте инфоблок и активность.</li>\n<li>Создайте или обновите товарные параметры. Явно задайте только значения, которые нужны правилам проекта.</li>\n<li>Запишите цену через актуальный для версии Bitrix API и проверьте тип цены, сумму и валюту.</li>\n<li>Прочитайте предложение через тот же компонент, endpoint или GraphQL-запрос, которым пользуется витрина.</li>\n<li>Только после успешного чтения обновите кеш, поисковый индекс или агрегаты каталога.</li>\n<li>Сохраните в журнале внешний ключ операции, ID товара, ID предложения и результат каждого слоя. Не записывайте секреты и персональные данные.</li>\n</ol>\n<h2>Отрицательный путь и повторный запуск</h2>\n<p>Если не загрузился модуль, не продолжайте выполнение. Если не найден родительский товар, верните понятную ошибку импорта. Если найдено несколько товаров, остановитесь: автоматический выбор создаёт труднообратимую ошибку данных.</p>\n<p>Если элемент создался, а цена не записалась, повторный запуск не должен создавать новый элемент. Сохраните ID предложения, исправьте причину и выполните операцию обновления. Для этого нужен внешний ключ: XML_ID, артикул в пределах каталога или ключ, заданный интеграцией.</p>\n<p>Два параллельных импорта могут одновременно не найти SKU и оба пройти к созданию. Проверки в PHP недостаточно. Защитите уникальность на уровне модели данных или очереди, а после конфликта повторите поиск существующей записи.</p>\n<h2>Ограничения</h2>\n<ul>\n<li>Коды свойств, типы цен, настройки остатков и правила доступности зависят от конкретного каталога.</li>\n<li><code>CCatalogProduct::Add</code> и <code>CPrice::SetBasePrice</code> остаются legacy-вызовами; для нового кода сверяйте D7-модели и версию модуля.</li>\n<li>Очистка кеша не исправляет неверную связь, цену или остаток. Выполняйте её последней.</li>\n<li>Учебные ID, артикул и цена в примере не являются production-данными и требуют замены.</li>\n<li>Проверка через прямой API не заменяет проверку прав, обработчиков событий и бизнес-ограничений проекта.</li>\n</ul>\n<h2>Критерий готовности</h2>\n<p>Операция готова, если повторный запуск не создаёт дубль, а один и тот же публичный запрос возвращает предложение с нужным родительским товаром, активностью, ожидаемой ценой, валютой и остатком. В журнале есть успешный результат каждого шага. При ошибке цепочка останавливается на конкретном слое и сохраняет диагностическое сообщение.</p>\n<h2>Проверяемые источники</h2>\n<ul>\n<li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php\">Документация Bitrix: CIBlockElement::Add</a> — создание элемента инфоблока и возвращаемый результат.</li>\n<li><a href=\"https://dev.1c-bitrix.ru/api_help/catalog/classes/ccatalogproduct/add.php\">Документация Bitrix: CCatalogProduct::Add</a> — товарные параметры, остаток и статус устаревшего метода.</li>\n<li><a href=\"https://dev.1c-bitrix.ru/api_help/catalog/classes/cprice/cprice__setbaseprice.a8de1fcf.php\">Документация Bitrix: CPrice::SetBasePrice</a> — установка базовой цены и переход к актуальным методам.</li>\n<li><a href=\"https://dev.1c-bitrix.ru/api_help/catalog/classes/ccatalog/ccatalog__getbyid.d6f66bc1.php\">Документация Bitrix: CCatalog::GetByID</a> — сведения о настройке инфоблока торгового каталога и связи SKU.</li>\n<li><a href=\"https://dev.1c-bitrix.ru/api_d7/bitrix/catalog/model/price/index.php\">Документация Bitrix D7: модель Price</a> — добавление и обновление цен через актуальную модель каталога.</li>\n<li><a href=\"https://dev.1c-bitrix.ru/api_help/catalog/available.php\">Документация Bitrix: доступность товара и возможность покупки</a> — условия пересчёта доступности после операций с элементом и параметрами каталога.</li>\n</ul>"
|
||
}
|