По умолчанию 1С-Битрикс считает доставку только на странице оформления заказа. Чтобы показать стоимость прямо в корзине, нужно определить локацию покупателя и запустить расчёт вручную через объект заказа. Разбираем, как это устроено и где обычно ломается.
Зачем показывать доставку в корзине
Стандартный сценарий Битрикса такой: покупатель складывает товары в корзину, переходит на страницу оформления, вводит адрес — и только тогда компонент bitrix:sale.order.ajax пересчитывает доставку. До этого момента стоимость доставки не известна ни системе, ни клиенту.
Для многих магазинов это проблема: покупатель видит финальную сумму слишком поздно, уже пройдя половину воронки. Показ ориентировочной стоимости доставки на этапе корзины снижает число брошенных заказов и число вопросов в поддержку.
- Прозрачность цены — клиент видит полную сумму до перехода к оформлению.
- Меньше отказов на последнем шаге, где «внезапная» доставка раздражает сильнее всего.
- Подсказка о бесплатной доставке — «добавьте на 500 ₽ до бесплатной доставки» работает именно в корзине.
Где Битрикс считает доставку
Расчёт доставки в Битриксе завязан на объект отгрузки (Shipment), который живёт внутри объекта заказа. Служба доставки — это класс-обработчик, зарегистрированный в модуле sale и управляемый через \Bitrix\Sale\Delivery\Services\Manager.
Ключевой момент: цену возвращает не сам профиль доставки в отрыве от заказа, а метод calculateDelivery() у отгрузки. Ему нужны корзина (вес, габариты, сумма), локация доставки и выбранный профиль. Поэтому «посчитать доставку в корзине» технически означает — собрать временный объект заказа и вызвать у него расчёт.
| Компонент | Что делает |
|---|---|
bitrix:sale.basket.basket | Отрисовывает корзину, но доставку не считает |
bitrix:sale.order.ajax | Считает доставку на оформлении заказа |
\Bitrix\Sale\Order | Ядро, через которое доступен расчёт отгрузки |
Старый API CSaleDelivery::CalculateDeliveryPrice считается устаревшим. Для актуальных версий используйте объектную модель D7 (пространство имён \Bitrix\Sale).
Определение локации покупателя
Без города расчёт большинства служб доставки невозможен — тарифы СДЭК, Почты России, ПЭК зависят от направления. Значит, до расчёта нужно откуда-то взять код локации. Варианты, по убыванию надёжности:
- Явный выбор города покупателем — селектор в шапке, значение хранится в сессии или куке.
- Геолокация по IP — модуль
bitrix:sale.location.selectorили сторонний сервис определяет город при первом заходе. - Локация по умолчанию — если ничего не определено, берём город магазина (например, Москву) и честно помечаем расчёт как ориентировочный «до Москвы».
Локация в Битриксе — это не строка «Москва», а код местоположения из модуля sale (свойство CODE в таблице b_sale_location). Именно его нужно проставить в свойство заказа типа LOCATION.
Расчёт доставки через объект заказа
Схема расчёта одинаковая для одной службы и для перебора всех доступных. Создаём временный заказ, наполняем его корзиной, ставим локацию, добавляем отгрузку с нужным профилем и вызываем расчёт:
- получаем корзину пользователя через
\Bitrix\Sale\Basket::loadItemsForFUser(); - создаём заказ:
$order = \Bitrix\Sale\Order::create($siteId, $userId), задаём тип плательщика и валюту; - прикрепляем корзину:
$order->setBasket($basket); - ставим локацию в свойство:
$propertyCollection->getDeliveryLocation()->setValue($locationCode); - создаём отгрузку от профиля:
$shipment = $shipmentCollection->createItem(Manager::getObjectById($deliveryId))и переносим в неё товары корзины; - считаем:
$result = $shipment->calculateDelivery().
Если нужно показать все подходящие способы, а не один, отберите доступные профили с учётом ограничений (вес, сумма, локация) через Manager::getRestrictedObjectsList($shipment) и посчитайте каждый в цикле.
CalculationResult. Всегда проверяйте $result->isSuccess() перед вызовом $result->getPrice(): служба может быть недоступна для данной корзины (превышен вес, не та зона), и это штатная ситуация, а не сбой.Вывод стоимости в шаблон корзины
Готовую сумму нужно донести до шаблона корзины. Есть два подхода, и выбор между ними определяет всю остальную реализацию.
| Подход | Плюсы | Минусы |
|---|---|---|
Доработка result_modifier.php шаблона sale.basket.basket | Всё в одном месте, данные приходят вместе с корзиной | Расчёт на каждый рендер, тяжелее кэшировать |
| Отдельный AJAX-эндпоинт + подгрузка в корзину | Не тормозит отрисовку корзины, легко обновлять при смене города | Больше кода, нужен свой контроллер |
Для боевого магазина почти всегда правильнее AJAX-подход: создайте свой класс-контроллер (наследник \Bitrix\Main\Engine\Controller), зарегистрируйте действие в .settings.php компонента или через config.php, и запрашивайте расчёт отдельно от корзины. При смене города в селекторе дёргаете тот же эндпоинт и обновляете блок доставки без перезагрузки страницы.
Не редактируйте типовые шаблоны компонентов напрямую в /bitrix/components/ — копируйте компонент или шаблон в /local/ и правьте там, иначе обновление платформы затрёт изменения.
Кэширование и производительность
Расчёт доставки — недешёвая операция: создание объекта заказа, применение ограничений, а для внешних служб (СДЭК, Boxberry) ещё и запрос к их API. Если считать всё синхронно на каждый рендер корзины, страница ощутимо просядет по скорости.
- Кэшируйте по ключу «локация + состав корзины + общий вес». Пока корзина и город не менялись, повторно считать незачем — используйте
\Bitrix\Main\Data\Cacheс коротким TTL. - Выносите внешние API в AJAX, чтобы медленный ответ службы не блокировал отрисовку корзины.
- Ставьте таймаут на обращения к внешним службам и предусмотрите фолбэк — показывайте «рассчитывается на оформлении», если API не ответил.
Типичные ошибки и подводные камни
Собрали случаи, которые чаще всего всплывают при внедрении расчёта доставки в корзине:
- Нет веса и габаритов у товаров. Службы, считающие по весу, вернут минимальный тариф или ошибку. Заполните вес в свойствах торговых предложений и в настройках каталога.
- Свойство локации не привязано к типу плательщика. Если у выбранного
PERSON_TYPEнет свойства с привязкой к местоположению,getDeliveryLocation()вернёт пусто. - Ограничения службы отсекают профиль. Настроенные ограничения по сумме, весу или зоне могут делать службу недоступной именно для тестовой корзины — проверяйте на реальных данных.
- Расчёт в корзине и на оформлении расходятся. Так бывает, если на оформлении добавляются доп. услуги или другой набор свойств. Держите логику расчёта в одном сервисном классе, а не дублируйте её.
Отдельно проверяйте поведение для незалогиненного пользователя: корзина у него привязана к FUSER_ID из куки, и заказ создаётся для гостя — это рабочий сценарий, но его легко упустить в тестах.
Итог
Показ стоимости доставки в корзине — это не настройка галочкой, а небольшая доработка: определить локацию покупателя, собрать временный объект заказа, посчитать отгрузку через актуальный API \Bitrix\Sale и аккуратно вывести результат, не сломав кэш. Сделанная правильно, она повышает прозрачность цены и заметно снижает отказы на оформлении.
Мы в B2Bsite регулярно внедряем такой расчёт в корзину и мини-корзину, увязываем его с выбором города, бесплатной доставкой от суммы и внешними API служб — так, чтобы страница оставалась быстрой. Если нужен точный и производительный расчёт доставки прямо в корзине, поможем спроектировать и внедрить решение под ваш магазин.
Частые вопросы
Можно ли показать доставку в корзине без программирования?
Штатными средствами Битрикса — нет. Компонент корзины доставку не считает, поэтому нужна доработка: определение локации и расчёт через объект заказа. Готовые решения на Маркетплейсе делают это тоже кодом.
Почему расчёт в корзине отличается от расчёта на оформлении?
Чаще всего из-за разного набора данных: на оформлении покупатель вводит точный адрес и может выбрать доп. услуги, а в корзине расчёт идёт по ориентировочной локации. Чтобы суммы совпадали, держите логику расчёта в одном сервисном классе.
Что делать, если город покупателя ещё не известен?
Определите его по IP или предложите выбрать в селекторе. Если ничего не определено, считайте по локации по умолчанию и явно подпишите, что стоимость ориентировочная и уточняется при оформлении.
Не будет ли расчёт замедлять корзину?
Может, если считать синхронно на каждый рендер, особенно с внешними API. Выносите расчёт в AJAX, кэшируйте по связке локация плюс состав корзины и ставьте таймаут на обращения к службам.
Как показать сразу несколько способов доставки с ценами?
Отберите доступные профили через Manager::getRestrictedObjectsList для отгрузки, затем посчитайте каждый в цикле методом calculateDelivery. Выводите только те, что вернули isSuccess.
Почему стоимость доставки одинаковая для всех городов?
Скорее всего, в расчёт передаётся не код локации, а название города, либо у товаров не заполнен вес. Проверьте, что в свойство LOCATION заказа попадает корректный CODE местоположения.
Совместимо ли это с композитным сайтом?
Да, но блок доставки нельзя кэшировать в статике композита — иначе покупатель увидит цену для чужого города. Загружайте его динамической частью через AJAX поверх композитного кэша.
Работает ли расчёт для незалогиненного пользователя?
Работает. Корзина гостя привязана к FUSER_ID из куки, а временный заказ создаётся для анонимного пользователя. Этот сценарий нужно отдельно проверять в тестах.
Поможем с настройкой и поддержкой 1С-Битрикс: Управление сайтом
Поможем с настройкой, доработкой и поддержкой 1С-Битрикс: Управление сайтом.