Как программно получить результат отчета СКД в таблицу значений при наличии параметров с выражениями?

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

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

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

«Ошибка при вызове метода контекста (Инициализировать): Синтаксическая ошибка...»

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

Выясняем причину: почему возникает синтаксическая ошибка?

При интерактивном (стандартном) формировании отчета на форме платформа 1С автоматически исполняет все выражения, заданные в параметрах и вычисляемых полях схемы компоновки данных. Но когда мы переходим к абсолютно программному вызову, поведение платформы меняется в целях безопасности.

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

Решение №1. Включение использования внешних функций при инициализации

Самый простой, быстрый и штатный способ решения этой проблемы — это явное указание процессору компоновки данных на то, что в схеме могут использоваться внешние функции встроенного языка 1С.

Давайте проанализируем синтаксис метода Инициализировать() объекта ПроцессорКомпоновкиДанных:


ПроцессорКомпоновкиДанных.Инициализировать(
    <Макет>, 
    <ВнешниеНаборыДанных>, 
    <ДанныеРасшифровки>, 
    <ВозможностьИспользованияВнешнихФункций>
)

Обратите внимание на четвертый параметр — ВозможностьИспользованияВнешнихФункций. Он принимает булево значение (Истина или Ложь). По умолчанию этот параметр равен Ложь. Чтобы разрешить платформе вычислять выражения параметров, нам достаточно передать туда значение Истина.

Рассмотрим пример готового программного кода для вывода отчета в таблицу значений:


// 1. Получаем схему компоновки данных из макета
СхемаКомпоновкиДанных = ОтчетОбъект.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");

// 2. Получаем настройки по умолчанию или конкретного варианта
ВариантНастройки = СхемаКомпоновкиДанных.ВариантыНастроек.Найти("Основной");
Настройки = ВариантНастройки.Настройки;

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

// 4. Создаем компоновщик макета и генерируем макет для коллекции значений
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;    
Макет = КомпоновщикМакета.Выполнить(
    СхемаКомпоновкиДанных, 
    Настройки, 
    , 
    , 
    Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений")
);

// 5. Инициализируем процессор компоновки С РАЗРЕШЕНИЕМ внешних функций
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// Передаем Истина в качестве 4-го параметра
ПроцессорКомпоновки.Инициализировать(Макет, , , Истина);

// 6. Выводим результат в Таблицу Значений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТаблицаРезультат = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

// Теперь ТаблицаРезультат содержит готовые данные отчета

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

Решение №2. Программное удаление выражений из схемы компоновки

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

Посмотрим на пример кода, который решает эту задачу:


// Получаем схему
СхемаКомпоновкиДанных = ОтчетОбъект.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");

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

// Теперь мы обязаны заполнить значение этого параметра вручную в настройках, 
// так как автовычисление по коду больше не сработает:
ВариантНастройки = СхемаКомпоновкиДанных.ВариантыНастроек.Найти("Основной");
Настройки = ВариантНастройки.Настройки;

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

// Дальнейшая компиляция и вывод пройдут успешно без включения внешних функций
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;    
Макет = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// Четвертый параметр можно оставить по умолчанию (Ложь)
ПроцессорКомпоновки.Инициализировать(Макет);

Этот способ более трудоемок, но гарантирует абсолютную безопасность выполнения кода в закрытых и облачных средах (например, в сервисе 1С:Фреш).

Рекомендация: Использование Компоновщика Настроек

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

Рассмотрим, как правильно инициализировать компоновщик настроек программно:


СхемаКомпоновкиДанных = ОтчетОбъект.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");

// Создаем компоновщик настроек
КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
// Инициализируем его источником доступных настроек нашей схемы
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));
// Загружаем настройки по умолчанию (или из нужного варианта)
КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);

// Заполняем параметры через пользовательские или основные настройки компоновщика
Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Пользователь");
Если Параметр <> Неопределено Тогда
    Параметр.Значение = Справочники.Пользователи.НайтиПоКоду("00001");
    Параметр.Использование = Истина;
EndIf;

// Получаем результирующие настройки для генерации макета
ИтоговыеНастройки = КомпоновщикНастроек.ПолучитьНастройки();

// Генерируем макет
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, ИтоговыеНастройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

Особенности генерации макета для коллекции значений

Когда мы выгружаем результат компоновки данных в таблицу значений с помощью ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений, важно учитывать несколько особенностей структуры получаемой таблицы:

  1. Отсутствие иерархии в привычном виде. Если в отчете настроены группировки с типом «Иерархия», платформа выгрузит все строки в виде плоской таблицы значений. Для понимания уровня вложенности строк вам придется анализировать заполненность колонок или добавлять в схему специальное поле порядка/уровня.
  2. Строки детальных записей и итогов. По умолчанию генератор выводит в результирующую таблицу значений как детальные записи, так и строки общих и группировочных итогов. Если вам нужны только детальные данные без дублирования сумм на разных уровнях группировок, перед выводом рекомендуется отключить вывод общих итогов в настройках СКД или программно удалить группировки верхнего уровня из структуры настроек.

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

← На главную