При программном формировании отчетов на базе системы компоновки данных (СКД), особенно при использовании связок из нескольких схем или передаче данных через внешние наборы (таблицы значений), разработчики нередко сталкиваются с исключением: «Ошибка компоновки макета по причине: Поле не найдено [ИмяПоля]». Разберем по шагам, почему возникает эта ошибка, где сохраняются конфликтные поля и как правильно выстроить работу со структурой настроек СКД.
Рассмотрим типовой сценарий: в первой схеме или отдельным запросом формируется таблица значений, после чего ее колонки динамически добавляются в коллекцию полей набора данных второй схемы компоновки данных:
КоллекцияПолей = СхемаКомпоновкиДанных.НаборыДанных[0].Поля;
КоллекцияПолей.Очистить();
Для Каждого Колонка Из ТЗ_.Колонки Цикл
ПолеНабора = КоллекцияПолей.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ПолеНабора.ПутьКДанным = Колонка.Имя;
ПолеНабора.Поле = Колонка.Имя;
КонецЦикла;
При первой попытке вывести отчет все отрабатывает штатно. Однако стоит пользователю или коду установить отбор, как генератор макета выбрасывает исключение с указанием поля, которого вроде бы нет в текущей выборке (например, Организация при группировке по предприятию).
Выясним причину такого поведения. Генератор макета компоновки анализирует не только текущий состав полей набора данных, но и все примененные настройки. Ошибка возникает тогда, когда в настройках отчета присутствует обращение к полю, отсутствующему в схеме. Чаще всего поле «застревает» в следующих местах:
ПолучитьМакет(), в ее исходных настройках могли остаться предопределенные отборы.КомпоновщикНастроек.ПользовательскиеНастройки поверх основных. Если пользователь ранее устанавливал фильтр по полю Организация, этот элемент сохраняется в пользовательских настройках даже после программного изменения колонок.Структура).Разберем самое прямое решение ситуации, когда во второй схеме остались неактуальные отборы. Перед инициализацией макета необходимо очистить условия как в основных, так и во вложенных элементах структуры:
// Очистка отборов верхнего уровня
КомпоновщикНастроек.Настройки.Отбор.Элементы.Очистить();
КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Очистить();
// Если используется диаграмма или вложенные группировки, обходим структуру
Для Каждого ЭлементСтруктуры Из КомпоновщикНастроек.Настройки.Структура Цикл
ЭлементСтруктуры.Отбор.Элементы.Очистить();
КонецЦикла;
Если требуется полностью привести настройки в соответствие с обновленной схемой данных, правильнее всего выполнить реинициализацию компоновщика с новым источником доступных настроек:
// Обновляем доступные настройки на основе измененной схемы
ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных);
КомпоновщикНастроек.Инициализировать(ИсточникНастроек);
// Загружаем чистые настройки схемы
КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
Проанализируем программное формирование полей набора. При добавлении элементов в КоллекцияПолей рекомендуется явно задавать не только ПутьКДанным и Поле, но и тип значения. Это предотвратит ошибки СКД при попытке наложить фильтрацию или использовать агрегатные функции:
Для Каждого Колонка Из ТЗ_.Колонки Цикл
ПолеНабора = КоллекцияПолей.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ПолеНабора.ПутьКДанным = Колонка.Имя;
ПолеНабора.Поле = Колонка.Имя;
ПолеНабора.Заголовок = Колонка.Имя;
ПолеНабора.ТипЗначения = Колонка.ТипЗначения;
КонецЦикла;
Нередко к двухэтапной схеме прибегают в надежде ускорить расчет отчета. Однако архитектурно такой подход часто дает обратный эффект по следующим причинам:
ТаблицаЗначений, мы лишаем СКД возможности модифицировать текст запроса. Механизм фигурных скобок {ГДЕ ...} перестает автоматически отсекать лишние записи на уровне базы данных, и в память сервера выгружается избыточный объем строк.ТаблицаЗначений, затем передается в ВнешниеНаборыДанных, после чего процессор компоновки снова копирует и трансформирует данные для построения группировок.Для ускорения работы рекомендуется переносить промежуточные вычисления в единый пакетный запрос внутри одного набора данных НаборДанныхЗапрос, используя менеджер временных таблиц непосредственно в СКД, либо обязательно индексировать колонки таблицы значений перед передачей во внешний набор:
// Индексация полей, по которым строятся группировки, точки диаграммы или накладываются отборы
ТЗ_.Индексы.Добавить("Предприятие");