Как в СКД получить реквизит из параметра-документа для передачи в виртуальную таблицу?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При создании отчетов на системе компоновки данных (СКД) часто возникает ситуация: пользователь выбирает в качестве параметра конкретный документ, а данные для отчета необходимо получить из периодического регистра сведений на момент времени или дату этого документа. Казалось бы, логично обратиться к реквизиту параметра напрямую через точку, однако разработчик сталкивается с синтаксическими ошибками платформы. Рассмотрим подробно, почему так происходит, и разберем все работающие способы решения этой задачи.

Почему возникают ошибки при попытке обратиться к реквизиту параметра

Разберем типичные ошибки, которые совершают разработчики при попытке извлечь дату из выбранного документа:

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

Способ 1. Использование функции общего модуля в выражении параметра

Самый быстрый и лаконичный способ получить дату документа без создания пользовательской формы отчета — обратиться к стандартной серверной функции через колонку «Выражение» на вкладке «Параметры» схемы компоновки данных.

Посмотрим, как настраивается этот механизм:

  1. На вкладке Параметры создаем параметр для документа (например, ДокументСсылка с типом ссылки на нужный документ). Включаем для него доступность пользователю.
  2. Создаем второй параметр — Период с типом Дата. Снимаем флаг доступности для пользователя, чтобы не загромождать интерфейс.
  3. В колонке Выражение параметра Период прописываем вызов функции, которая считывает реквизит ссылки из базы данных: ОбщегоНазначения.ЗначениеРеквизитаОбъекта(&ДокументСсылка, "Дата") Если требуется включить весь день документа или получить срез на конец дня, можно обернуть вызов в стандартную функцию компоновки:
    
    КонецПериода(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(&ДокументСсылка, "Дата"), "День")
    
  4. В тексте запроса в параметрах виртуальной таблицы среза указываем наш служебный параметр:
    
    ВЫБРАТЬ
        Курсы.Валюта,
        Курсы.Курс
    ИЗ
        РегистрСведений.КурсыВалют.СрезПоследних(&Период, ) КАК Курсы
    

Важное ограничение: общий модуль, в котором находится вызываемая функция, должен иметь установленные флаги Сервер и Вызов сервера. Если отчет открывается в безопасном режиме (например, в качестве внешнего отчета в базе с жесткими профилями безопасности БСП), платформа может ограничить выполнение методов общих модулей из выражений СКД.

Способ 2. Связи наборов данных в СКД (без единой строчки кода)

Если конфигурация не типовая, функции чтения реквизитов отсутствуют или отчет должен безопасно работать в любых условиях, используем штатную архитектуру СКД — связь двух наборов данных.

Разберем по шагам создание схемы:

  1. Создаем НаборДанных1 (тип «Запрос»), который извлекает дату переданного документа:
    
    ВЫБРАТЬ
        Док.Ссылка КАК ДокументСсылка,
        Док.Дата КАК ДатаДокумента
    ИЗ
        Документ.ПоступлениеТоваровУслуг КАК Док
    ГДЕ
        Док.Ссылка = &ДокументСсылка
    
  2. Создаем НаборДанных2 (тип «Запрос»), который получает информацию из регистра сведений. В параметрах виртуальной таблицы объявляем параметр &ПериодСреза:
    
    ВЫБРАТЬ
        Цены.Номенклатура,
        Цены.Цена
    ИЗ
        РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ПериодСреза, ) КАК Цены
    
  3. Переходим на вкладку Связи наборов данных и добавляем новую строку:
    1. Источник связи: НаборДанных1
    2. Приемник связи: НаборДанных2
    3. Условие связи: можно оставить пустым или сопоставить поля, если требуется фильтрация.
    4. Параметры: указываем имя параметра виртуальной таблицы — ПериодСреза.
    5. Выражение источника: выбираем поле ДатаДокумента.

При таком подходе СКД сначала выполнит первый запрос, получит точную дату выбранного документа и автоматически передаст ее в качестве значения параметра &ПериодСреза во второй запрос. Создавать форму отчета или писать программный код не требуется.

Способ 3. Переопределение параметра в модуле отчета

Когда требуется строгий контроль выполнения и отчет встроен в конфигурацию, можно применить стандартный серверный обработчик в модуле объекта отчета, не создавая отдельную экранную форму.

Проанализируем пример кода для модуля объекта отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    // Получаем значение пользовательского параметра с документом
    ЭлементПараметраДок = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("ДокументСсылка");
    
    Если ЭлементПараметраДок <> Неопределено И ЗначениеЗаполнено(ЭлементПараметраДок.Значение) Тогда
        // Находим служебный параметр периода для виртуальной таблицы
        ЭлементПараметраПериод = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Период");
        
        Если ЭлементПараметраПериод <> Неопределено Тогда
            // Устанавливаем дату документа в параметр периода
            ЭлементПараметраПериод.Значение = ЭлементПараметраДок.Значение.Дата;
            ЭлементПараметраПериод.Использование = Истина;
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Этот метод гарантированно отрабатывает как при формировании отчета пользователем, так и при фоновом выполнении регламентными заданиями.

Способ 4. Получение среза через соединение с физической таблицей

Если в силу архитектуры необходимо выполнить всю выборку исключительно в рамках одного запроса без связей наборов СКД и без внешних функций, срез последних можно рассчитать вручную через физическую таблицу регистра.

Посмотрим на пример структуры такого запроса:


ВЫБРАТЬ
    Док.Дата КАК ДатаСреза
ПОМЕСТИТЬ ВТ_Параметры
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Док
ГДЕ
    Док.Ссылка = &ДокументСсылка
;

ВЫБРАТЬ
    Регистр.Номенклатура КАК Номенклатура,
    МАКСИМУМ(Регистр.Период) КАК Период
ПОМЕСТИТЬ ВТ_МаксПериоды
ИЗ
    ВТ_Параметры КАК Параметры
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Регистр
        ПО Регистр.Период <= Параметры.ДатаСреза
СГРУППИРОВАТЬ ПО
    Регистр.Номенклатура
;

ВЫБРАТЬ
    Макс.Номенклатура,
    Цены.Цена
ИЗ
    ВТ_МаксПериоды КАК Макс
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
        ПО Макс.Номенклатура = Цены.Номенклатура
        И Макс.Период = Цены.Период

Этот метод более ресурсоемкий для СУБД по сравнению со штатной виртуальной таблицей СрезПоследних, однако он полностью автономен и не зависит от ограничений выражений СКД.

← На главную