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

Скидка на отдельную позицию в корзине - правила, купоны, своя цена

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

Решение

Смотрим, что вообще применилось к позиции:

foreach ($basket as $item) {
printf("%-30s цена %8.2f базовая %8.2f своя=%s\n",
$item->getField('NAME'),
$item->getPrice(), // с учётом скидок
$item->getBasePrice(), // без учёта скидок
$item->isCustomPrice() ? 'да' : 'нет');
// своя цена означает, что правила корзины эту позицию не трогают
}

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

Ставим свою цену на позицию:

$item->setFields([
'PRICE' => 800,
'CUSTOM_PRICE' => 'Y', // без флага пересчёт вернёт цену каталога
]);
$basket->save();

Флаг ручной стоимости - единственное, что удерживает цену при пересчёте. Без него ближайший вызов пересчёта корзины вернёт цену из каталога и молча затрёт поставленную.

Считаем скидки по составу корзины:

$order->doFinalAction(true); // применяет правила корзины и купоны
$discount = $order->getDiscount();
print_r($discount->getApplyResult()); // какие правила сработали и на что

Метод getApplyResult показывает список сработавших правил по каждой позиции. Это главный инструмент разбора: он отвечает на вопрос «почему скидки нет» точнее, чем сравнение цен.

Правила считаются на каждом пересчёте корзины, а пересчёт случается чаще, чем кажется: на добавлении товара, на смене количества, на выборе доставки. Поэтому условия правил держат простыми, а тяжёлые проверки по базе в них не выносят.

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

Применяем купон к текущей корзине:

Bitrix\Sale\DiscountCouponsManager::init();
$applied = Bitrix\Sale\DiscountCouponsManager::add($couponCode);
echo $applied ? "купон принят\n" : "купон отклонён\n";

Купон сам по себе скидки не даёт: он включает привязанное к нему правило. Без активного правила метод вернёт успех, а цена не изменится.

Проверяем скидку по позиции после пересчёта:

$order->doFinalAction(true);
foreach ($order->getBasket() as $item) {
$discount = $item->getBasePrice() - $item->getPrice();
printf("%-28s скидка %7.2f купон=%s\n",
$item->getField('NAME'), $discount,
$item->getField('DISCOUNT_NAME') ?: 'нет');
}

Поле DISCOUNT_NAME называет правило, давшее скидку именно этой позиции. Пустое поле при ненулевой разнице цен означает скидку каталога, а не правило корзины - это разные механизмы, и искать их надо в разных местах админки.

Типичные проблемы

Поставленная цена позиции возвращается к прежней.

Не выставлен флаг CUSTOM_PRICE. Любой пересчёт корзины берёт цену из каталога, если позиция не помечена как заданная вручную.

Купон принят, а скидки нет.

Правило, к которому привязан купон, отключено либо его условия не выполняются на текущем составе корзины. Купон только включает правило, а не заменяет его.

Скидка применилась ко всей корзине вместо одной позиции.

В правиле выбрано действие на заказ, а не на позицию. Действия «на товар» и «на заказ» дают разный результат при одинаковом условии.

Скидка в карточке товара не совпадает со скидкой в корзине.

Карточка показывает скидки каталога, корзина - правила корзины. Это два разных механизма, и совпадать они не обязаны.

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

Как сделать наценку на группу товаров?

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

Можно ли дать скидку при комбинации товаров?

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

Почему цену нельзя просто записать в базу?

При работающем обмене её перепишет ближайший сеанс: 1С считает себя источником цен. Своя цена живёт либо в правиле корзины, либо в отдельном типе цены, который обмен не трогает.

Как узнать, какое правило дало скидку?

Методом getApplyResult у объекта скидки заказа после doFinalAction. Он возвращает результат по каждой позиции с идентификаторами сработавших правил.

Смежное

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