Разработка сайта логистической компании на 1С-Битрикс
Сайт логистической компании — это не витрина, а рабочий инструмент. Грузоотправитель заходит с конкретной задачей: узнать стоимость перевозки из точки A в точку B, оформить заявку, отследить груз. Если калькулятор не считает, трекинг не показывает статус, а для повторной отправки нужно заново заполнять 12 полей — клиент уходит к конкуренту с нормальным личным кабинетом. Битрикс позволяет собрать всё это, но архитектура должна быть заточена под специфику логистики с первого дня.
Каталог услуг: инфоблоки под типы перевозок
Логистическая компания предлагает не один продукт, а матрицу услуг: FTL (полная загрузка), LTL (сборный груз), warehousing, таможенное оформление, last-mile доставка. Каждый тип имеет свои параметры — ограничения по весу, габаритам, температурному режиму, географии.
Структура данных:
- Инфоблок «Услуги» — разделы: Автоперевозки, Морские, Железнодорожные, Авиа, Складские услуги, Таможня
-
Highload-блок «Типы транспорта» — справочник: тент, рефрижератор, контейнер 20', контейнер 40', изотерм. Поля:
UF_NAME,UF_CAPACITY_KG,UF_VOLUME_M3,UF_PHOTO,UF_DESCRIPTION -
Highload-блок «Маршруты» — пары город-город с привязкой к типам перевозок. Поля:
UF_FROM_CITY,UF_TO_CITY,UF_TRANSPORT_TYPE,UF_TRANSIT_DAYS,UF_ACTIVE - Highload-блок «Ограничения груза» — максимальный вес, габариты, запрещённые категории по типу транспорта
Каждая услуга в инфоблоке содержит:
| Свойство | Тип | Назначение |
|---|---|---|
| SERVICE_TYPE | L (список) | FTL / LTL / Warehousing / Customs / LastMile |
| TRANSPORT_TYPES | S:Highload (множ.) | Привязка к допустимым типам транспорта |
| ROUTE_DIRECTIONS | S:Highload (множ.) | Доступные направления |
| MAX_WEIGHT | N | Макс. вес груза, кг |
| MAX_VOLUME | N | Макс. объём, м³ |
| TEMPERATURE_MODE | L | Обычный / Холодильник / Морозильник |
| INSURANCE_AVAILABLE | L | Да / Нет |
| CUSTOMS_INCLUDED | L | Да / Нет |
Для SEO критичны страницы вида «[тип перевозки] + [маршрут]»: «Автоперевозки Москва — Новосибирск», «Сборные грузы из Китая». Эти страницы генерируются из связки инфоблока услуг и Highload-блока маршрутов через кастомный компонент. URL строится по шаблону /uslugi/{service-code}/{from}-{to}/, ЧПУ настраивается через CIBlockElement::GetList с фильтром по свойствам ROUTE_FROM и ROUTE_TO.
Калькулятор стоимости перевозки: ключевая конверсионная точка
Калькулятор — причина, по которой 70% посетителей приходят на сайт логистической компании. Не форма «оставьте заявку, мы перезвоним», а реальный расчёт: откуда, куда, что везём, сколько стоит. Если калькулятор не даёт цену — посетитель не становится лидом.
Архитектура калькулятора состоит из трёх слоёв.
Слой 1 — фронтенд: мультишаговая форма. Шаг 1: откуда → куда (автокомплит городов через AJAX, данные из Highload-блока «Города» или внешний API — Yandex.Geocoder). Шаг 2: параметры груза — вес, объём (Д×Ш×В), количество мест, тип упаковки, температурный режим. Шаг 3: дополнительные опции — страхование, таможенное оформление, доставка до двери. Шаг 4: результат — стоимость, срок, доступные виды транспорта.
Форма реализуется как React-компонент или vanilla JS с пошаговой навигацией. Каждый шаг — AJAX-валидация на сервере. Автокомплит городов — отдельный endpoint /api/cities/suggest/?q=Моск, который ищет по b_hlbd_cities (Highload-блок) через DataManager::getList() с фильтром ['%UF_NAME' => $query].
Слой 2 — расчёт расстояния. Тарифы зависят от расстояния. Два подхода:
Подход A — предрассчитанная матрица расстояний в Highload-блоке DistanceMatrix. Поля: UF_FROM_CITY_ID, UF_TO_CITY_ID, UF_DISTANCE_KM, UF_TRANSIT_HOURS. При 500 городах — 250К записей, вполне подъёмно. Плюс: мгновенный ответ, не зависит от внешних API. Минус: нужно пересчитывать при добавлении городов.
Подход B — расчёт через внешний API в реальном времени. Yandex.Routing API (router.route()) или Google Distance Matrix API. Запрос: две точки → расстояние в км + время в пути. Кэширование результата в Highload-блок: если пара город-город уже рассчитана — берём из кэша, иначе — API-запрос + сохранение. TTL кэша — 30 дней.
// Получение расстояния с кэшированием
class DistanceService
{
public static function getDistance(int $fromCityId, int $toCityId): array
{
// Проверяем кэш в Highload-блоке
$cached = DistanceMatrixTable::getList([
'filter' => [
'UF_FROM_CITY_ID' => $fromCityId,
'UF_TO_CITY_ID' => $toCityId,
'>UF_CACHED_AT' => date('Y-m-d', strtotime('-30 days'))
]
])->fetch();
if ($cached) {
return [
'distance_km' => $cached['UF_DISTANCE_KM'],
'transit_hours' => $cached['UF_TRANSIT_HOURS']
];
}
// Запрос к Yandex.Routing API
$result = YandexRoutingClient::route(
Cities::getCoordinates($fromCityId),
Cities::getCoordinates($toCityId)
);
// Сохраняем в кэш
DistanceMatrixTable::add([
'UF_FROM_CITY_ID' => $fromCityId,
'UF_TO_CITY_ID' => $toCityId,
'UF_DISTANCE_KM' => $result['distance'],
'UF_TRANSIT_HOURS' => $result['duration'],
'UF_CACHED_AT' => new DateTime()
]);
return $result;
}
}
Слой 3 — тарификация. Тарифы хранятся в Highload-блоке Tariffs со структурой:
| Поле | Тип | Описание |
|---|---|---|
| UF_SERVICE_TYPE | список | FTL / LTL / Express |
| UF_TRANSPORT_TYPE | привязка | Тип транспорта |
| UF_DISTANCE_FROM | число | Начало диапазона, км |
| UF_DISTANCE_TO | число | Конец диапазона, км |
| UF_RATE_PER_KM | число | Ставка за км |
| UF_MIN_RATE | число | Минимальная стоимость |
| UF_WEIGHT_COEFF | число | Коэффициент за перевес |
| UF_VOLUME_COEFF | число | Коэффициент за объём |
Формула расчёта для LTL: max(distance_km * rate_per_km, min_rate) * weight_coeff * volume_coeff + insurance + customs_fee. Для FTL — проще: фиксированная ставка за км × расстояние, без весовых коэффициентов (машина целиком).
Менеджер обновляет тарифы через административный интерфейс Highload-блока — без привлечения разработчика. Это критичный момент: тарифы меняются еженедельно, и если для обновления цены нужен деплой — система мертва.
Результат калькулятора возвращается JSON-ответом:
{
"variants": [
{
"transport": "Тент 20т",
"service": "FTL",
"price": 45000,
"currency": "RUB",
"transit_days": 3,
"distance_km": 1800
},
{
"transport": "Сборный груз",
"service": "LTL",
"price": 12500,
"currency": "RUB",
"transit_days": 7,
"distance_km": 1800
}
]
}
Под результатом — кнопка «Оформить заявку», которая переносит все параметры расчёта в форму заказа. Пользователю не нужно вводить данные повторно.
Отслеживание груза
Трекинг — вторая причина, по которой клиенты возвращаются на сайт. Поле ввода трекинг-номера на главной странице, результат — цепочка статусов с датами и текущее местоположение на карте.
Источник данных — 1С:TMS или 1С:Логистика. Интеграция через REST API Битрикса: 1С отправляет обновления статуса через POST /rest/logistics.shipment.updateStatus с полями tracking_number, status_code, location, timestamp. Битрикс хранит статусы в Highload-блоке ShipmentStatuses.
На фронте — AJAX-запрос по трекинг-номеру. Ответ содержит массив статусов (принят, на складе, в пути, на таможне, доставлен) и координаты последнего известного местоположения для отображения на карте через Yandex.Maps.
Обновление в реальном времени — через polling каждые 60 секунд или WebSocket, если объём трафика оправдывает сложность.
Клиентский портал: B2B-кабинет
Личный кабинет для корпоративных клиентов — то, что отличает серьёзную логистическую компанию от «сайта-визитки с калькулятором». Это не просто история заказов, а полноценный рабочий инструмент логиста.
Авторизация и роли. Компания-клиент регистрируется как юрлицо. Внутри компании — несколько пользователей с разными ролями. Реализация через группы пользователей Битрикса (CGroup) и кастомные поля:
- Администратор компании — видит все заказы, управляет пользователями, скачивает документы, видит финансы
- Логист — создаёт заказы, отслеживает статусы, скачивает ТТН и CMR
- Бухгалтер — доступ только к документам: счета, акты, счета-фактуры
Привязка пользователя к компании — через кастомное поле UF_COMPANY_ID в b_user. Проверка доступа — middleware в init.php, который при каждом запросе к /personal/ проверяет группу пользователя и UF_COMPANY_ID.
Функциональность кабинета:
История заказов — список всех перевозок компании с фильтрацией по дате, статусу, направлению. Данные из Highload-блока Orders с полями: UF_ORDER_NUMBER, UF_COMPANY_ID, UF_FROM_CITY, UF_TO_CITY, UF_STATUS, UF_CARGO_DESCRIPTION, UF_WEIGHT, UF_VOLUME, UF_PRICE, UF_CREATED_AT. Пагинация через bitrix:system.pagenavigation, фильтрация — AJAX.
Документооборот — каждый заказ содержит набор документов: ТТН, CMR, инвойс, упаковочный лист, страховой полис. Файлы хранятся как множественное свойство типа «Файл» в привязке к заказу. Скачивание — через кастомный контроллер, который проверяет принадлежность документа к компании пользователя перед отдачей файла. Никаких прямых ссылок на /upload/ — только авторизованный доступ.
// Проверка доступа к документу
class DocumentController extends Controller
{
public function download(int $orderId, int $fileId): Response
{
$user = $GLOBALS['USER'];
$order = OrdersTable::getById($orderId)->fetch();
if ($order['UF_COMPANY_ID'] !== $user->getUfCompanyId()) {
throw new AccessDeniedException();
}
$file = CFile::GetFileArray($fileId);
return new BinaryFileResponse($file['SRC']);
}
}
Шаблоны повторных отправок. Постоянный клиент отправляет одни и те же грузы по одним и тем же маршрутам. Логист сохраняет заказ как шаблон, в следующий раз — выбирает шаблон, меняет дату, подтверждает. Шаблоны — отдельный Highload-блок ShipmentTemplates с полями, дублирующими структуру заказа, плюс UF_TEMPLATE_NAME и UF_COMPANY_ID.
Финансовый раздел — баланс взаиморасчётов, выставленные счета, история оплат. Данные синхронизируются из 1С через REST API по расписанию (каждые 15 минут) или по событию.
Интеграция с 1С:TMS
Синхронизация заказов между сайтом и 1С:TMS (или 1С:Управление автотранспортом) — двусторонняя:
- Сайт → 1С: новый заказ с сайта отправляется в 1С через REST API. Endpoint на стороне 1С принимает JSON с параметрами заказа, создаёт документ «Заявка на перевозку»
-
1С → Сайт: изменение статуса в 1С триггерит webhook на Битрикс. Обработчик обновляет
UF_STATUSв Highload-блокеOrdersи отправляет email/SMS клиенту
Формат обмена — JSON через HTTP REST. XML-обмен через CommerceML для логистики избыточен — это формат торговли, не перевозок.
Карта покрытия и автопарк
Интерактивная карта — Yandex.Maps с кастомным слоем. Маркеры складов и хабов из Highload-блока Warehouses (поля: UF_NAME, UF_ADDRESS, UF_COORDINATES, UF_TYPE, UF_PHOTO). Линии маршрутов между хабами — ymaps.Polyline с данными из Highload-блока маршрутов. Клик по хабу — балун с адресом, графиком работы, доступными услугами.
Автопарк — инфоблок с типами транспорта. Карточка машины: фото, грузоподъёмность, объём кузова, тип (тент, рефрижератор, контейнеровоз). Вывод через bitrix:news.list с кастомным шаблоном — сетка карточек с иконками характеристик.
API для партнёров
REST API для интеграции с системами партнёров: экспедиторов, маркетплейсов, ERP-систем клиентов. Endpoints:
-
POST /api/v1/orders/create— создание заказа -
GET /api/v1/orders/{id}/status— статус заказа -
GET /api/v1/tracking/{number}— трекинг -
POST /api/v1/calculate— расчёт стоимости
Авторизация — API-ключ в заголовке X-Api-Key. Ключи генерируются в админке, привязаны к компании-партнёру. Rate limiting — 100 запросов в минуту через middleware.
Этапы и сроки
| Масштаб | Сроки |
|---|---|
| Сайт-визитка с калькулятором, до 10 маршрутов | 4-6 недель |
| Корпоративный сайт с кабинетом, трекинг, 1С-интеграция | 10-16 недель |
| Платформа с API для партнёров, B2B-портал, полная автоматизация | 16-24 недели |
Сроки не включают настройку обмена на стороне 1С — это отдельный проект со стороны 1С-разработчика, который идёт параллельно.







