Developing a Calculator with Dynamic Price Loading in 1C-Bitrix
A calculator with dynamic price loading differs from a static one in that its prices are fetched from the Bitrix catalogue in real time, not hardcoded in JavaScript. This means: if the price of steel changes, the metal structures calculator immediately uses the new prices — without any code changes. This is exactly where most implementations fail: either prices are cached too aggressively (stale data for hours), or an AJAX request is made to the database on every slider movement (unnecessary load).
Where to Get Prices: Catalogue API
Prices in Bitrix are stored in the b_catalog_price table. Each product can have multiple prices (retail, wholesale, special) — the price type is determined by CATALOG_GROUP_ID. To load prices into a calculator, the API is used:
// Fetch prices for a set of products
$prices = \Bitrix\Catalog\PriceTable::getList([
'filter' => [
'PRODUCT_ID' => $productIds,
'CATALOG_GROUP_ID' => 1, // retail price
],
'select' => ['PRODUCT_ID', 'PRICE', 'CURRENCY'],
])->fetchAll();
For a calculator that works with trade offers (SKUs), bear in mind that the price is stored at the trade offer level (b_catalog_price.PRODUCT_ID = trade offer ID), not at the parent product level.
Dynamic Loading Architecture
The correct approach for a calculator with live prices:
-
Initialisation. On page load — fetch the initial prices for all required items. Not via AJAX — but PHP rendering into
data-attributes or a JSON object in<script>. The first render is fast, with no extra requests. -
In-browser recalculation. The user changes parameters — JavaScript recalculates the cost using the already-loaded prices. No network request.
-
Price refresh on trigger. Optional: if prices may change during a session (exchange-traded goods, currency-based prices), a periodic AJAX request to refresh prices is added — once every 5–10 minutes, not on every interaction.
// Initialise prices from PHP
const calcPrices = <?= json_encode($pricesData) ?>;
// Recalculate when parameters change (no AJAX)
function recalculate() {
const materialId = document.getElementById('material').value;
const quantity = parseFloat(document.getElementById('quantity').value);
const pricePerUnit = calcPrices[materialId]?.price ?? 0;
document.getElementById('total').textContent =
formatPrice(pricePerUnit * quantity);
}
Prices with Discounts and User Groups
For authenticated users in the "Wholesale" group, the price must be fetched from the corresponding price type (CATALOG_GROUP_ID = 2). When the calculator initialises, the server-side determines the user's group and returns the appropriate set of prices.
The cumulative discount and cart rule mechanisms are not applied here — the calculator works with list prices. If prices including discounts are needed, they must be calculated via CCatalogProduct::GetOptimalPrice, which is significantly slower and requires caching.
Price Caching
Prices from the catalogue are cached on the server with Bitrix tagged cache:
$cache = \Bitrix\Main\Data\Cache::createInstance();
$cacheKey = 'calc_prices_' . md5(serialize($productIds));
if ($cache->initCache(3600, $cacheKey, '/calc/prices/')) {
$prices = $cache->getVars();
} elseif ($cache->startDataCache()) {
$prices = fetchPricesFromDB($productIds);
$cache->endDataCache($prices);
// invalidation tag — triggered when a catalogue price is updated
\Bitrix\Main\Data\TaggedCache::clearByTag('catalog_price_' . $productId);
}
A TTL of 3600 seconds (1 hour) is a sensible balance for most catalogues. For exchange-traded goods — 60–300 seconds.
Case Study: Metal Products Calculator with Live Prices
Client — a metal trading company. Catalogue: 3,000 items, prices updated daily via an export from 1C. Calculator: the user selects the product type (angle bar, channel bar, pipe), steel grade, and length — and receives the price per tonne and the total cost.
Problem with the original implementation: prices were hardcoded in a JSON file updated manually when the price list changed. There were situations where the calculator was showing week-old prices.
New implementation: prices are fetched from b_catalog_price on the first page render (PHP → JSON in <script>). The tagged cache is invalidated by a Bitrix agent 30 minutes after the 1C import completes. Users always see current prices with a delay of no more than 30 minutes after the price list is updated — acceptable for this business.
Development Timeline
- Calculator with catalogue price loading, without personalisation — 3–6 days
-
- Group prices (wholesale, dealers) — +1–2 days
-
- Dynamic in-session price refresh — +1 day
-
- Stock integration (warehouse availability) — +2–3 days







