Одной из частых задач в практике программиста 1С является анализ оборачиваемости запасов. В частности, возникает необходимость выделить товары, которые числятся на остатках, но по которым не было совершено ни одной продажи за определенный интервал времени. В этой статье мы разберем, как правильно составить такой запрос, какие виртуальные таблицы использовать и как избежать типичных ошибок при работе с датами в консоли запросов.
Для того чтобы получить список товаров, которые не продавались, нам нужно опираться на данные регистров накопления. В большинстве типовых и самописных конфигураций для этого подходят регистры ТоварыНаСкладах, ОстаткиТоваров или специализированный регистр Продажи. Рассмотрим ситуацию на примере универсального регистра остатков.
Основная логика заключается в следующем: нам необходимо найти записи, где начальный остаток был больше нуля (или приход был в этом периоде), но при этом поле расхода за период осталось пустым или равным нулю. Для этого эффективнее всего использовать виртуальную таблицу ОстаткиИОбороты.
Проанализируем структуру базового запроса, который позволяет увидеть движения товара:
ВЫБРАТЬ
ОстаткиОстаткиИОбороты.Товар КАК Товар,
ОстаткиОстаткиИОбороты.КоличествоНачальныйОстаток КАК НачальныйОстаток,
ОстаткиОстаткиИОбороты.КоличествоКонечныйОстаток КАК КонечныйОстаток,
ОстаткиОстаткиИОбороты.КоличествоПриход КАК Приход,
ОстаткиОстаткиИОбороты.КоличествоРасход КАК Расход
ИЗ
РегистрНакопления.Остатки.ОстаткиИОбороты(&ДатаНачала, &ДатаОкончания, , , ) КАК ОстаткиОстаткиИОбороты
ГДЕ
ОстаткиОстаткиИОбороты.КоличествоРасход = 0
В данном примере мы обращаемся к виртуальной таблице ОстаткиИОбороты. Условие в секции ГДЕ отсекает все товары, по которым был зафиксирован расход. Однако стоит учитывать, что "Расход" в регистре остатков может означать не только продажу, но и списание или перемещение. Если ваша цель — именно продажи, стоит использовать регистр Продажи или анализировать поле Регистратор.
Часто при тестировании запроса в консоли возникает желание "жестко" прописать даты прямо в тексте запроса, не используя таблицу параметров. Для этого в языке запросов 1С предусмотрена конструкция ДАТАВРЕМЯ. Рассмотрим подробнее, как ее использовать и какие ошибки допускают новички.
Синтаксис функции: ДАТАВРЕМЯ(Год, Месяц, День, Час, Минута, Секунда). Важно помнить:
Пример корректного использования дат в условии:
ГДЕ
Партия.Дата МЕЖДУ ДАТАВРЕМЯ(2022, 2, 1, 0, 0, 0) И ДАТАВРЕМЯ(2022, 2, 28, 23, 59, 59)
Тем не менее, мы рекомендуем всегда использовать параметры (&ДатаНачала). Это позволяет платформе 1С оптимизировать план запроса и делает ваш код универсальным. В консоли запросов значения параметров можно задать в специальной таблице, что гораздо удобнее правки текста.
Иногда использование таблицы ОстаткиИОбороты с фильтром по расходу может быть недостаточно точным (например, если товар вообще не числился на складе в этом периоде, он не попадет в выборку). Выясним, как составить запрос, который найдет все товары из справочника Номенклатура, по которым не было продаж.
Для этого мы проанализируем ситуацию с помощью соединения двух таблиц: полного списка номенклатуры и таблицы оборотов продаж.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрНакопления.Продажи.Обороты(&ДатаНачала, &ДатаОкончания, , ) КАК ПродажиОбороты
ПО
Номенклатура.Ссылка = ПродажиОбороты.Номенклатура
ГДЕ
Номенклатура.ЭтоГруппа = ЛОЖЬ
И ПродажиОбороты.Номенклатура ЕСТЬ NULL
Разберем по шагам, как работает этот механизм:
Номенклатура.ЛЕВОЕ СОЕДИНЕНИЕ, чтобы товары, которых нет в регистре продаж, не исчезли из итоговой таблицы.ГДЕ мы проверяем поле ПродажиОбороты.Номенклатура ЕСТЬ NULL. Если продажи по товару были, это поле будет заполнено ссылкой. Если продаж не было — там будет NULL.ЭтоГруппа = ЛОЖЬ), чтобы в списке были только конечные товары.Если справочник номенклатуры очень большой, а период анализа длительный, прямой запрос с ЛЕВОЕ СОЕДИНЕНИЕ может работать медленно. В таких случаях мы используем временные таблицы. Сначала выберем список проданных товаров во временную таблицу, а затем исключим их из общего списка.
// Помещаем проданные товары во временную таблицу
ВЫБРАТЬ РАЗЛИЧНЫЕ
Продажи.Номенклатура КАК Товар
ПОМЕСТИТЬ ПроданныеТовары
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
;
// Выбираем номенклатуру, которой нет во временной таблице
ВЫБРАТЬ
Номенклатура.Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка НЕ В (ВЫБРАТЬ Товар ИЗ ПроданныеТовары)
И НЕ Номенклатура.ЭтоГруппа
Этот метод позволяет платформе более эффективно использовать индексы. Обратите внимание на оператор НЕ В — он проверяет отсутствие ссылки в сформированном списке проданных товаров.
Мы рассмотрели несколько способов получения списка непроданных товаров. Выбор конкретного метода зависит от вашей конфигурации:
ОстаткиИОбороты с фильтром КоличествоРасход = 0.ЛЕВОЕ СОЕДИНЕНИЕ с проверкой на NULL.ДАТАВРЕМЯ, чтобы не потерять данные за последние секунды суток.