Перейти к содержимому

Товар создан, но не продаётся - разбор причин

Элемент завели скриптом или обменом, в списке раздела он виден, а цены и кнопки покупки у него нет. Корзина такую позицию не принимает даже из кода. Разбираем причины по убыванию частоты, начиная с того, есть ли у элемента запись товара в торговом каталоге.

С чего начать

Смотрим, есть ли у элемента запись товара:

\Bitrix\Main\Loader::requireModule('catalog');
$product = \Bitrix\Catalog\ProductTable::getList([
'select' => ['ID', 'TYPE', 'AVAILABLE', 'QUANTITY'],
'filter' => ['=ID' => $elementId], // ID товарной записи равен ID элемента
])->fetch();
var_dump($product); // false означает, что элемент товаром так и не стал

Один этот вызов делит причины пополам. Пустой результат уводит к первым двум причинам: товарной записи нет, и продавать пока просто нечего. Найденная запись переводит разбор к типу товара, ценам и правам групп.

Считаем такие элементы по всему инфоблоку:

$elements = \CIBlockElement::GetList([], ['IBLOCK_ID' => $iblockId], false, false, ['ID']);
$ids = [];
while ($row = $elements->Fetch()) {
$ids[] = (int)$row['ID'];
}
$withProduct = \Bitrix\Catalog\ProductTable::getList([
'select' => ['ID'],
'filter' => ['@ID' => $ids], // читаем только те, у кого товарная запись есть
])->fetchAll();
printf("элементов %d, товаров %d\n", count($ids), count($withProduct));

Расхождение чисел показывает масштаб. Один сбойный товар - ошибка оператора, а сотни - скрипт или профиль обмена, который заводит элементы мимо каталога.

Проверяем, что инфоблок вообще торговый каталог:

$catalog = \CCatalogSKU::getInfoByIBlock($iblockId);
// CATALOG_TYPE: D - каталог, P - товары с предложениями, O - предложения, X - всё сразу
printf("тип каталога: %s\n", $catalog ? $catalog['CATALOG_TYPE'] : 'инфоблок не каталог');

Пустой ответ означает, что инфоблок к модулю каталога не привязан. В таком инфоблоке товарная запись не появится, сколько раз её ни добавляй скриптом.

Смотрим цены товара и права групп на их типы:

$prices = \Bitrix\Catalog\PriceTable::getList([
'select' => ['ID', 'CATALOG_GROUP_ID', 'PRICE', 'CURRENCY'],
'filter' => ['=PRODUCT_ID' => $elementId],
])->fetchAll();
$access = \Bitrix\Catalog\GroupAccessTable::getList([
'filter' => ['=CATALOG_GROUP_ID' => $priceTypeId], // ACCESS_VIEW и ACCESS_BUY
])->fetchAll();

Цена привязана к паре товар плюс тип цены, а не к элементу вообще. Строка цены есть, а права группы на этот тип нет - покупатель не увидит ни суммы, ни кнопки.

Заводим недостающую товарную запись и цену:

$result = \Bitrix\Catalog\Model\Product::add([
'ID' => $elementId, // всегда равен ID элемента инфоблока
'TYPE' => \Bitrix\Catalog\ProductTable::TYPE_PRODUCT,
'QUANTITY' => 15,
'QUANTITY_TRACE' => 'Y', // вести количественный учёт
'CAN_BUY_ZERO' => 'N', // не продавать при нулевом остатке
]);
if (!$result->isSuccess()) {
throw new \RuntimeException(implode('; ', $result->getErrorMessages()));
}

Создание товара всегда двухшаговое: сначала элемент инфоблока, следом товарная запись с тем же идентификатором. После успешного вызова платформа сама пересчитывает признак доступности, отдельно его выставлять не нужно.

Причины

  1. Элемент создан без записи товара в каталоге примерно 30% случаев

    ПризнакКарточка открывается как обычная новость: ни цены, ни кнопки покупки на ней нет.

    ПроверкаЧитаем товарную запись по идентификатору элемента выборкой выше: у сбойных позиций она пустая.

    Что делатьДобавляем товарные параметры сразу после создания элемента, в том же скрипте или обработчике импорта.

  2. Инфоблок не привязан к торговому каталогу примерно 20% случаев

    ПризнакПусто у всех элементов инфоблока разом, и вкладки торгового каталога в админке нет.

    ПроверкаСмотрим тип каталога по инфоблоку: пустой ответ означает, что привязки к модулю нет.

    Что делатьРегистрируем инфоблок торговым каталогом; для связки с предложениями инфоблок товаров и свойство привязки задаются только вместе.

  3. Тип товара не рассчитан на продажу напрямую примерно 16% случаев

    ПризнакРодительская карточка показывает цену от вариантов, но сама в корзину не уходит.

    ПроверкаСмотрим поле типа у товарной записи: родитель с предложениями и родитель без них не продаются.

    Что делатьПродаём предложения, а не родителя; простому товару ставим обычный тип, услуге - тип услуги.

  4. Цена заведена родителю вместо торгового предложения примерно 14% случаев

    ПризнакВ админке цена у товара видна, а на витрине выбор варианта обнуляет её и прячет кнопку.

    ПроверкаСверяем идентификатор в строке цены с идентификатором предложения, а не родительской карточки.

    Что делатьПереносим цену на предложение, а перед записью ищем существующую пару товар плюс тип цены.

  5. Группе покупателей закрыт нужный тип цены примерно 12% случаев

    ПризнакГость цены не видит, а под администратором та же карточка показывает и сумму, и кнопку.

    ПроверкаЧитаем права групп на тип цены: нужны и право видеть, и право покупать.

    Что делатьВыдаём группе право покупать по нужному типу цены: без него корзина позицию не примет.

  6. Нулевой остаток при включённом количественном учёте примерно 8% случаев

    ПризнакКнопка пропала у части позиций, и это ровно те, у которых обмен обнулил остаток.

    ПроверкаСмотрим количество и признак доступности товарной записи вместе с разрешением покупки при нуле.

    Что делатьРазрешаем покупку при нулевом остатке там, где товар идёт под заказ, либо пополняем количество.

Частые вопросы

Почему товар виден в списке, но у него нет цены?

Элемент инфоблока и товар каталога - две разные записи с одним идентификатором. Пока товарная запись не создана, цену привязывать не к чему, и компонент выводит карточку как обычный контент.

Создал элемент через CIBlockElement::Add, что ещё нужно?

Нужен второй шаг: товарные параметры через Model\Product::add с тем же идентификатором. Дальше цена по паре товар плюс тип цены, иначе покупать по-прежнему нечего.

После обмена с 1С часть товаров без цен, как найти их разом?

Выборкой товаров, у которых нет строки цены нужного типа. Такие позиции появляются пачками, когда номенклатуру завели раньше, чем проставили цены в учётной системе.

Признак доступности стоит в «да», а купить всё равно нельзя. Почему?

Доступность считается по остатку и настройкам учёта количества. Компоненты и корзина дополнительно проверяют активность элемента, даты публикации и права группы на тип цены.

Можно ли хранить цену в свойстве инфоблока?

Технически можно, но корзина, заказы, скидки и склад такое свойство не увидят. Продажные данные живут в объектах каталога, а не в свойствах элемента инфоблока.

Смежное

Первоисточник