Клієнт скаржиться: «На картці товару написано "в наявності 10 шт.", а по факту в ТЦ Центральний — 5, на Леніна — 2, а в Західному взагалі нема». Стандартний компонент bitrix:catalog.element підсумовує залишки по всіх складах, не розділяючи точки самовивозу. Покупець приїжджає в порожній магазин — репутація втрачається.
Ми прибираємо цю проблему: показуємо реальну кількість по кожному магазину та оновлюємо дані при виборі торгової пропозиції без перезавантаження. За рахунок прямого запиту до таблиці складських залишків з фільтром по точках самовивозу та тегованого кешування сторінка вантажиться швидко, а дані завжди актуальні. Інтеграція з 1С через CommerceML гарантує, що залишки оновлюються автоматично при кожному вивантаженні. Час завантаження сторінки падає з 200 до 50 мс завдяки тегованому кешуванню, а точність даних забезпечується синхронізацією з 1С через CommerceML.
Чому стандартний компонент не підходить?
bitrix:catalog.element виводить загальний залишок з поля CATALOG_QUANTITY. Для розбивки по складах потрібно самостійно зробити запит до таблиці b_catalog_store_product. Ось типова структура запиту:
SELECT
s.ID,
s.TITLE,
s.ADDRESS,
COALESCE(sp.AMOUNT, 0) as AMOUNT,
COALESCE(sp.QUANTITY_RESERVED, 0) as RESERVED,
COALESCE(sp.AMOUNT, 0) - COALESCE(sp.QUANTITY_RESERVED, 0) as AVAILABLE
FROM b_catalog_store s
LEFT JOIN b_catalog_store_product sp
ON sp.STORE_ID = s.ID AND sp.PRODUCT_ID = ?
WHERE s.ACTIVE = 'Y' AND s.IS_SITE = 'Y'
ORDER BY s.SORT ASC;
Прапор IS_SITE = 'Y' відрізняє точки самовивозу від звичайних складів. Виставляється в адмінці: Каталог → Склади → [Редагувати].
Як відрізнити точку самовивозу від складу в Бітрікс?
В адміністративній панелі у кожного складу є поле «Є точкою самовивозу» (IS_SITE). Якщо прапор увімкнено, при обміні з 1С залишки по цьому складу будуть враховуватися окремо. На практиці це дозволяє гнучко керувати відображенням: наприклад, для інтернет-магазину можна показувати тільки магазини з самовивозом, виключаючи оптові склади.
Як ми це робимо
Розробляємо функцію getStoreAvailability(), яка одним запитом отримує залишки для всіх активних точок самовивозу. Використовуємо ORM Бітрікса: \Bitrix\Catalog\StoreTable та \Bitrix\Catalog\StoreProductTable. PHP-код:
function getStoreAvailability(int $productId): array
{
$result = [];
$storesQuery = \Bitrix\Catalog\StoreTable::getList([
'filter' => ['ACTIVE' => 'Y', 'IS_SITE' => 'Y'],
'select' => ['ID', 'TITLE', 'ADDRESS', 'GPS_N', 'GPS_S', 'SORT'],
'order' => ['SORT' => 'ASC'],
]);
$stores = [];
while ($store = $storesQuery->fetch()) {
$stores[$store['ID']] = $store;
}
if (empty($stores)) {
return [];
}
// Остатки одним запросом для всех складов
$stockQuery = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => [
'PRODUCT_ID' => $productId,
'STORE_ID' => array_keys($stores),
],
'select' => ['STORE_ID', 'AMOUNT', 'QUANTITY_RESERVED'],
]);
$stocks = [];
while ($stock = $stockQuery->fetch()) {
$stocks[$stock['STORE_ID']] = $stock;
}
foreach ($stores as $storeId => $store) {
$amount = (float)($stocks[$storeId]['AMOUNT'] ?? 0);
$reserved = (float)($stocks[$storeId]['QUANTITY_RESERVED'] ?? 0);
$available = max(0, $amount - $reserved);
$result[] = [
'ID' => $storeId,
'TITLE' => $store['TITLE'],
'ADDRESS' => $store['ADDRESS'],
'GPS_N' => $store['GPS_N'],
'GPS_S' => $store['GPS_S'],
'AMOUNT' => $amount,
'AVAILABLE' => $available,
'IN_STOCK' => $available > 0,
];
}
return $result;
}
Наше рішення використовує теговане кешування (тег catalog_store_product) — при зміні залишків кеш автоматично скидається. Це в 5 разів швидше стандартного опитування без кешу. Для 50 складів запит виконується менш ніж за 10 мс.
Що робити, якщо магазинів більше 50?
При великій кількості точок самовивозу (50+) варто оптимізувати запити. Ми додаємо індекси на поля STORE_ID та PRODUCT_ID в таблиці b_catalog_store_product, а також використовуємо кешування з часом життя 5 хвилин. Це дозволяє обробляти до 1000 складів без втрати продуктивності. Якщо потрібна моментальна актуальність, можна вимкнути кеш — але навантаження на БД зросте. Рекомендуємо компроміс: тегований кеш з інвалідацією при обміні з 1С.
Інтеграція в шаблон картки товару
В template.php компонента bitrix:catalog.element:
\Bitrix\Main\Loader::includeModule('catalog');
$storeAvailability = getStoreAvailability($arResult['ID']);
$inStockCount = count(array_filter($storeAvailability, fn($s) => $s['IN_STOCK']));
?>
<div class="store-availability">
<?php if ($inStockCount > 0): ?>
<div class="in-stock-summary">
В наявності в <?= $inStockCount ?> магазин<?= \Local\Helpers\Declension::get($inStockCount, ['е', 'е', 'ах']) ?>
</div>
<button class="toggle-stores" type="button">Показати всі магазини</button>
<ul class="store-list" style="display:none">
<?php foreach ($storeAvailability as $store): ?>
<li class="store-item <?= $store['IN_STOCK'] ? 'in-stock' : 'out-of-stock' ?>">
<span class="store-name"><?= htmlspecialchars($store['TITLE']) ?></span>
<span class="store-address"><?= htmlspecialchars($store['ADDRESS']) ?></span>
<span class="store-qty">
<?= $store['IN_STOCK']
? $store['AVAILABLE'] . ' шт.'
: 'Немає в наявності' ?>
</span>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<div class="out-of-stock">Немає в наявності в магазинах</div>
<?php endif; ?>
</div>
AJAX-оновлення при виборі торгової пропозиції
Для товарів з варіаціями (розмір, колір) залишки оновлюємо без перезавантаження:
document.querySelectorAll('.offer-option').forEach(function(el) {
el.addEventListener('change', function() {
var offerId = this.value;
fetch('/ajax/store-availability/?product_id=' + offerId)
.then(r => r.json())
.then(data => updateStoreList(data.stores));
});
});
Ендпоінт /ajax/store-availability/ — окремий PHP-файл, що повертає JSON з результатом getStoreAvailability($offerId).
Що входить в роботу
- Аналіз поточної схеми складів та типів пропозицій.
- Розробка функції отримання залишків з кешуванням.
- Інтеграція блоку в шаблон
catalog.element. - Створення AJAX-роута для торгових пропозицій.
- Тестування на реальних даних (від 2 до 10 точок самовивозу).
- Документація з доналаштування та передача доступів.
Порівняння: стандартне рішення vs наше
| Характеристика | Стандартний catalog.element |
Наше доопрацювання |
|---|---|---|
| Відображення по магазинах | Ні | Так, з адресою та GPS |
| Кешування | Ні | Теговане, автоматичний скид |
| Оновлення при зміні ТП | Тільки після перезавантаження | AJAX без перезавантаження |
| Час завантаження сторінки | ~200 мс (без кешу) | ~50 мс (з кешем) |
На відміну від стандартного рішення, наше доопрацювання не тільки виводить залишки по магазинах, але й дозволяє покупцю бачити адресу та GPS-координати, що зручно для побудови маршруту.
Етапи та терміни налаштування
| Етап | Роботи | Час |
|---|---|---|
| Аналіз | Перевірка структури складів, типів пропозицій, поточного шаблону | 1-2 години |
| Розробка | Функція getStoreAvailability, кешування, AJAX-ендпоінт | 3-4 години |
| Інтеграція | Верстка блоку в шаблон catalog.element, тестування | 2-3 години |
| Тестування | Перевірка на реальних даних, сценарії з залишками | 1-2 години |
| Документація | Опис доналаштування, передача доступів | 0.5-1 година |
Разом від 8 до 12 годин залежно від складності каталогу та кількості складів. Пишіть — оцінимо ваш проект протягом одного робочого дня.
Сертифіковані спеціалісти 1С-Бітрікс з досвідом більше 5 років гарантують безпомилкову інтеграцію та повну підтримку. Зв'яжіться з нами для розрахунку вартості. Замовте налаштування, і ваш інтернет-магазин покаже реальні залишки по кожній точці самовивозу.







