Сценарий типичный: в магазине есть самовывоз, но он не должен показываться для части товаров, для заказов ниже определённой суммы или для отдельных зон доставки. Если оставить всё как есть, покупатель видит лишний способ доставки, а менеджер потом вручную исправляет заказы. В WooCommerce это лучше решать на уровне кода: так логика будет прозрачной и не сломается после обновления темы.
Когда проблема проявляется и что именно проверять
Сначала стоит понять, на каком этапе самовывоз вообще должен исчезать. Ошибка часто не в WooCommerce, а в том, что правило сформулировано слишком расплывчато: «убрать самовывоз для некоторых товаров» — это не техническое условие. Нужно проверить, по чему именно вы хотите фильтровать доставку:
- по конкретному товару;
- по категории товара;
- по сумме корзины;
- по выбранной зоне доставки;
- по тегу или атрибуту товара;
- по роли пользователя или типу клиента.
Если самовывоз не скрывается, обычно причина одна из трёх: фильтр подключён не там, условие проверяется по неверным данным корзины или код выполняется слишком поздно, когда WooCommerce уже сформировал список методов доставки.
Быстрая диагностика
Перед правкой кода проверьте базовые вещи:
- самовывоз включён в WooCommerce → Настройки → Доставка и добавлен в нужную зону;
- товар действительно попадает в корзину в нужном варианте, а не как другой variation;
- в теме нет кастомного кода, который уже меняет методы доставки;
- кэш страницы не подсовывает старый список способов доставки;
- если используется плагин доставки, он не переопределяет стандартный
local_pickup.
Пошаговое решение без плагина
Самый надёжный способ — отфильтровать методы доставки через woocommerce_package_rates. Этот хук позволяет убрать local_pickup из доступных вариантов до того, как их увидит покупатель.
Ниже пример: самовывоз скрывается, если в корзине есть товар из категории fragile или если сумма корзины меньше 3000 рублей. Логику можно адаптировать под свои условия.
add_filter( 'woocommerce_package_rates', 'wpdata_hide_local_pickup_for_rules', 100, 2 );
function wpdata_hide_local_pickup_for_rules( $rates, $package ) {
$hide_pickup = false;
$cart_total = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product_id = $cart_item['product_id'];
$cart_total += (float) $cart_item['line_total'];
if ( has_term( 'fragile', 'product_cat', $product_id ) ) {
$hide_pickup = true;
break;
}
}
if ( $cart_total < 3000 ) {
$hide_pickup = true;
}
if ( $hide_pickup ) {
foreach ( $rates as $rate_id => $rate ) {
if ( 'local_pickup' === $rate->method_id ) {
unset( $rates[ $rate_id ] );
}
}
}
return $rates;
}Код лучше добавлять в дочернюю тему или в небольшой mu-plugin, а не в functions.php активной темы, если проект живой и его будут обновлять. Так меньше риск потерять настройку при смене шаблона.
Если нужно отключать самовывоз только для конкретного товара
Для точечного сценария удобнее проверять ID товара. Это полезно, когда один или два товара нельзя выдавать самовывозом из-за габаритов, хрупкости или складской логистики.
add_filter( 'woocommerce_package_rates', 'wpdata_disable_pickup_for_product', 100, 2 );
function wpdata_disable_pickup_for_product( $rates, $package ) {
$blocked_product_ids = array( 123, 456 );
$disable_pickup = false;
foreach ( WC()->cart->get_cart() as $item ) {
if ( in_array( (int) $item['product_id'], $blocked_product_ids, true ) ) {
$disable_pickup = true;
break;
}
}
if ( $disable_pickup ) {
foreach ( $rates as $rate_id => $rate ) {
if ( 'local_pickup' === $rate->method_id ) {
unset( $rates[ $rate_id ] );
}
}
}
return $rates;
}Если товар вариативный, иногда нужен не product_id, а variation_id. Это частая причина, почему правило «не срабатывает»: в корзине лежит вариация, а код проверяет только родительский товар. В таком случае добавьте проверку обоих значений.
Как не сломать расчёт доставки в корзине и на checkout
WooCommerce пересчитывает доставку не только на странице оформления заказа, но и при AJAX-обновлении корзины. Поэтому важно, чтобы условие было детерминированным: не зависело от случайного состояния сессии, не читало данные из шаблона и не полагалось на уже отрисованный HTML.
Если у вас включён кэш на уровне сервера или CDN, проверьте, что страницы корзины и оформления заказа не кэшируются как обычные публичные страницы. Иначе покупатель может увидеть старый набор способов доставки после изменения корзины.
Проверка результата после внедрения
После добавления кода проверьте решение по шагам:
- Добавьте в корзину товар, который должен скрывать самовывоз.
- Откройте корзину и страницу checkout.
- Убедитесь, что
Local pickupили локальный самовывоз больше не отображается. - Удалите проблемный товар и проверьте, что самовывоз снова появляется, если правило это допускает.
- Проверьте сценарий с вариативным товаром, если он участвует в логике.
Если хотите быстро увидеть, какие методы доставки приходят в пакет, временно добавьте логирование. Это полезно на staging-сайте, но не стоит оставлять на продакшене.
add_filter( 'woocommerce_package_rates', function( $rates, $package ) {
error_log( print_r( array_keys( $rates ), true ) );
return $rates;
}, 10, 2 );После теста удалите этот фрагмент. Логи на боевом сайте быстро засоряются и могут раскрывать внутреннюю структуру доставки.
Частые ошибки и как их исправить
- Проверяют только
product_id, хотя в корзине вариация. Добавьте проверкуvariation_idили анализируйте оба значения. - Сравнивают сумму корзины по
WC()->cart->total. На этом этапе значение может быть ещё не тем, что вы ожидаете. Для логики доставки чаще надёжнее использовать сумму позицийline_totalили уже готовые данные пакета. - Фильтр подключён с низким приоритетом. Если другой плагин меняет методы доставки позже, ваш код может быть перезаписан. Иногда помогает приоритет
100или выше, но лучше сначала понять, кто ещё вмешивается. - Скрывают не тот метод. У самовывоза в WooCommerce обычно
method_idравенlocal_pickup, но конкретныйrate_idможет отличаться. Удалять нужно поmethod_id, если хотите убрать все варианты самовывоза. - Тестируют только на одной зоне доставки. В другой зоне самовывоз может иметь другой идентификатор или вообще быть настроен отдельно.
Когда лучше использовать плагин, а когда код
Если правило простое и одно: например, скрывать самовывоз для одной категории или при минимальной сумме, код обычно проще и надёжнее. Если же условий много, нужна админка для менеджера и регулярные изменения без разработчика, тогда имеет смысл смотреть в сторону плагина доставки или кастомного расширения с настройками.
| Подход | Плюсы | Минусы |
|---|---|---|
| Код через хук | Контроль, минимум зависимостей, легко отладить | Нужен разработчик, правила хранятся в коде |
| Плагин доставки | Удобно для менеджера, можно редактировать без правки файлов | Дополнительная зависимость, возможны конфликты |
| Комбинированный вариант | Критичная логика в коде, настройки — в админке | Сложнее поддерживать архитектуру |
Практические советы по безопасности и производительности
Не вставляйте такой код в случайный сниппет-плагин без контроля версий. Лучше хранить изменения в репозитории или хотя бы в отдельном mu-plugin. Это упростит откат, если после обновления WooCommerce поведение доставки изменится.
Если логика зависит от категорий, не делайте тяжёлые запросы в цикле по каждому товару. Для небольших корзин это незаметно, но на крупных магазинах лишние обращения к базе быстро становятся лишними. Когда правило сложное, сначала соберите все нужные ID в массив, а затем проверяйте их в памяти.
И ещё один практический момент: если вы используете сторонние плагины доставки, проверьте их документацию на предмет собственного фильтра или настройки скрытия методов. Иногда проще отключить самовывоз штатно, чем бороться с конфликтом двух фильтров.