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