16.11.2017

Складской учет

Интернет проекты растут, увеличение товарной базы приводит к поиску инструмента для учета текущего количества и резервов товара на складах. Для проектов на базе Битрикс существует два решения “из коробки”: учет на базе 1С или складской учет. Решение на базе 1С имеет свои особенности и проблемы, которые в этой статье рассматриваться не будут. Перед нами же встала задача перехода действующего интернет магазина на режим работы со складским учетом.

В режиме складского учета в административной части Битрикс появляются документы. В целом именно по ним учет и ведется. Чтобы перейти на него, необходимо поставить соответствующую галочку в настройках модуля “Торговый каталог”, но перед этим Битрикс сообщит Вам, что необходимо обнулить количество и резервы товаров, для этого предусмотрены служебные процедуры ниже в настройках во вкладке “Очистка каталога”. Нет проблем, обнулили и включили складской учет. Далее необходимо создать документы прихода товаров на склады, чтобы их количество перестало быть нулевым.

Проблемы

И тут возникает проблема. На рабочем проекте уже есть активные невыполненные заказы. В настройках модуля “Интернет-магазин” задано держать таким заказам резервы.

Мы же обнулили количество и резервы товара, но в заказах товары есть, а значит, они должны быть в резерве.

    Для полноты описания надо понимать, что у заказов есть флаги:
  • Резерв – скрытый параметр, указывает, хранит ли заказ товары в резерве;
  • Отгрузка – указывает, списаны ли товары со склада.

Если отменить заказ без флага отгрузки, то его товары вернутся на склады, а резерв уменьшится и станет отрицательным.

До отмены заказа После отмены заказа

Отдельно надо сказать про настройку “Снятие резервов (через сколько дней)”. Спустя 3 дня после создания заказа, товары стали возвращаться из резерва, количество и резервы словно сошли с ума. И первое, что мы сделали, поставили снятие резервов в ноль. Теперь мы точно знаем, что заказ держит товары в резерве, если не установлен флаг отгрузки.

Решение

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

Шаг 1. Выключение складского учета и резервирования.

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

COption::SetOptionString('catalog', 'default_use_store_control', "N");// складской учет
COption::SetOptionString('catalog', 'enable_reservation', "N");// резервирование

Шаг 2. Деактивация документов.

Необходимо деактивировать документы прихода товара.

$arDocs = CCatalogDocs::GetList(
	array("ID" => "ASC"),
	array("DOC_TYPE" => "A", "STATUS" => "Y")
);

while ($Doc = $arDocs->Fetch())
	CCatalogDocs::cancellationDocument($Doc["ID"]);

Шаг 3. Выставление заказам флагов по статусам.

На данном шаге нужно привести в порядок флаги в заказах, логика завязана на конкретные статусы проекта. Основной момент: необходимо использовать CSaleOrder::Update, так как данный метод не проводит дополнительных проверок при установке флагов и не завершается с ошибками (например, устанавливаем флаг отгрузки, но не хватает количества товара, данному методу это неважно, и он установит флаг как нам необходимо).

У отправленных и завершенных заказов должен стоять флаг отгрузки и доставки, резервы они держать уже не должен. У новых неотправленных заказов снимаем флаг резерва (по логике наоборот, выставим на 7 шаге) и отменяем флаги отгрузки и доставки. У отмененных заказов снимаем все флаги.

Итак, конкретно по действиям, в переменной $value либо “Y” либо “N”:

// разрешение доставка
$arFields["ALLOW_DELIVERY"] = $value;
$arFields["EMP_ALLOW_DELIVERY_ID"] = $userId;
$arFields["DATE_ALLOW_DELIVERY"] = date("d.m.Y H:i:s");

// отгрузка
$arFields["DEDUCTED"] = $value;
$arFields["EMP_DEDUCTED_ID"] = $userId;
$arFields["STORE_ID"] = 1;// благо склад был один
$arFields["DATE_DEDUCTED"] = date("d.m.Y H:i:s");

// резерв
$arFields["RESERVED"] = $value;

Завершающее действие:

CSaleOrder::Update($OrderID, $arFields);

У флагов отгрузки и резерва есть особенность: надо продублировать изменение для всех товаров корзины заказа.

$arBasket = CSaleBasket::GetList(
	array(),
	array("ORDER_ID" => $OrderID)
);

$arFields["RESERVED"] = $value;// или DEDUCTED
while($basket = $arBasket->Fetch())
	CSaleBasket::Update($basket["ID"], $arFields);

Кроме того, у флага отгрузки есть еще одна особенность: установка флага создает barcod-запись, ее нужно либо удалять при отмене флага, либо создавать при его установке.

$arStoreBarcode = CSaleStoreBarcode::GetList(
	array(),
	array("BASKET_ID" => $basket["ID"])
);

$barcodeIDs = array();
while ($storeBarcode = $arStoreBarcode->Fetch())
	$barcodeIDs[] = $storeBarcode["ID"];

if ($value == "Y")
{
	foreach ($barcodeIDs as $id)
		CSaleStoreBarcode::Delete($id);

	CSaleStoreBarcode::Add(array(
		"BASKET_ID"   => $basket["ID"],
		"BARCODE"     => "",
		"STORE_ID"    => 1,
		"QUANTITY"    => intval($basket["QUANTITY"]),
		"CREATED_BY"  => intval($userId),
		"MODIFIED_BY" => intval($userId),
		)
	);
}
elseif($value == "N")
	foreach ($barcodeIDs as $id)
		CSaleStoreBarcode::Delete($id);	

Шаг 4. Обнуление количества и резервов товаров.

И только теперь нужно обнулять у товаров количество и резервы. Этот шаг является завершающим для подготовительного этапа перед переходом на режим работы со складским учетом. Код я приводить не буду, так как есть готовый функционал в административной части Битрикс.

Шаг 5: Включение складского учета и резервирования.

Включаем резервирование товаров и складской учет аналогично шагу 1.

Шаг 6: Активация документов.

Следующий шаг требует, чтобы на складах были товары, изменять количество товара теперь возможно только через документы складского учета, иначе не гарантируется корректная работа резервирования и учета количества товара. Активируем документы, изменные на втором шаге. Код аналогичен, изменяется только метод.

CCatalogDocs::conductDocument($Doc["ID"]);

Шаг 7: Выставление заказам хранения резервов и отгрузки.

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

CSaleOrder::DeliverOrder($OrderID, $value);// флаг разрешения отгрузки
CSaleOrder::DeductOrder($OrderID, $value);// флаг отгрузки
CSaleOrder::ReserveOrder($OrderID, $value);// флаг резерва

Заключение

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

Решение написано и протестировано в виде единого скрипта. Все шаги разбиты на отдельные ajax-запросы, выполняющиеся последовательно. Каждый шаг выполняется за несколько итераций во избежание превышения максимального времени выполнения php-скрипта.