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