Перейти к содержимому

Низкие остатки - пороги, отчёт закупщику, поведение витрины

Настраиваем контроль кончающегося товара: пороги остатка, отчёт закупщику, поведение витрины при нуле и проверку после каждого обмена с учётной системой.

Что нужно знать заранее

Единый порог для всего каталога работает плохо. Для расходников пять штук - это почти ноль, а для дорогой техники пять штук на складе означают спокойный запас на месяц вперёд.

Поэтому порог делают двухуровневым. Общее значение задаётся настройкой проекта, а у отдельных позиций и категорий появляется собственный порог в свойстве товара.

Поведение витрины при нулевом остатке - решение бизнеса, а не разработчика. Товар можно скрыть, оставить с пометкой об отсутствии или продавать под заказ с увеличенным сроком доставки.

Шаги

  1. Договориться с заказчиком об общем пороге и о позициях с особыми условиями.
  2. Завести свойство товара под индивидуальный порог и заполнить его для этих позиций.
  3. Собирать список позиций ниже порога заданием по расписанию после обмена.
  4. Отправлять отчёт закупщику файлом или сообщением с прямыми ссылками на товары.
  5. Настроить поведение витрины при нуле и проверить его глазами покупателя.

Решение

Собираем товары ниже общего порога:

\Bitrix\Main\Loader::includeModule('catalog');
$threshold = (int)\Bitrix\Main\Config\Option::get('vendor.shop', 'low_stock', 5);
$rs = \CIBlockElement::GetList(['CATALOG_QUANTITY' => 'ASC'],
['IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y', '<=CATALOG_QUANTITY' => $threshold],
false, ['nTopCount' => 500], ['ID', 'NAME', 'CATALOG_QUANTITY', 'PROPERTY_MIN_STOCK']);

Выборка сразу забирает и остаток, и индивидуальный порог позиции. Один запрос вместо двух заметно экономит время на каталоге в десятки тысяч товаров с торговыми предложениями.

Учитываем индивидуальный порог позиции:

$low = [];
while ($row = $rs->Fetch()) {
$own = (int)($row['PROPERTY_MIN_STOCK_VALUE'] ?? 0);
$limit = $own > 0 ? $own : $threshold; // своё значение важнее общего
if ((float)$row['CATALOG_QUANTITY'] <= $limit) { $low[] = $row; }
}
printf("позиций ниже порога: %d\n", count($low));

Отправляем отчёт закупщику:

$lines = array_map(static fn($r) => sprintf('%s - остаток %s (ID %d)',
$r['NAME'], $r['CATALOG_QUANTITY'], $r['ID']), array_slice($low, 0, 30));
\CEvent::Send('VENDOR_LOW_STOCK', SITE_ID,
['EMAIL' => 'zakupki@example.com', 'LIST' => implode("\n", $lines), 'TOTAL' => count($low)]);
// в письмо кладут первые тридцать строк, полный список прикладывают файлом

Отчёт полезен только со ссылками на карточки товаров. Список названий заставляет закупщика искать позиции руками, и через неделю такой отчёт перестают открывать вовсе.

Настраиваем поведение витрины при нуле:

\CCatalogProduct::Update($productId, [
'QUANTITY_TRACE' => 'Y', // остаток списывается при заказе
'CAN_BUY_ZERO' => 'N', // продавать при нуле нельзя
'SUBSCRIBE' => 'Y', // покупателю предлагают подписку на поступление
]);

Проверяем остатки после обмена:

\Bitrix\Main\EventManager::getInstance()->addEventHandler('catalog',
'OnSuccessCatalogImport1C', [LowStock::class, 'check']);
// обмен меняет остатки пачками, и проверку логично вешать на его завершение
// событие срабатывает по окончании каждого файла выгрузки, а не раз за сеанс

Типичные проблемы

Отчёт по остаткам приходит с сотнями позиций.

Порог задан одним числом для всего каталога без учёта оборачиваемости позиций. Для расходников и для дорогой техники пороги задают отдельными значениями.

Покупатели заказывают товар, которого нет на складе.

У товара выключено списание остатка либо разрешена покупка при нуле. Поведение при нулевом остатке настраивают осознанно и проверяют на витрине.

Отчёт приходит до обмена и показывает вчерашние числа.

Проверка висит на расписании и не связана с завершением обмена с учётной системой. Её вешают на событие окончания обмена, а не на фиксированное время.

Закупщик перестал открывать письмо об остатках.

В письме одни названия без ссылок и без указания, что именно изменилось. В отчёт добавляют ссылки на карточки и отмечают позиции, ушедшие в ноль впервые.

Товар с остатком у одного склада считается отсутствующим.

Проверка смотрит общий остаток, а витрина показывает наличие по конкретному складу. Правила подсчёта наличия держат одинаковыми в отчёте и на витрине.

Частые вопросы

Какой порог выбрать по умолчанию?

Тот, при котором закупка успевает приехать до нуля с запасом. Число зависит от срока поставки и от скорости продаж конкретной группы товаров.

Где хранить индивидуальный порог?

В свойстве товара или в свойстве раздела для целой группы позиций. Общий порог остаётся в настройках и работает для всех остальных товаров.

Скрывать товар без остатка или оставлять?

Это решение бизнеса: скрытие бережёт покупателя от разочарования, показ сохраняет позиции в поиске. Компромисс - оставить карточку с пометкой и подпиской на поступление.

Как часто присылать отчёт?

Раз в день после основного обмена и по запросу закупщика. Более частые письма превращаются в шум и перестают читаться.

Считать остаток по всем складам сразу?

Так, как считает витрина: иначе отчёт и сайт будут спорить между собой. Если наличие показывается по складу, отчёт тоже собирают по складам.

Смежное

Первоисточник