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