Files
progcode/editorial/agent-rewrites/363.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
16 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 363,
"slug": "bitrix-api-создание-добавление-торгового-пре",
"title": "Bitrix API: как создать торговое предложение, связать его с товаром и проверить каталог",
"excerpt": "Торговое предложение в Bitrix состоит не только из элемента инфоблока. Разбираем связь 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>Сначала определите два инфоблока: инфоблок товаров и инфоблок торговых предложений. Их связь задаёт настройка торгового каталога. Не подставляйте свойство <code>CML2_LINK</code> вслепую: в конкретном проекте его код или ID может отличаться.</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>Названия методов зависят от версии Bitrix. Документация помечает <code>CCatalogProduct::Add</code> и <code>CPrice::SetBasePrice</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>. Перед запуском получите реальные значения из настройки инфоблока SKU и из схемы свойств.</p>\n<pre><code>&lt;?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-&gt;Add([\n 'IBLOCK_ID' =&gt; $offerIblockId,\n 'NAME' =&gt; $offerName,\n 'ACTIVE' =&gt; 'Y',\n 'PROPERTY_VALUES' =&gt; [\n 'CML2_LINK' =&gt; $productId, // проверьте код свойства в проекте\n 'ARTNUMBER' =&gt; 'COFFEE-250',\n ],\n]);\n\nif (!$offerId) {\n throw new RuntimeException(\n 'Не удалось создать предложение: ' . $element-&gt;LAST_ERROR\n );\n}\n\nif (!CCatalogProduct::Add([\n 'ID' =&gt; $offerId,\n 'VAT_INCLUDED' =&gt; 'Y',\n 'QUANTITY' =&gt; 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>Получить цены по PRODUCT_ID, равному 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>Методы старого API могут быть доступны в проекте, но это не делает их рекомендуемыми для нового кода.</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://www.dev.1c-bitrix.ru/api_help/catalog/classes/ccatalogproduct/add.php\">Документация Bitrix: CCatalogProduct::Add</a> — товарные параметры, остаток и статус устаревшего метода.</li>\n<li><a href=\"https://m.dev.1c-bitrix.ru/api_help/catalog/classes/cprice/cprice__setbaseprice.a8de1fcf.php\">Документация Bitrix: CPrice::SetBasePrice</a> — установка базовой цены и переход к актуальным методам.</li>\n</ul>"
}