Configuring Click & Collect (Order Online — Pick Up In Store) for 1C-Bitrix
Click & Collect is a scheme in which a customer places an order in an online store and picks it up at a physical location. In Bitrix this is implemented via a delivery module with the "pickup" type linked to specific warehouses/locations. The non-trivial part is showing real stock availability per location before checkout, reserving the product at a specific warehouse, and notifying the customer when the order is ready.
Data Structure: Pickup Locations and Warehouses
Bitrix stores pickup locations in the b_sale_location table (for the "Pickup" type). Warehouse logic is in b_catalog_store:
SELECT s.ID, s.TITLE, s.ADDRESS, sp.AMOUNT
FROM b_catalog_store s
JOIN b_catalog_store_product sp ON sp.STORE_ID = s.ID
WHERE sp.PRODUCT_ID = ? AND s.ACTIVE = 'Y' AND sp.AMOUNT > 0
ORDER BY s.SORT ASC;
Each pickup location in Bitrix can be linked to a warehouse. Configured in Catalog → Warehouses — each warehouse has an XML_ID field for mapping to a delivery location.
Pickup Delivery Service
In the admin panel: Store → Delivery → Add Delivery Service → Type: Pickup.
Creating programmatically:
\Bitrix\Main\Loader::includeModule('sale');
$deliveryService = \Bitrix\Sale\Delivery\Services\Manager::getList([
'filter' => ['CODE' => 'self_pickup'],
])->fetch();
if (!$deliveryService) {
// Register the pickup service
\Bitrix\Sale\Delivery\Services\Manager::add([
'NAME' => 'In-store pickup',
'CODE' => 'self_pickup',
'ACTIVE' => 'Y',
'CLASS_NAME' => '\Bitrix\Sale\Delivery\Services\Base',
'CURRENCY' => 'USD',
'PRICE' => 0,
]);
}
Pickup Location Selection in Cart
In the checkout component template (sale.order.ajax or sale.basket.order) — a dropdown or map with locations:
// Get active warehouses that have all cart items in stock
$basketItems = $order->getBasket();
$productIds = [];
foreach ($basketItems as $item) {
$productIds[] = $item->getProductId();
}
// For each warehouse, check if all cart items are available
$storesResult = \Bitrix\Catalog\StoreTable::getList([
'filter' => ['ACTIVE' => 'Y'],
'select' => ['ID', 'TITLE', 'ADDRESS', 'GPS_N', 'GPS_S'],
'order' => ['SORT' => 'ASC'],
]);
$availableStores = [];
while ($store = $storesResult->fetch()) {
$allAvailable = true;
foreach ($productIds as $productId) {
$stockResult = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => ['PRODUCT_ID' => $productId, 'STORE_ID' => $store['ID']],
'select' => ['AMOUNT'],
])->fetch();
if (!$stockResult || $stockResult['AMOUNT'] < $basketItems->getQuantityByProductId($productId)) {
$allAvailable = false;
break;
}
}
if ($allAvailable) {
$availableStores[] = $store;
}
}
Reservation at a Specific Warehouse
After the pickup location is selected — reserve the products at that warehouse, not from the general pool:
// In the order save handler
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale', 'OnSaleOrderBeforeSaved',
function (\Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
// Read the selected pickup location from order properties
$pickupStoreId = $order->getPropertyCollection()
->getItemByOrderPropertyCode('PICKUP_STORE_ID')
?->getValue();
if (!$pickupStoreId) {
return;
}
// Reserve at the specific warehouse
foreach ($order->getBasket() as $basketItem) {
\Bitrix\Catalog\StoreProductTable::reserveProduct(
$basketItem->getProductId(),
(int)$pickupStoreId,
$basketItem->getQuantity(),
$order->getId()
);
}
}
);
The order property PICKUP_STORE_ID is created as a custom order field: Store → Settings → Order Properties.
Setup Timeline
Pickup delivery service, location selection with stock availability check, warehouse reservation, saving to order property — 2–4 business days depending on the complexity of the existing warehouse infrastructure.







