Development of the 1C-Bitrix geolocation module

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
    1177
  • 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

Developing a Geolocation Module for 1C-Bitrix

Geolocation is not just "determine the city". On an e-commerce site, geolocation affects delivery method availability, product assortment (not all products are shipped to a region), warehouse source for shipment, regional phone number in the header, regional promotions. Bitrix has bitrix:geo for basic city detection, but it is not integrated with the catalog, basket, and custom components — each connection must be built independently.

Architecture

Module vendor.geo solves three tasks:

  1. Region detection — by IP, by browser geolocation, by explicit user choice
  2. Region storage — hierarchy: country → region → city, with all metadata
  3. Application — API for components: current region, delivery filtering, regional content

ORM tables:

  • b_vendor_geo_country — countries: id, name, iso2, iso3, phone_code
  • b_vendor_geo_region — regions/oblasts: id, country_id, name, timezone
  • b_vendor_geo_city — cities: id, region_id, name, latitude, longitude, population
  • b_vendor_geo_user_location — user's selected region: user_id or session_id, city_id, detection_method (ip/browser/manual), created_at

City Detection by IP

Three options in decreasing order of accuracy and cost:

MaxMind GeoIP2 (recommended for high accuracy):

$reader = new \GeoIp2\Database\Reader('/path/to/GeoLite2-City.mmdb');
$record = $reader->city($_SERVER['REMOTE_ADDR']);
$cityName   = $record->city->names['ru'] ?? $record->city->name;
$regionName = $record->mostSpecificSubdivision->names['ru'] ?? null;

ip-api.com (free, 45 req/min):

$data = json_decode(file_get_contents("http://ip-api.com/json/{$ip}?lang=ru&fields=city,regionName"), true);

dadata.ru (more accurate for Russia):

// POST https://suggestions.dadata.ru/suggestions/api/4_1/rs/iplocate/address

The result is cached by IP address for 24 hours. For corporate networks (one IP = entire office) and CDN proxies, detection is less accurate — in these cases, the result is marked as low_confidence.

City Confirmation Popup

On first visit, a popup is shown: "Is your city Moscow?". If the user clicks "No, choose another" — a modal window with city search opens:

GET /bitrix/components/vendor/geo.city-search/ajax.php?q=Krasnod
→ [{"id":12,"name":"Krasnodar","region":"Krasnodar Krai"}, ...]

Search by b_vendor_geo_city.name with LIKE 'Krasnod%', pre-prepared index on the column. Selected city is saved in cookie and b_vendor_geo_user_location.

Geolocation Application

Regional phone in header:

$city = \Vendor\Geo\GeoService::getCurrentCity();
$phone = RegionalPhoneTable::getByCity($city['ID']) ?? Option::get('vendor.geo', 'default_phone');

Delivery method filtering:

In the checkout component, before displaying deliveries, filter by region:

$deliveries = \Bitrix\Sale\Delivery\Services\Manager::getActiveList();
$cityId     = GeoService::getCurrentCityId();

$deliveries = array_filter($deliveries, function($delivery) use ($cityId) {
    $regions = DeliveryRegionTable::getAvailableRegions($delivery->getId());
    return empty($regions) || in_array($cityId, $regions);
});

Nearest warehouse for availability calculation:

If the store works with multiple warehouses, the nearest is determined by coordinates → haversine distance.

Regional Content in Infoblock

Infoblock elements and sections can be linked to visibility regions through the RegionBinding HL-block. The vendor:geo.iblock.filter component automatically adds a filter by current region to the query.

Development Timeline

Stage Duration
ORM-tables, city database import 1 day
City detection by IP (MaxMind + fallback) 1 day
City confirmation popup, city search 1 day
Current region API, session and cookie 0.5 day
Regional phones, content 1 day
Delivery filtering by region 1 day
Nearest warehouse by coordinates 1 day
Testing 0.5 day

Total: 7 working days. Geolocation via browser Geolocation API (GPS/WiFi) — +1 day.