Диагностика проблемы: почему товары не должны участвовать в автоматических скидках
В типичных интернет-магазинах на WooCommerce часто возникает ситуация, когда необходимо применить автоматические скидки ко всем товарам, кроме определённой группы или отдельных продуктов. Например, это могут быть новинки, товары по специальным ценам, бренды с ограниченными акциями или товары из определённых категорий. Без фильтрации таких товаров скидка будет уменьшать их стоимость, что может привести к убыткам или конфликтам с поставщиками.
Чтобы решить эту задачу, важно понять, как WooCommerce применяет скидки и где можно вмешаться программно для исключения товаров.
Как работают автоматические скидки в WooCommerce
Автоматические скидки чаще всего реализуются через купоны с автоматическим применением или через плагины, которые применяют скидки по определённым правилам. В любом случае WooCommerce вычисляет итоговую стоимость корзины, применяя фильтры и хуки, например woocommerce_cart_calculate_fees или woocommerce_before_calculate_totals.
Для исключения товаров из скидок нужно перехватить момент расчёта цены и изменить стоимость товаров, к которым скидка не должна применяться.
Пошаговое решение: исключаем товары из автоматических скидок через хук woocommerce_before_calculate_totals
1. Определяем критерии исключения
Это могут быть ID товаров, ID категорий, теги или мета-поля. В примере ниже исключим товары по ID.
2. Добавляем кастомный код в файл functions.php вашей активной темы или в плагин для кастомных функций
add_action('woocommerce_before_calculate_totals', 'exclude_products_from_auto_discount', 20, 1);
function exclude_products_from_auto_discount( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Массив ID товаров для исключения из скидок
$excluded_products = array(12, 34, 56);
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$product_id = $cart_item['product_id'];
if ( in_array( $product_id, $excluded_products ) ) {
// Убираем все скидки, установленные ранее
if ( isset( $cart_item['data'] ) ) {
$cart_item['data']->set_price( $cart_item['data']->get_regular_price() );
}
}
}
}3. Объяснение кода
- На хук
woocommerce_before_calculate_totalsнавешиваем функцию с приоритетом 20, чтобы она сработала после других скидочных обработчиков. - В функции проверяем, не админ ли это и не AJAX ли, чтобы избежать лишних вычислений.
- Создаём массив
$excluded_productsс ID товаров, которые нужно исключить. - Для каждого товара в корзине, если его ID совпадает, устанавливаем цену равной обычной цене без скидок с помощью
set_price().
Проверка результата после внедрения
1. Добавьте в корзину несколько товаров, включая те, что в массиве $excluded_products.
2. Проверьте, что скидка применяется ко всем товарам, кроме исключённых.
3. Для отладки можно временно добавить error_log в функцию, чтобы убедиться, что она срабатывает и меняет цены:
error_log('Обрабатывается товар ID: ' . $product_id);4. Очистите кэш сайта и браузера.
Частые ошибки и как их исправить
- Скидка не применяется к остальным товарам: Возможно, приоритет хука слишком низкий, попробуйте увеличить приоритет до 30 или 40.
- Цена исключённых товаров не сбрасывается: Проверьте, что в корзине действительно те ID, что указаны в массиве, и что вы вызываете
set_price()для объекта товара. - Изменения не видны в админке или при AJAX-запросах: Убедитесь, что проверка
is_admin() && ! defined('DOING_AJAX')корректно исключает ненужные вызовы, возможно, временно отключите для отладки. - Проблемы с совместимостью плагинов скидок: Некоторые плагины могут переопределять цены позже, изучите их документацию и логику применения скидок.
Практические советы по производительности и безопасности
- Не храните массив исключённых товаров в коде, если их много — лучше использовать мета-поля или отдельную категорию для динамического определения.
- Избегайте выполнения кода при админских запросах и AJAX, не относящихся к фронтенду корзины.
- Тестируйте на копии сайта перед внедрением в продакшен.
- Если используете плагины кеширования, обязательно очищайте кеш после внесения изменений.
Альтернативные варианты реализации
| Вариант | Плюсы | Минусы |
|---|---|---|
Через хук woocommerce_before_calculate_totals | Гибко, программируемо, не требует плагинов | Требует навыков PHP, возможны конфликты |
| Использование плагинов скидок с фильтрами исключений | Удобный интерфейс, поддержка сложных правил | Можно платить за премиум, меньше контроля |
| Добавление мета-полей и условий в шаблоны | Легко для небольших правок | Может приводить к дублированию логики |