PHP
#bitrix#catalog#quantity#stock#php#ccatalogProduct

Обновить остатки товара в каталоге и на складе

Обновление остатков товара через CCatalogProduct::Update с установкой количества на складе через CCatalogStoreProduct.

Как использовать

  1. Общее количество: updateProductQuantity($productId, $quantity). Модуль catalog обязателен.
  2. На складе: updateProductStoreQuantity($productId, $storeId, $quantity); при отсутствии записи создаётся новая.
  3. По всем складам с пересчётом суммы: 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

Проверка

  1. Модуль catalog — перед вызовом убедитесь, что Loader::includeModule('catalog') выполнен и товар есть в каталоге (CCatalogProduct::GetByID не пустой). Иначе функции вернут false.

  2. Общее количество в админке — после updateProductQuantity откройте карточку товара (Товары → каталог). В блоке «Количество» должно отображаться новое значение. Проверьте, что QUANTITY_TRACE и CAN_BUY_ZERO не сбросились (если не меняли их явно).

  3. Количество по складам — при включённом складском учёте проверьте «Остатки по складам» для товара. После updateProductStoreQuantity или updateProductQuantitiesByStores суммы по складам и общее количество должны совпадать с переданными данными.

  4. Диагностика при ошибке — проверьте существование склада CCatalogStore::GetByID($storeId) и наличие товара в каталоге. При добавлении в CCatalogStoreProduct убедитесь, что передаёте числовые ID и неотрицательное количество (если система это допускает).

Типичные ошибки

  • Складской учёт выключен — CCatalogStoreProduct работает только при включённом складском учёте. Иначе используйте только updateProductQuantity для общего количества.
  • Не пересчитывать общее количество — при обновлении только по складам общее QUANTITY в каталоге не обновится автоматически. Используйте updateProductQuantitiesByStores или после ручного обновления складов вызовите updateProductQuantity с суммой по складам.
  • Перезапись QUANTITY_TRACE / CAN_BUY_ZERO — при Update передавайте текущие значения из GetByID (как в примере), иначе можно случайно изменить политику учёта или продажи при нуле.
  • Отрицательное количество — в зависимости от настроек каталога отрицательные значения могут быть недопустимы. Валидируйте входные данные перед вызовом.

Где применять

  • Импорт, 1С, обработчики заказов: обновление остатков после прихода/списания. Продакшн и dev с подключённым каталогом и при необходимости складским учётом.
  • Агенты и фоновые задачи: синхронизация остатков; используйте идемпотентный агент с блокировкой при массовых обновлениях.

Связанные сниппеты: Минимальная цена по типам, Идемпотентный агент с блокировкой, Смена статуса заказа D7.