Контроль неликвидных товаров — одна из ключевых задач любого торгового предприятия. Избыточные запасы, которые мертвым грузом лежат на складе, замораживают оборотные средства и снижают общую рентабельность бизнеса. В линейке решений «Штрих-М: Торговое предприятие» (версий 5 и 7) партионный учет и структура данных имеют свои особенности, из-за чего стандартные подходы к разработке аналитических отчетов могут вызывать трудности у начинающих программистов 1С.
В рамках данной статьи мы подробно разберем, как решить задачу поиска залежавшегося товара. Мы изучим штатные возможности конфигурации, проанализируем типичные ошибки при написании запросов к регистрам накопления, разберем проблему «отсечения новинок» и построим оптимальный, быстродействующий запрос на языке 1С для вывода реальных неликвидов.
Прежде чем приступать к написанию собственного программного кода, давайте выясним, действительно ли в системе нет готового решения. Довольно часто пользователи и даже товароведы не знают обо всех возможностях программы. В конфигурациях «Штрих-М: Торговое предприятие» заложены базовые механизмы для подобного анализа:
Если штатный функционал по каким-то причинам вас не устраивает (например, требуется специфическое оформление или интеграция в сторонний АРМ), перейдем к разработке собственного запроса.
Рассмотрим попытку построения запроса, с которой часто сталкиваются разработчики. Идея проста: получить остатки товаров, получить продажи за период, а затем соединить эти две таблицы левым соединением. Если продаж не было (значение равно NULL), то товар считается неликвидным.
Посмотрим на пример ошибочного кода, который часто приводят на форумах:
ВЫБРАТЬ
ОстаткиНоменклатурыОстаткиИОбороты.Номенклатура,
ОстаткиНоменклатурыОстаткиИОбороты.КоличествоНачальныйОстаток КАК Остатки
ПОМЕСТИТЬ ВТОстатки
ИЗ
РегистрНакопления.ОстаткиНоменклатуры.ОстаткиИОбороты(&Начало, &Конец, МЕСЯЦ, , ) КАК ОстаткиНоменклатурыОстаткиИОбороты
ГДЕ
ОстаткиНоменклатурыОстаткиИОбороты.КоличествоНачальныйОстаток > 0
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ОстаткиНоменклатурыОстаткиИОбороты.Номенклатура,
ОстаткиНоменклатурыОстаткиИОбороты.КоличествоОборот = 0 КАК Продажи
ПОМЕСТИТЬ ВТПродажи
ИЗ
РегистрНакопления.ОстаткиНоменклатуры.ОстаткиИОбороты(&Начало, &Конец, МЕСЯЦ, , ) КАК ОстаткиНоменклатурыОстаткиИОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТОстатки.Номенклатура,
ВТОстатки.Остатки КАК Остатки,
ВТПродажи.Номенклатура КАК Номенклатура1,
ВТПродажи.Продажи КАК Продажи
ИЗ
ВТОстатки КАК ВТОстатки
ЛЕВОЕ СОЕДИНЕНИЕ ВТПродажи КАК ВТПродажи
ПО ВТОстатки.Номенклатура = ВТПродажи.Номенклатура
ГДЕ
ВТПродажи.Номенклатура = NULL // Ошибка!
Проанализируем две критические ошибки, из-за которых данный запрос возвращает либо пустой результат, либо некорректные данные.
Ошибка первая: сравнение с NULL через знак равенства. В теории реляционных баз данных и в языке запросов 1С значение NULL означает отсутствие данных. Оно не равно ничему, даже самому себе. Выражение ВТПродажи.Номенклатура = NULL всегда будет вычисляться как ЛОЖЬ. Для проверки отсутствия записи во второй таблице при левом соединении необходимо использовать специальный оператор ЕСТЬ NULL (в SQL — IS NULL).
Ошибка вторая: булево выражение в поле выборки. В таблице ВТПродажи поле Продажи формируется как КоличествоОборот = 0 КАК Продажи. Это логическое выражение, которое вернет либо ИСТИНА (если оборотов не было), либо ЛОЖЬ (если обороты были). При левом соединении, даже если продаж не было вовсе, строка из таблицы ВТОстатки соединится с записью из ВТПродажи, и поле примет конкретное булево значение, а не ожидаемый NULL. Проверять на отсутствие данных нужно строго ключевое поле связки — саму номенклатуру из правой таблицы.
Если мы просто выберем товары, которые лежат на складе и не продавались за последние два месяца, в этот список неизбежно попадут абсолютно новые позиции, которые поступили на склад буквально несколько дней назад. По ним физически еще не успело пройти продаж, но неликвидом они не являются.
Выясним, как решить эту проблему. Нам необходимо применить так называемый «период охлаждения» или отсечь свежие поступления. Для этого мы должны получить список товаров, по которым были приходы (движения с видом «Приход» по регистру накопления ОстаткиНоменклатуры) за определенный период (например, за последние 14–30 дней). Исключив эти новые товары из итоговой выборки, мы получим только честный, залежавшийся неликвид.
Разберем по шагам логику построения эффективного запроса для поиска неликвидных товаров:
ГДЕ оставляем только те строки, у которых в таблицах продаж и новинок образовался NULL.Рассмотрим пример готового запроса, который можно использовать в консоли отчетов или при разработке собственного отчета:
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоОстаток КАК КоличествоОстаток
ПОМЕСТИТЬ ВТ_ТекущиеОстатки
ИЗ
РегистрНакопления.ОстаткиНоменклатуры.Остатки(&ПериодАнализаКонец, ) КАК Остатки
ГДЕ
Остатки.КоличествоОстаток > 0
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ПродажиОбороты.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ПроданныеТовары
ИЗ
РегистрНакопления.Продажи.Обороты(&ПериодАнализаНачало, &ПериодАнализаКонец, , ) КАК ПродажиОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ОстаткиОбороты.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_СвежиеПоступления
ИЗ
РегистрНакопления.ОстаткиНоменклатуры.Обороты(&ПериодНовинокНачало, &ПериодАнализаКонец, , ) КАК ОстаткиОбороты
ГДЕ
ОстаткиОбороты.КоличествоПриход > 0
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоОстаток КАК ОстатокНаСкладе
ИЗ
ВТ_ТекущиеОстатки КАК Остатки
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПроданныеТовары КАК Продажи
ПО Остатки.Номенклатура = Продажи.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвежиеПоступления КАК Новинки
ПО Остатки.Номенклатура = Новинки.Номенклатура
ГДЕ
Продажи.Номенклатура ЕСТЬ NULL
И Новинки.Номенклатура ЕСТЬ NULL
Рассмотрим подробнее параметры, которые передаются в разработанный нами запрос:
&ПериодАнализаКонец — текущая дата (или дата, на которую мы хотим зафиксировать складские остатки).&ПериодАнализаНачало — дата, определяющая глубину анализа продаж. Как правило, для выявления неликвидов берут период от 60 до 90 дней.&ПериодНовинокНачало — дата, ограничивающая интервал признания товара «новинкой». Обычно этот период составляет от 14 до 30 дней до даты окончания анализа. Если товар пришел в этот интервал, система не будет считать его неликвидом, даже если по нему не было продаж.Использование временных таблиц (конструкция ПОМЕСТИТЬ) позволяет оптимизировать нагрузку на СУБД. Программа сначала подготавливает небольшие плоские таблицы в оперативной памяти, а затем быстро производит левое соединение по индексированным полям. Это обеспечивает высокую скорость работы отчета даже на базах данных с большим количеством номенклатурных позиций и интенсивным товарооборотом.