Комплекты, наборы и скидки
Комплекты помогают продавать один товар как состав из нескольких позиций, наборы показывают рекомендации «купить вместе», а скидки и купоны меняют итоговую цену товара при расчете.
Эти сценарии нужны, чтобы управлять составом покупки, сопутствующими товарами и правилами продажи.
Перед выполнением примеров подключите модули iblock и catalog. Для работы с купонами и расчета скидок дополнительно подключите модуль sale.
Работать с комплектами и наборами
Класс CCatalogProductSet управляет двумя сценариями: комплектами товаров и наборами рекомендаций.
Настроить комплект товаров
Комплект применяют, когда покупатель выбирает один товар, а система учитывает несколько позиций в его составе. Например, комплект «Рабочее место» включает стол, лампу и кресло.
После настройки такой товар можно показывать как одну продаваемую позицию с составом.
Создать товар-комплект
Сначала создайте обычный товар по статье Работа с товарами и торговыми предложениями. Затем смените его тип через \Bitrix\Catalog\Model\Product::update(). В примере $productId хранит идентификатор товара, который станет комплектом.
$result = \Bitrix\Catalog\Model\Product::update($productId, [
'TYPE' => \Bitrix\Catalog\ProductTable::TYPE_SET,
]);
if (!$result->isSuccess())
{
throw new \RuntimeException(implode('; ', $result->getErrorMessages()));
}
Тип TYPE_SET регистрирует товар как составной. После этого система ожидает заполнения состава.
Добавить состав комплекта
Состав комплекта задают через CCatalogProductSet::add() с типом TYPE_SET. Заполните параметры перед вызовом метода.
- В
ITEM_IDпередайте$productId— товар-комплект. - В
ITEMSперечислите товары, которые входят в комплект. В примере это лампа$lampProductIdи кресло$chairProductId. - Для каждого товара состава укажите количество в
QUANTITYи порядок вSORT. - В
ACTIVEпередайте активность комплекта.
Ограничения состава:
- массив
ITEMSне должен быть пустым, ITEM_IDосновного товара и вложенных товаров должен быть положительным числом,QUANTITYдолжен быть больше0,- один и тот же товар нельзя добавить в состав дважды,
- в комплект можно включать товары и торговые предложения.
Метод может вернуть ошибку, если товар не существует, массив ITEMS пустой, количество равно 0 или один товар повторяется в составе. В примере ошибки сохраняются в $errors, а в $message попадает первый текст ошибки или запасное сообщение.
$setId = \CCatalogProductSet::add([
'TYPE' => \CCatalogProductSet::TYPE_SET,
'ITEM_ID' => $productId,
'ACTIVE' => 'Y',
'ITEMS' => [
[
'ITEM_ID' => $lampProductId,
'QUANTITY' => 1,
'SORT' => 100,
],
[
'ITEM_ID' => $chairProductId,
'QUANTITY' => 1,
'SORT' => 200,
],
],
]);
if (!$setId)
{
$errors = \CCatalogProductSet::getErrors();
$message = $errors ? $errors[0]['text'] : 'Не удалось создать комплект';
throw new \RuntimeException($message);
}
Метод возвращает идентификатор комплекта или false. Сохраните результат в $setId: он нужен для изменения и удаления комплекта. После успешного выполнения товар получает состав.
Получить состав комплекта
Список элементов комплекта возвращает CCatalogProductSet::getAllSetsByProduct(). Первым параметром передайте $productId — товар-комплект, вторым — тип TYPE_SET.
$sets = \CCatalogProductSet::getAllSetsByProduct(
$productId,
\CCatalogProductSet::TYPE_SET
);
Метод возвращает массив комплектов и их состав для указанного товара. Если комплект не найден или параметры некорректны, метод возвращает false.
Изменить состав комплекта
Состав комплекта изменяет CCatalogProductSet::update(). Первым параметром передайте $setId — идентификатор комплекта, который вернул CCatalogProductSet::add(). Вторым параметром передайте массив с новым описанием состава.
Передавайте полный новый состав комплекта. Элементы, которых нет в массиве ITEMS, метод удалит из состава.
$isUpdated = \CCatalogProductSet::update($setId, [
'TYPE' => \CCatalogProductSet::TYPE_SET,
'ITEM_ID' => $productId,
'ITEMS' => [
[
'ITEM_ID' => $lampProductId,
'QUANTITY' => 2,
'SORT' => 100,
],
[
'ITEM_ID' => $chairProductId,
'QUANTITY' => 1,
'SORT' => 200,
],
],
]);
if (!$isUpdated)
{
$errors = \CCatalogProductSet::getErrors();
$message = $errors ? $errors[0]['text'] : 'Не удалось обновить комплект';
throw new \RuntimeException($message);
}
Метод возвращает результат операции.
Настроить набор рекомендаций
Набор применяют для рекомендаций «купить вместе». Например, к ноутбуку показывают сумку и аксессуар. Набор не меняет состав продаваемого товара и не требует смены его типа. Такой набор можно использовать в блоке сопутствующих товаров.
Создать набор
Набор создают через CCatalogProductSet::add() с типом TYPE_GROUP. Заполните параметры перед вызовом метода.
- В
ITEM_IDпередайте$productId— товар, для которого нужно показать рекомендации. - В
ITEMSперечислите товары, которые рекомендуете купить вместе. В примере это сумка$bagProductIdи аксессуар$accessoryProductId. - Для каждой рекомендации укажите количество в
QUANTITYи порядок сортировки вSORT. - В
ACTIVEпередайте активность набора.
В набор можно включать товары, торговые предложения и комплекты.
Метод может вернуть ошибку, если товар не существует, массив ITEMS пустой, количество равно 0 или товар повторяется в наборе.
$groupId = \CCatalogProductSet::add([
'TYPE' => \CCatalogProductSet::TYPE_GROUP,
'ITEM_ID' => $productId,
'ACTIVE' => 'Y',
'ITEMS' => [
[
'ITEM_ID' => $bagProductId,
'QUANTITY' => 1,
'SORT' => 100,
],
[
'ITEM_ID' => $accessoryProductId,
'QUANTITY' => 1,
'SORT' => 200,
],
],
]);
if (!$groupId)
{
$errors = \CCatalogProductSet::getErrors();
$message = $errors ? $errors[0]['text'] : 'Не удалось создать набор';
throw new \RuntimeException($message);
}
Метод возвращает идентификатор набора или false. Сохраните результат в $groupId — он нужен для изменения и удаления набора. После выполнения у товара появляется список рекомендованных позиций.
Получить набор товара
Список рекомендаций возвращает CCatalogProductSet::getAllSetsByProduct() с типом TYPE_GROUP. Передайте $productId — товар, для которого создали набор.
$groups = \CCatalogProductSet::getAllSetsByProduct(
$productId,
\CCatalogProductSet::TYPE_GROUP
);
Метод возвращает массив наборов рекомендаций для товара $productId. Если набор не найден или параметры некорректны, метод возвращает false.
Изменить набор
Набор изменяет тот же метод CCatalogProductSet::update(). Передайте $groupId — идентификатор набора, который вернул CCatalogProductSet::add(), и новый массив ITEMS.
Передавайте полный новый список рекомендаций. Элементы, которых нет в массиве ITEMS, метод удалит из набора.
$isUpdated = \CCatalogProductSet::update($groupId, [
'TYPE' => \CCatalogProductSet::TYPE_GROUP,
'ITEM_ID' => $productId,
'ITEMS' => [
[
'ITEM_ID' => $bagProductId,
'QUANTITY' => 1,
'SORT' => 100,
],
],
]);
if (!$isUpdated)
{
$errors = \CCatalogProductSet::getErrors();
$message = $errors ? $errors[0]['text'] : 'Не удалось обновить набор';
throw new \RuntimeException($message);
}
После успешного выполнения у товара остается набор с обновленным списком рекомендованных позиций.
Удалить комплект или набор
Комплект или набор можно удалить с помощью CCatalogProductSet::delete(). В метод передайте идентификатор комплекта или набора. В примере удаляется комплект по $setId.
if (!\CCatalogProductSet::delete($setId))
{
$errors = \CCatalogProductSet::getErrors();
$message = $errors ? $errors[0]['text'] : 'Не удалось удалить комплект или набор';
throw new \RuntimeException($message);
}
Чтобы удалить набор, передайте в тот же метод $groupId. После удаления проверьте результат через CCatalogProductSet::getAllSetsByProduct().
При удалении комплекта метод возвращает основной товар к типу обычного товара. При удалении набора метод сбрасывает признак наличия набора, если у товара больше нет наборов.
Работать со скидками и купонами
Скидка задает правило изменения цены, а купон ограничивает применение скидки кодом. В типичном сценарии разработчик создает скидку на товар, выпускает купон, добавляет код купона в расчет и проверяет, какие скидки доступны товару.
Порядок работы со скидкой и купоном:
- Создайте скидку.
- Создайте купон для скидки.
- Включите использование купонов у скидки.
- Добавьте код купона в расчет цены.
- Проверьте активные скидки товара.
В примерах скидки используются код сайта s1, валюта RUB и идентификатор типа цены $basePriceTypeId. Замените их на значения из настроек собственного сайта и каталога.
Создать скидку на товар
Скидку на товар создают через CCatalogDiscount::Add(). В примере скидка действует на товар $productId на сайте с кодом s1 и уменьшает цену на 10%.
Заполните основные поля скидки:
SITE_ID— связывает скидку с сайтом,ACTIVE— включает правило скидки,NAME— хранит название правила,SORT— задает порядок сортировки среди других скидок,VALUE_TYPE— задает тип скидки: в примереTYPE_PERCENTозначает скидку в процентах,VALUE— задает размер скидки, например, для скидки10%передайте значение10,CURRENCY— задает валюту скидки, поле нужно передавать и для процентной скидки,CONDITIONS— определяет, к каким товарам и при каких условиях применяется скидка.
В примере дерево CONDITIONS описывает одно условие:
CondGroup— объединяет условия в группу,DATA.All = AND— требует выполнения всех дочерних условий,DATA.True = True— означает, что группа проверяет выполнение условий, а не их отрицание,CondIBElement— выбирает товар по идентификатору$productId,logic = Equal— требует точного совпадения.
Метод может вернуть ошибку, если код сайта неверный, товар в условии не существует, валюта некорректна или дерево CONDITIONS пустое.
$discountId = \CCatalogDiscount::Add([
'SITE_ID' => 's1',
'ACTIVE' => 'Y',
'NAME' => 'Скидка 10% на товар',
'SORT' => 100,
'VALUE_TYPE' => \CCatalogDiscount::TYPE_PERCENT,
'VALUE' => 10,
'CURRENCY' => 'RUB',
'CONDITIONS' => [
'CLASS_ID' => 'CondGroup',
'DATA' => [
'All' => 'AND',
'True' => 'True',
],
'CHILDREN' => [
[
'CLASS_ID' => 'CondIBElement',
'DATA' => [
'logic' => 'Equal',
'value' => $productId,
],
],
],
],
]);
if (!$discountId)
{
global $APPLICATION;
$exception = $APPLICATION->GetException();
$message = $exception ? $exception->GetString() : 'Не удалось создать скидку';
throw new \RuntimeException($message);
}
Метод возвращает идентификатор скидки или false. Сохраните результат в $discountId — он нужен для выпуска купона.
При ошибке пример получает текст последнего исключения из глобального объекта $APPLICATION. Константы VALUE_TYPE доступны в CCatalogDiscount и \Bitrix\Catalog\DiscountTable.
При создании скидки через CCatalogDiscount::Add() поле USE_COUPONS не включайте в параметры. Если для скидки нужен купон, создайте купон отдельным шагом и включите купоны через \Bitrix\Catalog\DiscountTable::setUseCoupons().
Создать купон
Купон создают отдельно от скидки через \Bitrix\Catalog\DiscountCouponTable::add(). Заполните параметры перед вызовом метода:
DISCOUNT_ID— связывает купон со скидкой$discountId,ACTIVE— включает купон,COUPON— хранит строковый код, который пользователь вводит при покупке. В примере используется кодSALE-10-PRODUCT,TYPE— задает режим применения, например,TYPE_ONE_ORDERозначает купон для одного заказа.
Метод может вернуть ошибку, если DISCOUNT_ID неверный, код купона пустой, код длиннее 32 символов или купон уже существует.
$couponResult = \Bitrix\Catalog\DiscountCouponTable::add([
'DISCOUNT_ID' => $discountId,
'ACTIVE' => 'Y',
'COUPON' => 'SALE-10-PRODUCT',
'TYPE' => \Bitrix\Catalog\DiscountCouponTable::TYPE_ONE_ORDER,
]);
if (!$couponResult->isSuccess())
{
throw new \RuntimeException(implode('; ', $couponResult->getErrorMessages()));
}
\Bitrix\Catalog\DiscountTable::setUseCoupons($discountId, 'Y');
Метод возвращает объект результата $couponResult. Идентификатор созданного купона доступен через $couponResult->getId().
Метод \Bitrix\Catalog\DiscountTable::setUseCoupons() включает использование купонов для скидки.
Добавить купон в расчет
Чтобы купон участвовал в расчете цены, добавьте его код в менеджер модуля sale.
Методы CCatalogDiscount::SetCoupon, GetCoupons, ClearCoupon и одноименные методы CCatalogDiscountCoupon устарели. Используйте \Bitrix\Sale\DiscountCouponsManager::add, get и clear.
if (!\Bitrix\Main\Loader::includeModule('sale'))
{
throw new \RuntimeException('Не удалось подключить модуль sale');
}
if (!\Bitrix\Sale\DiscountCouponsManager::add('SALE-10-PRODUCT'))
{
throw new \RuntimeException('Не удалось добавить купон в расчет');
}
Получить список активных скидок
После расчета итоговой цены через GetOptimalPrice() получите активные скидки методом CCatalogDiscount::GetDiscountByProduct(). Он учитывает товар, группы пользователя, доступные типы цен и сайт.
В примере используются параметры:
$productId— товар, для которого проверяют скидки,$userGroups— группы текущего пользователя из$USER->GetUserGroupArray(),'N'— режим без обновления купонов перед выборкой,[$basePriceTypeId]— идентификатор типа цены, который доступен пользователю при расчете,SITE_ID— текущий сайт.
global $USER;
$userGroups = $USER->GetUserGroupArray();
$discounts = \CCatalogDiscount::GetDiscountByProduct(
$productId,
$userGroups,
'N',
[$basePriceTypeId],
SITE_ID
);
if ($discounts === false)
{
throw new \RuntimeException('Не удалось получить скидки товара');
}
foreach ($discounts as $discount)
{
echo $discount['NAME'] . "\n";
}
Метод возвращает массив скидок в $discounts. Каждый элемент массива $discount содержит данные одной скидки, в примере выводится ее название из поля NAME.
Если скидок нет, метод возвращает пустой массив. Если идентификатор товара некорректен или товар не найден, метод возвращает false.
Не используйте CCatalogDiscount::GetDiscountProductsList и CCatalogDiscount::GetDiscountSectionsList для выборки товаров со скидками. Эти методы устарели.
Проверить результат
После создания проверьте результат отдельно для каждого сценария.
- Для комплекта вызовите
CCatalogProductSet::getAllSetsByProduct($productId, \CCatalogProductSet::TYPE_SET). В результате должен быть комплект с товарами изITEMS. - Для набора вызовите
CCatalogProductSet::getAllSetsByProduct($productId, \CCatalogProductSet::TYPE_GROUP). В результате должен быть набор с рекомендованными товарами. - Для скидки и купона рассчитайте итоговую цену через
CCatalogProduct::GetOptimalPrice()и проверьте активные скидки черезCCatalogDiscount::GetDiscountByProduct(). В списке должна быть скидка, созданная в$discountId. - Если метод возвращает
falseили объект результата с ошибками, получите текст ошибки через метод конкретного API:getErrors(),getErrorMessages()или$APPLICATION->GetException(). После ошибки не продолжайте сценарий.