В практике разработки на платформе 1С:Предприятие часто возникает задача распределения показателей (например, выручки, затрат или оплат) по определенным периодам, которые не кратны стандартным (месяц, квартал), а определяются записями в регистре сведений. Типичный пример — расчет выручки за каждый этап строительства, когда даты начала этапов хранятся в истории изменений, а данные о продажах — в регистрах накопления. Разберем подробно, как реализовать такой отчет в системе компоновки данных (СКД) для конфигурации "Управление торговлей 11".
Рассмотрим исходные данные. У нас имеется регистр сведений ИсторияЭтаповСтроительства, в котором фиксируется дата начала каждого этапа для конкретного клиента. Нам необходимо сопоставить эти интервалы с данными регистра накопления ВыручкаИСебестоимостьПродаж. Основная сложность заключается в том, что в регистре сведений есть только дата начала этапа, а для получения оборотов за период нам нужна и дата окончания.
Проанализируем шаги, которые нам необходимо предпринять:
Если ваша платформа версии ниже 8.3.21, это основной способ получения даты окончания периода. Идея заключается в том, чтобы для каждой записи из регистра сведений найти "следующую" по времени запись для того же клиента. Дата следующей записи и будет являться датой окончания текущего этапа.
Рассмотрим структуру запроса подробнее:
ВЫБРАТЬ
ЭтапыСлева.Клиент КАК Клиент,
ЭтапыСлева.Этап КАК Этап,
ЭтапыСлева.Период КАК ДатаНачала,
МИНИМУМ(ЕСТЬNULL(ЭтапыСправа.Период, ДАТАВРЕМЯ(3999, 12, 31))) КАК ДатаОкончания
ПОМЕСТИТЬ ВТ_Интервалы
ИЗ
РегистрСведений.ИсторияЭтаповСтроительства КАК ЭтапыСлева
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияЭтаповСтроительства КАК ЭтапыСправа
ПО ЭтапыСлева.Клиент = ЭтапыСправа.Клиент
И ЭтапыСлева.Период < ЭтапыСправа.Период
СГРУППИРОВАТЬ ПО
ЭтапыСлева.Клиент,
ЭтапыСлева.Этап,
ЭтапыСлева.Период
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Интервалы.Клиент,
ВТ_Интервалы.Этап,
СУММА(ВыручкаОбороты.ВыручкаОборот) КАК ВыручкаФакт
ИЗ
ВТ_Интервалы КАК ВТ_Интервалы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(,, День, ) КАК ВыручкаОбороты
ПО ВТ_Интервалы.Клиент = ВыручкаОбороты.АналитикаУчетаПоПартнерам.Партнер
И ВыручкаОбороты.Период >= ВТ_Интервалы.ДатаНачала
И ВыручкаОбороты.Период < ВТ_Интервалы.ДатаОкончания
СГРУППИРОВАТЬ ПО
ВТ_Интервалы.Клиент,
ВТ_Интервалы.Этап
Разберем важные нюансы этого запроса. Во-первых, мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Это критически важно для обработки последнего (текущего) этапа. Если использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ, то для последнего этапа не найдется записи "справа" (где дата больше), и текущий этап просто исчезнет из отчета. Во-вторых, функцией ЕСТЬNULL мы подставляем "дату конца времен" (31.12.3999) для текущего открытого этапа, чтобы захватить все продажи по сегодняшний день.
Если вы работаете на современной версии платформы, расчет интервалов можно значительно упростить и ускорить с помощью функции LEAD. Она позволяет заглянуть в следующую строку выборки без тяжелого соединения таблицы с самой собой.
Проанализируем пример использования оконной функции:
ВЫБРАТЬ
История.Клиент,
История.Этап,
История.Период КАК ДатаНачала,
LEAD(История.Период, 1, ДАТАВРЕМЯ(3999, 12, 31)) OVER (PARTITION ПО История.Клиент УПОРЯДОЧИТЬ ПО История.Период) КАК ДатаОкончания
ПОМЕСТИТЬ ВТ_Интервалы_Оконные
ИЗ
РегистрСведений.ИсторияЭтаповСтроительства КАК История
Здесь PARTITION BY (в языке 1С — ПО) разделяет данные по клиентам, а ORDER BY (УПОРЯДОЧИТЬ ПО) гарантирует правильную последовательность этапов. Это решение работает гораздо быстрее на больших объемах данных, так как платформа выполняет обход таблицы один раз.
Выясним, как сделать отчет более гибким и оптимизированным с точки зрения компоновки данных. Вместо одного огромного запроса мы можем использовать два набора данных и связать их на уровне механизмов СКД.
Набор данных 1 (Запрос): Получает интервалы этапов (используя любой из способов выше).
Набор данных 2 (Запрос): Получает обороты из виртуальной таблицы ВыручкаИСебестоимостьПродаж.Обороты. В тексте запроса второго набора мы НЕ указываем период в параметрах виртуальной таблицы явно, либо используем стандартные параметры.
На закладке "Связи наборов данных" мы настраиваем связь:
ДатаНачала из Набора1 в параметр виртуальной таблицы &НачалоПериода, а ДатаОкончания — в &КонецПериода.Это позволит системе для каждого этапа выполнять отдельный оптимизированный запрос к регистру выручки, передавая границы этапа непосредственно в параметры виртуальной таблицы. Это наиболее "правильный" путь с точки зрения архитектуры СКД.
При работе с регистром ВыручкаИСебестоимостьПродаж необходимо учитывать специфику типовой конфигурации:
1. Выбор валюты.
В регистре хранятся суммы в нескольких валютах: управленческого учета, регламентированного учета и валюте взаиморасчетов. Обязательно уточните у бизнес-аналитиков, какая именно сумма должна попадать в отчет (обычно это поле СуммаВыручки для управленческого учета).
2. Учет возвратов.
Регистр является оборотным, и возвраты в нем отражаются сторнирующими записями (уменьшают оборот). Однако, если возврат произошел в периоде, когда этап уже сменился, выручка уменьшится по новому этапу. Если это критично, необходимо анализировать первичные документы или связывать возвраты с документами продажи.
3. Аналитика "Заказ клиента" или "Договор".
Строительство часто ведется в рамках конкретного договора или заказа. Если у одного клиента несколько строек одновременно, простого соединения по полю Партнер будет недостаточно — вы соберете выручку "в одну кучу". В этом случае в регистр сведений "История этапов" и в условия соединения запроса обязательно нужно добавить разрез Договор или Заказ.
4. Оптимизация и индексация.
Поскольку мы соединяем таблицы по периодам и клиентам, убедитесь, что в регистре сведений ИсторияЭтаповСтроительства поле Клиент является ведущим или индексированным. В противном случае, при накоплении большой истории, отчет начнет существенно замедляться.
Для реализации отчета по выручке за этапы строительства мы рассмотрели три подхода: от классического self-join до современных оконных функций и механизмов связей СКД. Наиболее эффективным для поддержки и расширения является метод с использованием связей наборов данных, так как он позволяет легко добавлять новые показатели из других регистров (например, оплаты или себестоимость), просто добавляя новые наборы данных и связывая их по тем же временным интервалам.