Обновить остатки товара в каталоге и на складе
Обновление остатков товара через CCatalogProduct::Update с установкой количества на складе через CCatalogStoreProduct.
Как использовать
- Общее количество: updateProductQuantity($productId, $quantity). Модуль catalog обязателен.
- На складе: updateProductStoreQuantity($productId, $storeId, $quantity); при отсутствии записи создаётся новая.
- По всем складам с пересчётом суммы: updateProductQuantitiesByStores($productId, [storeId => qty]).
В импортах, синхронизации с 1С или обработчиках заказов нужно обновлять остатки товара: общее количество в каталоге и/или количество по складам. Проблема: обновить только нужные поля, не затирая QUANTITY_TRACE и CAN_BUY_ZERO; при складском учёте — создавать или обновлять записи в CCatalogStoreProduct и при необходимости пересчитывать общее количество. Симптомы: некорректные остатки после импорта, «пропажа» товара при нуле на складе, дубли или ошибки при обновлении склада. Ниже — функции для общего количества, для склада и пакетное обновление по складам с пересчётом; проверка и ограничения.
Решение
Обновление остатков в каталоге и на конкретном складе. Модуль catalog обязателен; для складов нужен включённый складской учёт.
use Bitrix\Main\Loader;
/** Обновить общее количество товара в каталоге */
function updateProductQuantity($productId, $quantity) {
if (!Loader::includeModule('catalog')) {
return false;
}
$product = CCatalogProduct::GetByID($productId);
if (!$product) {
return false;
}
$fields = [
'QUANTITY' => $quantity,
'QUANTITY_TRACE' => $product['QUANTITY_TRACE'] ?: 'Y',
'CAN_BUY_ZERO' => $product['CAN_BUY_ZERO'] ?: 'N'
];
return CCatalogProduct::Update($productId, $fields);
}
/** Обновить количество на конкретном складе */
function updateProductStoreQuantity($productId, $storeId, $quantity) {
if (!Loader::includeModule('catalog')) {
return false;
}
$store = CCatalogStore::GetByID($storeId);
if (!$store) {
return false;
}
$storeProduct = CCatalogStoreProduct::GetList(
[],
['PRODUCT_ID' => $productId, 'STORE_ID' => $storeId],
false,
false,
['ID', 'AMOUNT']
)->Fetch();
if ($storeProduct) {
return CCatalogStoreProduct::Update($storeProduct['ID'], ['AMOUNT' => $quantity]);
}
return CCatalogStoreProduct::Add([
'PRODUCT_ID' => $productId,
'STORE_ID' => $storeId,
'AMOUNT' => $quantity
]);
}
/** Обновить остатки по складам и пересчитать общее количество */
function updateProductQuantitiesByStores($productId, $storeQuantities) {
if (!Loader::includeModule('catalog')) {
return false;
}
$totalQuantity = 0;
foreach ($storeQuantities as $storeId => $quantity) {
if (updateProductStoreQuantity($productId, $storeId, $quantity)) {
$totalQuantity += $quantity;
}
}
return updateProductQuantity($productId, $totalQuantity);
}
Примеры вызова:
updateProductQuantity(12345, 100);
updateProductStoreQuantity(12345, 1, 50);
updateProductQuantitiesByStores(12345, [
1 => 30,
2 => 20,
3 => 10
]);
// Общее количество станет 60
Проверка
-
Модуль catalog — перед вызовом убедитесь, что
Loader::includeModule('catalog')выполнен и товар есть в каталоге (CCatalogProduct::GetByIDне пустой). Иначе функции вернутfalse. -
Общее количество в админке — после
updateProductQuantityоткройте карточку товара (Товары → каталог). В блоке «Количество» должно отображаться новое значение. Проверьте, что QUANTITY_TRACE и CAN_BUY_ZERO не сбросились (если не меняли их явно). -
Количество по складам — при включённом складском учёте проверьте «Остатки по складам» для товара. После
updateProductStoreQuantityилиupdateProductQuantitiesByStoresсуммы по складам и общее количество должны совпадать с переданными данными. -
Диагностика при ошибке — проверьте существование склада
CCatalogStore::GetByID($storeId)и наличие товара в каталоге. При добавлении в CCatalogStoreProduct убедитесь, что передаёте числовые ID и неотрицательное количество (если система это допускает).
Типичные ошибки
- Складской учёт выключен — CCatalogStoreProduct работает только при включённом складском учёте. Иначе используйте только
updateProductQuantityдля общего количества. - Не пересчитывать общее количество — при обновлении только по складам общее QUANTITY в каталоге не обновится автоматически. Используйте
updateProductQuantitiesByStoresили после ручного обновления складов вызовитеupdateProductQuantityс суммой по складам. - Перезапись QUANTITY_TRACE / CAN_BUY_ZERO — при Update передавайте текущие значения из GetByID (как в примере), иначе можно случайно изменить политику учёта или продажи при нуле.
- Отрицательное количество — в зависимости от настроек каталога отрицательные значения могут быть недопустимы. Валидируйте входные данные перед вызовом.
Где применять
- Импорт, 1С, обработчики заказов: обновление остатков после прихода/списания. Продакшн и dev с подключённым каталогом и при необходимости складским учётом.
- Агенты и фоновые задачи: синхронизация остатков; используйте идемпотентный агент с блокировкой при массовых обновлениях.
Связанные сниппеты: Минимальная цена по типам, Идемпотентный агент с блокировкой, Смена статуса заказа D7.