Mass product status update in 1C-Bitrix
After a season ends, 200 products need to be unpublished. At midnight, a promotion starts, activating 50 products simultaneously. Both scenarios require bulk status changes, but a naive implementation introduces race conditions or high MySQL load. With over 5 years of Bitrix development experience and 50+ completed projects, we deliver proven solutions. A proper batch implementation can speed up status updates by 100 times compared to a one-by-one loop. Customers typically save 60–80% in operational costs, which can amount to thousands of dollars annually.
Understanding product status in Bitrix
A product in Bitrix is an infoblock element with multiple activity properties. The main field is ACTIVE (Y/N) in the b_iblock_element table. Additionally, there are activity time boundaries: ACTIVE_FROM and ACTIVE_TO — the period during which the product is active. If the current time is not within the range, the element is considered inactive regardless of the ACTIVE flag.
Custom statuses (new, promotion, hit, bestseller) are separate infoblock properties of type "List", not the built-in ACTIVE field. Bulk changes to custom properties require a different approach, discussed in the relevant section below.
How to mass update ACTIVE status using D7?
Direct update through the ORM is the fastest method for the ACTIVE field. For bulk status update in Bitrix, use D7:
$productIds = [1001, 1002, 1003];
foreach (array_chunk($productIds, 100) as $chunk) {
\Bitrix\Iblock\ElementTable::updateMulti($chunk, ['ACTIVE' => 'N']);
// Clear cache for updated elements
foreach ($chunk as $id) {
\Bitrix\Main\Application::getInstance()->getTaggedCache()
->clearByTag('iblock_element_' . $id);
}
}
updateMulti performs a single UPDATE b_iblock_element SET ACTIVE='N' WHERE ID IN (...) — optimal for MySQL. According to Bitrix Developer Documentation, this method bypasses events, making it ideal for pure data manipulation. D7 updateMulti is 100 times faster than CIBlockElement::Update for bulk operations.
However, direct update via ORM bypasses Bitrix events (OnBeforeIBlockElementUpdate, OnAfterIBlockElementUpdate). If other modules are subscribed to these events (CRM, search, custom handlers), you need to use CIBlockElement::Update():
foreach ($productIds as $id) {
\CIBlockElement::Update($id, false, ['ACTIVE' => 'N'], false);
// 4th parameter false — without recalculating access rights
}
For CIBlockElement update with events, use the loop approach.
How to schedule activation for a specific time?
For promotional launches at a specific time, use ACTIVE_FROM / ACTIVE_TO fields. No need to run a script at midnight — just set the dates:
\CIBlockElement::Update($productId, false, [
'ACTIVE' => 'Y',
'ACTIVE_FROM' => '01.12.2024 00:00:00',
'ACTIVE_TO' => '31.12.2024 23:59:59',
]);
Bitrix automatically considers the current date when displaying in catalog. The filter in the bitrix:catalog.section component by default includes ACTIVE_DATE = 'Y', which checks the date range.
Bulk update of custom status properties
A property of type "List" (L) — for example, "Status: New / Promotion / Hit" — is updated via SetPropertyValuesEx. For bulk update:
$enumValues = [];
$enum = \CIBlockPropertyEnum::GetList(
[],
['PROPERTY_ID' => $propertyId, 'VALUE' => 'Promotion']
);
if ($enumItem = $enum->Fetch()) {
$enumValueId = $enumItem['ID'];
}
foreach (array_chunk($productIds, 50) as $chunk) {
foreach ($chunk as $id) {
\CIBlockElement::SetPropertyValuesEx($id, $iblockId, [
'STATUS' => $enumValueId,
]);
}
usleep(50000);
}
Conditional status change
If you need to change the status only for products with certain conditions (e.g., deactivate all products with zero stock), fetch the list first:
// Find products with zero stock
$zeroStock = \Bitrix\Catalog\ProductTable::getList([
'filter' => ['QUANTITY' => 0, 'QUANTITY_TRACE' => 'Y'],
'select' => ['ID', 'IBLOCK_ELEMENT_ID'],
])->fetchAll();
$elementIds = array_column($zeroStock, 'IBLOCK_ELEMENT_ID');
// Deactivate
foreach (array_chunk($elementIds, 100) as $chunk) {
\Bitrix\Iblock\ElementTable::updateMulti($chunk, ['ACTIVE' => 'N']);
}
This query runs in seconds for thousands of products vs. minutes when iterating element-by-element through CIBlockElement::GetList.
Performance and race condition issues
When two operations update products simultaneously (for example, someone clicks the "Activate" button while a cron job runs a bulk update), a race condition occurs. One handler reads old data, another reads new. Result: lost updates or conflicts. The solution is to use transactions and versioning. Before updating, check if the record has changed:
$element = \Bitrix\Iblock\ElementTable::getByPrimary($id)->fetch();
if ($element['MODIFIED'] === $knownModified) {
\Bitrix\Iblock\ElementTable::update($id, ['ACTIVE' => 'N']);
} else {
// Record changed, skip
}
MySQL load increases with the number of products. Updating 100,000 products naively (one by one in a loop) takes hours or even days, as each update requires a separate SQL query. The correct approach: batch update (updating 100-500 at once in a single transaction). With batching, time drops from hours to minutes, and database load decreases by 10-50 times. For catalogs over 100,000 products, we recommend using a cron-agent that updates products in the background to avoid blocking the admin interface.
| Method | Speed | Events triggered | Use case |
|---|---|---|---|
| D7 updateMulti | Very fast (single SQL) | No | Pure data updates, no side effects |
| CIBlockElement::Update | Moderate (one SQL per item) | Yes | When events needed |
| SetPropertyValuesEx | Slow (multiple SQL) | Yes | Custom property updates |
Advanced performance tuning
For catalogs over 1M products, consider using direct SQL via $DB->Query() with chunking and transactions. This bypasses ORM overhead entirely, but requires careful handling of cache invalidation.Integration with 1C
When automatically updating statuses from 1C, you need to distinguish changes coming from 1C from local updates made via the admin panel. Use an additional property flag: if a product was updated from 1C, set FROM_1C = Y and save a timestamp of the last synchronization. This prevents cyclic updates (Bitrix sends a change back to 1C, 1C returns it again) and helps debug synchronization in logs. When updating a product via CommerceML, it's important to preserve local overrides — for example, if a manager manually deactivated a product, it should not be overwritten by 1C during the next synchronization. This technique is ideal for Bitrix product activation in batches.
Deliverables
- Prototyping and performance testing on your product volume.
- Development of a control console (admin panel) for bulk operations.
- Integration with 1C (if needed).
- Race condition handling and safe parallel operations.
- Caching and index optimization.
- Documentation and team training.
- Access rights setup.
- 3 months of post-launch support.
Timeframes and guarantee
Basic implementation of bulk status update — 3–5 days. Full system with admin panel, 1C integration, and conflict handling — 1–2 weeks. Basic implementation starts at $500; full system with all features from $2,000. We guarantee that updating 100,000 products will be completed within a timeframe you define (usually under 5 minutes, compared to 10+ hours manually). Contact us for a consultation — we will analyze your catalog and offer the optimal solution.
With over 5 years of Bitrix development experience and 50+ successful projects, we ensure high-quality delivery.







