Ограничение опции самовывоза в WooCommerce по городам

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

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

Когда это действительно нужно

Ограничение по городу полезно, если:

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

Если у вас один пункт выдачи на весь регион, проще настроить обычный самовывоз без фильтрации. Но если логистика завязана на город, фильтр по адресу экономит время и снижает число ошибок в корзине.

Диагностика: что проверить до правки кода

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

Проверьте три вещи:

  • включена ли доставка на странице WooCommerce → Настройки → Доставка;
  • используется ли метод local_pickup в нужной зоне доставки;
  • есть ли у вас стабильный список городов, для которых самовывоз разрешён.

Если городов много и они часто меняются, сначала лучше вынести список в отдельную настройку или хотя бы в массив в коде, чтобы не править условие в нескольких местах.

Пошаговое решение через фильтр доставки

Самый практичный вариант — отфильтровать доступные методы доставки через хук woocommerce_package_rates. Он срабатывает при расчёте доставки и позволяет убрать самовывоз, если город не входит в разрешённый список.

1. Добавьте код в дочернюю тему или мини-плагин

Лучше не вставлять это в основной файл темы, если сайт живёт долго. Безопаснее использовать дочернюю тему или небольшой mu-plugin. Пример ниже скрывает самовывоз для всех городов, кроме указанных в массиве.

add_filter( 'woocommerce_package_rates', 'wp8_limit_local_pickup_by_city', 10, 2 );

function wp8_limit_local_pickup_by_city( $rates, $package ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
        return $rates;
    }

    $allowed_cities = array( 'Москва', 'Химки', 'Красногорск' );
    $city = '';

    if ( ! empty( $package['destination']['city'] ) ) {
        $city = trim( wp_strip_all_tags( $package['destination']['city'] ) );
    }

    if ( in_array( $city, $allowed_cities, true ) ) {
        return $rates;
    }

    foreach ( $rates as $rate_id => $rate ) {
        if ( isset( $rate->method_id ) && 'local_pickup' === $rate->method_id ) {
            unset( $rates[ $rate_id ] );
        }
    }

    return $rates;
}

Логика простая: если город входит в список, самовывоз остаётся. Если нет — метод удаляется из доступных тарифов.

2. Если нужен не список городов, а один разрешённый город

Иногда задача ещё проще: самовывоз доступен только в одном городе. Тогда условие можно упростить, чтобы не держать лишний массив.

add_filter( 'woocommerce_package_rates', 'wp8_pickup_only_for_moscow', 10, 2 );

function wp8_pickup_only_for_moscow( $rates, $package ) {
    $city = isset( $package['destination']['city'] ) ? trim( wp_strip_all_tags( $package['destination']['city'] ) ) : '';

    if ( 'Москва' === $city ) {
        return $rates;
    }

    foreach ( $rates as $rate_id => $rate ) {
        if ( 'local_pickup' === $rate->method_id ) {
            unset( $rates[ $rate_id ] );
        }
    }

    return $rates;
}

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

Если город хранится не в shipping_city

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

add_filter( 'woocommerce_package_rates', 'wp8_debug_shipping_destination', 1, 2 );

function wp8_debug_shipping_destination( $rates, $package ) {
    if ( function_exists( 'wc_get_logger' ) ) {
        $logger = wc_get_logger();
        $logger->debug( print_r( $package['destination'], true ), array( 'source' => 'shipping-destination' ) );
    }

    return $rates;
}

После теста этот код нужно убрать. Он нужен только чтобы понять, из какого поля брать город и не гадать по интерфейсу.

Проверка результата после внедрения

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

  1. откройте корзину в режиме инкогнито;
  2. введите адрес с разрешённым городом и убедитесь, что самовывоз виден;
  3. поменяйте город на запрещённый и обновите расчёт доставки;
  4. проверьте, что local_pickup исчезает из списка;
  5. сохраните заказ и убедитесь, что метод доставки не подставляется автоматически.

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

Частые ошибки и как их исправить

Самовывоз не исчезает

Чаще всего причина в том, что метод доставки называется не local_pickup, а другой вариант с кастомной логикой. Проверьте method_id у тарифа. Иногда плагин доставки создаёт собственный идентификатор, и тогда условие нужно менять под него.

Город не определяется

Если поле пустое, проблема обычно в форме оформления заказа или в том, что покупатель ещё не заполнил адрес. В этом случае метод доставки будет виден до ввода города. Это нормально, если расчёт идёт по мере заполнения формы. Если вам нужно скрывать самовывоз сразу, придётся опираться на другой признак, например на выбранную зону доставки или на список почтовых индексов.

Метод пропадает у всех, включая разрешённые города

Проверьте написание городов в массиве. В PHP сравнение через in_array() с третьим параметром true чувствительно к точному совпадению строки. Лишний пробел, другой регистр или сокращение названия уже дадут промах.

После обновления темы код перестал работать

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

Плагин, код или настройка в зоне доставки

Если задача простая, можно обойтись настройками зон доставки. Но как только появляется зависимость от города, кода обычно меньше и он надёжнее. Коротко сравним варианты:

ПодходКогда подходитМинус
Настройка зон доставкиОдин город, одна зона, без исключенийПлохо масштабируется, если городов несколько
Код через woocommerce_package_ratesНужна точная логика по городуНужно аккуратно тестировать и поддерживать
Сторонний плагин доставкиУже есть сложная логистика и тарифыЗависимость от стороннего решения и его обновлений

Если у вас уже стоит плагин для доставки, сначала проверьте, не умеет ли он ограничивать методы по адресу без кастомного кода. Но если такой опции нет, фильтр WooCommerce обычно проще и прозрачнее.

Безопасность и производительность

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

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

Если у вас много похожих правил для доставки, имеет смысл собрать их в один небольшой плагин. Так проще сопровождать логику и не искать фрагменты по шаблонам.

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

Оптимизация AJAX запросов в WordPress: эффективные методы и примеры кода
09.01.2026
WooCommerce: как отключить автологин после регистрации пользователя
04.08.2026
Автоматизация обработки форм в WordPress: практические примеры и код
20.11.2025
Как использовать WPRemark для оценки и отзывов в WordPress
16.01.2026
Автозаполнение форм в WordPress: улучшение пользовательского опыта
15.11.2025