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