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