Настройка предложения ближайшего магазина по геолокации 1С-Битрикс
Функция «найти ближайший магазин» нужна ритейлу с сетью офлайн-точек. Пользователь разрешает доступ к геолокации — сайт показывает ближайший магазин, его режим работы, остатки товаров. Рассматриваем реализацию на базе складского модуля Битрикса.
Хранение адресов магазинов
В Битриксе магазины (склады/торговые точки) хранятся в таблице b_catalog_store (модуль catalog). У каждого склада есть поля: TITLE, ADDRESS, PHONE, SCHEDULE, GPS_N (широта), GPS_S (долгота), ACTIVE.
Если поля GPS_N / GPS_S не заполнены — нужно геокодировать адреса (через API Яндекса или Google) и сохранить координаты. Это делается один раз для всех магазинов.
Добавить координаты магазину программно:
\Bitrix\Catalog\StoreTable::update($storeId, [
'GPS_N' => 53.9045, // широта
'GPS_S' => 27.5615, // долгота
]);
Вычисление расстояния и выбор ближайшего
Координаты пользователя получаете через JavaScript navigator.geolocation.getCurrentPosition() и передаёте на сервер. На сервере вычисляете расстояние до каждого склада по формуле Haversine.
Запрос с сортировкой по расстоянию через SQL (PostgreSQL/MySQL с формулой Haversine):
SELECT id, title, address, gps_n, gps_s,
(6371 * acos(
cos(radians(:lat)) * cos(radians(gps_n)) *
cos(radians(gps_s) - radians(:lng)) +
sin(radians(:lat)) * sin(radians(gps_n))
)) AS distance
FROM b_catalog_store
WHERE active = 'Y' AND gps_n IS NOT NULL
ORDER BY distance ASC
LIMIT 5;
Либо PHP-реализация без прямого SQL, если хотите использовать ORM Битрикса:
function haversineDistance(
float $lat1, float $lon1,
float $lat2, float $lon2
): float {
$R = 6371; // км
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat/2)**2
+ cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon/2)**2;
return $R * 2 * atan2(sqrt($a), sqrt(1-$a));
}
$stores = \Bitrix\Catalog\StoreTable::getList([
'filter' => ['=ACTIVE' => 'Y', '!=GPS_N' => false],
'select' => ['ID', 'TITLE', 'ADDRESS', 'GPS_N', 'GPS_S', 'SCHEDULE', 'PHONE'],
])->fetchAll();
usort($stores, function($a, $b) use ($userLat, $userLng) {
$dA = haversineDistance($userLat, $userLng, $a['GPS_N'], $a['GPS_S']);
$dB = haversineDistance($userLat, $userLng, $b['GPS_N'], $b['GPS_S']);
return $dA <=> $dB;
});
$nearest = array_slice($stores, 0, 3);
AJAX-эндпоинт для фронтенда
// /local/ajax/nearest-store.php
$lat = (float)$_POST['lat'];
$lng = (float)$_POST['lng'];
// ... выборка и сортировка ...
header('Content-Type: application/json');
echo json_encode([
'nearest' => [
'id' => $nearest[0]['ID'],
'title' => $nearest[0]['TITLE'],
'address' => $nearest[0]['ADDRESS'],
'distance' => round($dist, 1),
'schedule' => $nearest[0]['SCHEDULE'],
'phone' => $nearest[0]['PHONE'],
],
]);
Показ остатков в ближайшем магазине
После определения магазина можно показывать остатки товара в этой точке прямо в карточке товара. Данные из b_catalog_store_product:
$stock = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => [
'=PRODUCT_ID' => $productId,
'=STORE_ID' => $nearestStoreId,
],
'select' => ['AMOUNT'],
])->fetch();
$inStock = $stock && $stock['AMOUNT'] > 0;
UI и UX
Типичный интерфейс: всплывающий блок в шапке «Ближайший магазин: [Название], [расстояние] км» или виджет на странице товара «Наличие в магазинах». Кнопка «Определить» запускает запрос геолокации. Если пользователь отказал в доступе — показываете список магазинов с поиском по адресу.
Для полноценной карты — подключаете Яндекс.Карты JS API или Google Maps API и расставляете метки всех точек.
| Этап | Время |
|---|---|
| Геокодирование адресов магазинов | 2–3 ч |
| AJAX-эндпоинт с вычислением расстояния | 3–4 ч |
| Виджет на странице / в шапке | 3–5 ч |
| Показ остатков по ближайшему складу | 2–3 ч |
| Интеграция карты (опционально) | 4–6 ч |







