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

Региональность магазина - выбор города, цены и склады

Делаем магазин региональным: выбор города, свои цены и остатки, правильный кэш и город в заказе.

Механика

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

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

Папки в адресе выглядят дешевле, но обходятся дороже в поддержке. Каждая ссылка на сайте обязана нести префикс региона, и любой забытый компонент уводит покупателя из его города обратно в столицу.

Один сайт с выбранным городом - самая частая схема на практике. Адреса при этом общие, а от региона зависят цены, наличие, доставка и контакты; расплата - сложный кэш и слабая различимость регионов в поисковой выдаче.

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

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

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

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

Шаги

  1. Выбрать схему: поддомены, папки в адресе или один сайт с выбором города.
  2. Определять город по адресу посетителя, а сделанный выбор хранить в куке.
  3. Сопоставить регионы со складами и типами цен в отдельном справочнике.
  4. Развести кэш витрины и ссылки по регионам, включая композитные страницы.
  5. Показать региональные контакты, условия доставки и сроки на витрине.
  6. Передать город и склад в заказ, чтобы учётная система их увидела.

Код

Определяем город по адресу посетителя:

use Bitrix\Main\Service\GeoIp\Manager;
$cityName = Manager::getCityName('ru'); // город по адресу посетителя
$country = Manager::getCountryCode(); // страна пригодится для валюты
// данные приходят от подключённого источника: без него ответ будет пустым
$default = 'msk'; // регион по умолчанию задают всегда
// адрес клиента берут через getRealIp: он учитывает заголовок от посредника

Геолокация работает только с подключённым источником данных. Источников три: локальная база на диске, облачный сервис и сторонний сервис определения; порядок и параметры задают в настройках геолокации, а не в коде.

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

Запоминаем выбор человека:

$cookie = new \Bitrix\Main\Web\Cookie('REGION', $region, time() + 31536000);
\Bitrix\Main\Application::getInstance()->getContext()->getResponse()->addCookie($cookie);
// выбор человека важнее геолокации: перезаписывать его на каждом заходе нельзя
$regionCode = $request->getCookie('REGION') ?: $byGeo ?: $default;

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

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

$region = RegionTable::getRow([
'filter' => ['=CODE' => $regionCode],
'select' => ['CODE', 'NAME', 'PHONE', 'PRICE_TYPE', 'STORE_ID'],
]);
// один справочник вместо десятка условий в шаблонах: город, телефон, цена, склад
// справочник удобно держать highload-блоком: он же нужен в фильтрах и в админке

Справочник регионов - это то место, куда смотрит весь остальной код. Разложенные по шаблонам условия вида «если Казань» живут до первого нового города и делают добавление региона отдельным проектом.

Показываем наличие по складам региона:

$rows = \Bitrix\Catalog\StoreProductTable::getList([
'select' => ['AMOUNT', 'STORE_ID'],
'filter' => ['=PRODUCT_ID' => $productId, '@STORE_ID' => $region['STORE_ID']],
])->fetchAll();
$amount = array_sum(array_column($rows, 'AMOUNT')); // остаток именно этого города
// склады региона обычно перечисляют списком: у крупных городов их несколько

Общий остаток по всем складам покупателю не интересен и вреден. Он обещает товар, которого в его городе нет, а разбирается это уже в разговоре с менеджером, после оформленного заказа.

Берём цену своего региона:

'PRICE_CODE' => [$region['PRICE_TYPE'] ?: 'BASE'], // тип цены задан регионом
'USE_PRICE_COUNT' => 'N',
// права на тип цены при этом должны быть выданы всем покупателям
// базовый тип оставляют запасным: новый регион без своей цены не ломает витрину

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

Разводим кэш по регионам:

$cache->initCache(3600, 'catalog_' . $sectionId . '_' . $regionCode, '/vendor/catalog');
// без региона в ключе первый зашедший оставит свои цены всем остальным
// композитную страницу делят по региону тем же способом, что и по группе

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

Передаём город и склад в заказ:

$props = $order->getPropertyCollection();
$props->getItemByOrderPropertyId($cityPropId)->setValue($region['NAME']);
$props->getItemByOrderPropertyId($storePropId)->setValue($region['STORE_ID']);
$order->save();
// учётная система получит город и склад обычными реквизитами заказа

Город в заказе нужен не только для доставки. По нему сверяют остатки, выбирают склад отгрузки и разбирают спорные заказы, а без него менеджер угадывает регион по телефонному коду.

Ограничения

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

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

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

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

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

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

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

Покупатель из другого города видит чужие цены.

Регион не входит в ключ кэша витрины, и первый зашедший наполнил кэш своими ценами. В ключ добавляют код региона.

Выбранный город сбрасывается на каждой странице.

Геолокация перезаписывает выбор человека при каждом заходе. Определение по адресу применяют только при отсутствии сохранённого выбора.

На витрине показан остаток, которого в городе нет.

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

В поиске поддомены признаны дублями основного сайта.

Контент региональных страниц совпадает дословно, а карта сайта у поддоменов общая. Регионы разводят контактами, сроками и своей картой сайта.

Менеджер не видит, из какого города пришёл заказ.

Город и склад не переданы свойствами заказа, поэтому в учётную систему они не попали. Их кладут в заказ при оформлении.

На стенде город не определяется вовсе.

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

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

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

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

Поддомены или папки: что выбрать?

Поддомены предсказуемее: платформа сама разводит сайты, настройки и почту, а поисковая выдача видит разные адреса. Папки дешевле по лицензии, но всю работу по разведению ссылок и кэша берёт на себя код проекта.

Как привязать склады к городам?

Сопоставление ведут на стороне сайта: своим полем склада или отдельным справочником регионов. Учётной системе город и склад отдают вместе с заказом, а обратно принимают только остатки по складам.

Как определить город посетителя?

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

Почему после включения регионов цены перепутались?

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

Смежное

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