Можно ли в вычисляемых полях СКД использовать функции из модуля объекта внешнего отчета?

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

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

Архитектурное ограничение платформы 1С

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

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


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

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

Рассмотрим рабочие альтернативы, позволяющие обойти данное ограничение.

Способ 1. Использование встроенных функций языка выражений СКД или запроса

Прежде чем реализовывать обходные пути с вызовом процедур из модулей, оценим, нельзя ли решить задачу стандартными средствами СКД. Начиная с платформы 8.3.20, язык выражений СКД существенно расширен. В нем доступны конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ, системные функции ЕСТЬNULL, Формат, Целое, Окр, ЗначениеЗаполнено, а также богатый набор строковых функций: СтрДлина, ВРег, НРег, СтрНайти, СтрЗаменить, СтрШаблон, СтрСравнить, СтрНачинаетсяС, СтрЗаканчиваетсяНа, СтрРазделить, СтрСоединить, СтрСодержит.

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

Способ 2. Подготовка данных в наборе данных — объекте

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

  1. В схеме компоновки данных создаем Набор данных - Объект и задаем имя содержащейся таблицы (например, ТаблицаДанных).
  2. Описываем поля набора данных, включая поле для результата нашего сложного расчета.
  3. В модуле объекта внешнего отчета переопределяем событие ПриКомпоновкеРезультата.
  4. Формируем исходную таблицу значений запросом, обходим ее в цикле, применяем к строкам функции модуля объекта и передаем готовую таблицу во внешний набор данных:

Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Получаем исходные данные запросом
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура,
        |   КоличествоОстаток КАК Количество
        |ИЗ
        |   РегистрНакопления.ТоварыНаСкладах.Остатки";
    Таблица = Запрос.Выполнить().Выгрузить();
    
    // Добавляем колонку для вычисленного значения
    Таблица.Колонки.Добавить("РезультатРасчета");
    
    Для Каждого Стр Из Таблица Цикл
        // Вызываем функцию модуля объекта отчета
        Стр.РезультатРасчета = МояЭкспортнаяФункция(Стр.Номенклатура, Стр.Количество);
    КонецЦикла;
    
    // Передаем заполненную таблицу в СКД
    ВнешниеНаборы = Новый Структура("ТаблицаДанных", Таблица);
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы, ДанныеРасшифровки, Истина);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Способ 3. Передача контекста отчета через параметр и функцию БСП

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

Разберем этот алгоритм:

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

Способ 4. Программный обход вывода результата

Еще один вариант, позволяющий применить расчеты модуля объекта без общего модуля — перехват записей на этапе вывода табличного документа:

  1. В процедуре ПриКомпоновкеРезультата отключаем стандартную обработку (СтандартнаяОбработка = Ложь).
  2. Формируем макет и инициализируем ПроцессорКомпоновкиДанных.
  3. В цикле получаем сформированные строки через метод Следующий() объекта ПроцессорКомпоновкиДанных.
  4. В получаемом элементе результата модифицируем выводимые значения, вызывая нужную функцию модуля объекта отчета, после чего передаем элемент в ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент.ВывестиЭлемент().

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

← На главную