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