Developing a shipping calculator in Vue.js for 1C-Bitrix

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
    1173
  • 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
    745
  • 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

Delivery Calculator Development on Vue.js for 1C-Bitrix

A delivery calculator is needed on the product page, in the cart, and on the pre-checkout page — so the user knows the shipping cost before entering any details. Standard Bitrix only shows delivery options at the checkout step. A Vue calculator solves this through preliminary calculation.

Delivery Calculation API in Bitrix

Bitrix calculates delivery via \Bitrix\Sale\Delivery\Services\Manager. For preliminary calculation, we create a "virtual" order:

class DeliveryCalculatorController extends \Bitrix\Main\Engine\Controller
{
    public function calculateAction(array $items, string $locationCode): array
    {
        // Create a temporary order object without saving
        $order = \Bitrix\Sale\Order::create(SITE_ID, null);

        $basket = \Bitrix\Sale\Basket::create(SITE_ID);
        foreach ($items as $item) {
            $basketItem = $basket->createItem('catalog', $item['id']);
            $basketItem->setFields([
                'QUANTITY' => $item['qty'],
                'CURRENCY' => 'RUB',
            ]);
        }
        $order->setBasket($basket);

        // Set the delivery address
        $shipmentCollection = $order->getShipmentCollection();
        $shipment = $shipmentCollection->createItem();
        $shipment->setField('DELIVERY_LOCATION', $locationCode);

        // Get the list of available services with prices
        $deliveries = \Bitrix\Sale\Delivery\Services\Manager::getRestrictedObjectsList($shipment);
        $result = [];
        foreach ($deliveries as $delivery) {
            $calcResult = $delivery->calculate($shipment);
            $result[] = [
                'id'    => $delivery->getId(),
                'name'  => $delivery->getName(),
                'price' => $calcResult->isSuccess() ? $calcResult->getPrice() : null,
                'days'  => $calcResult->getPeriodDescription(),
            ];
        }
        return ['deliveries' => $result];
    }
}

Calculator Component

<script setup>
const props = defineProps(['productId', 'productWeight', 'productDimensions']);

const city = ref('');
const locationCode = ref('');
const deliveryOptions = ref([]);
const isLoading = ref(false);

// City autocomplete via DaData or Bitrix location API
async function onCityInput(query) {
    const locations = await locationApi.suggest(query);
    // User selects — we save the locationCode
}

async function calculate() {
    if (!locationCode.value) return;
    isLoading.value = true;
    const result = await deliveryApi.calculate({
        items: [{ id: props.productId, qty: 1 }],
        locationCode: locationCode.value,
    });
    deliveryOptions.value = result.deliveries;
    isLoading.value = false;
}
</script>

Default City Detection

Bitrix automatically detects the user's city via \Bitrix\Sale\Location\LocationManager::getUserLocation() (by IP through GeoIP). Use this for pre-population:

$detectedLocation = \Bitrix\Sale\Location\LocationManager::getUserLocation();
echo '<div id="delivery-calc" data-location-code="' . $detectedLocation['CODE'] . '"
    data-location-name="' . htmlspecialchars($detectedLocation['NAME']['RU']) . '"></div>';

Calculation Caching

Delivery calculation is a potentially slow operation (external requests to SDEK, DPD APIs). Cache the results:

$cacheKey = md5(json_encode($items) . $locationCode);
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(3600, $cacheKey, '/delivery-calc/')) {
    return $cache->getVars();
}
// ... calculation ...
$cache->startDataCache();
$cache->endDataCache($result);

TTL — 1 hour, rates change infrequently.

Real-World Case

A store selling oversized goods (furniture): shipping costs vary significantly by city and product dimensions. Users were adding items to the cart just to find out the delivery price, then leaving. A Vue calculator directly on the product page with IP-based city detection let users immediately see "Delivery to Krasnodar from 1,200 RUB, 3–5 days". The checkout abandonment rate dropped — per client data.

Delivery Timelines

Option Timeline
Calculator with manual city input 3 to 5 business days
With IP geolocation and autocomplete 5 to 8 business days
With external delivery service API integration 8 to 12 business days