Setting up a suggestion for the nearest store based on 1C-Bitrix geolocation

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Setting Up Nearest Store Suggestions by Geolocation in 1C-Bitrix

The "find the nearest store" feature is needed by retailers with a network of offline locations. The user grants location access — the site displays the nearest store, its opening hours, and product stock. This article covers an implementation based on the Bitrix warehouse module.

Storing Store Addresses

In Bitrix, stores (warehouses/retail points) are stored in the b_catalog_store table (the catalog module). Each warehouse has fields: TITLE, ADDRESS, PHONE, SCHEDULE, GPS_N (latitude), GPS_S (longitude), ACTIVE.

If the GPS_N / GPS_S fields are not filled in — the addresses must be geocoded (via Yandex or Google APIs) and coordinates saved. This is done once for all stores.

Adding coordinates to a store programmatically:

\Bitrix\Catalog\StoreTable::update($storeId, [
    'GPS_N' => 53.9045,  // latitude
    'GPS_S' => 27.5615,  // longitude
]);

Calculating Distance and Selecting the Nearest Store

The user's coordinates are obtained via JavaScript navigator.geolocation.getCurrentPosition() and sent to the server. The server calculates the distance to each warehouse using the Haversine formula.

Query with distance-sorted results via SQL (PostgreSQL/MySQL with the Haversine formula):

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;

Or a PHP implementation without direct SQL, if you prefer to use the Bitrix ORM:

function haversineDistance(
    float $lat1, float $lon1,
    float $lat2, float $lon2
): float {
    $R = 6371; // km
    $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 Endpoint for the Frontend

// /local/ajax/nearest-store.php
$lat = (float)$_POST['lat'];
$lng = (float)$_POST['lng'];

// ... selection and sorting ...

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'],
    ],
]);

Showing Stock at the Nearest Store

After determining the store, product stock at that location can be displayed directly on the product card. Data from 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 and UX

Typical interface: a popup block in the header "Nearest store: [Name], [distance] km" or a widget on the product page "In-store availability". A "Detect" button triggers the geolocation request. If the user denies access — display a list of stores with address search.

For a full map, connect the Yandex Maps JS API or Google Maps API and place markers for all locations.

Stage Time
Geocoding store addresses 2–3 h
AJAX endpoint with distance calculation 3–4 h
Widget on the page / in the header 3–5 h
Stock display for the nearest warehouse 2–3 h
Map integration (optional) 4–6 h