При разработке отчетов на Системе компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда корректно работающий запрос после переноса во вложенную конструкцию начинает выдавать ошибку исполнения. Типичный пример такой ошибки: Ошибка инициализации по причине: Не найдено поле замены "ДоговорКонтрагентаВладелец". Эта проблема специфична для механизмов СКД и связана с тем, как платформа автоматически генерирует итоговый текст запроса для СУБД.
В этой статье мы подробно разберем, почему возникает данная ошибка, проанализируем внутренние механизмы компоновки данных и рассмотрим несколько способов решения проблемы, включая настройку ролей полей и работу с внешними наборами данных.
Разберем природу этого явления. В СКД существуют так называемые виртуальные поля или поля замены. Когда мы выбираем в отчете ссылочное поле (например, ДоговорКонтрагента), система компоновки «под капотом» пытается получить не только саму ссылку, но и ее представление, а также связанные данные, такие как Владелец. Это необходимо для корректной работы иерархии, группировок по владельцу и отображения текстовых наименований вместо идентификаторов (UUID).
Когда запрос становится вложенным, механизм «разыменования» ссылок в СКД может дать сбой. Рассмотрим ситуацию: основной компоновщик видит поле во внешнем запросе, но не может проследить его связь с владельцем, так как эта связь скрыта внутри подзапроса. Система пытается создать поле замены ДоговорКонтрагентаВладелец для служебных нужд, но не находит источника данных для его заполнения во вложенной выборке.
Самый простой и эффективный способ — помочь системе компоновки данных увидеть необходимые связи. Если ошибка указывает на отсутствие поля ДоговорКонтрагентаВладелец, нам необходимо явно выбрать это поле на всех уровнях вложенности запроса.
Проанализируем пример изменения запроса. Предположим, изначально у нас был простой выбор:
ВЫБРАТЬ
Продажи.ДоговорКонтрагента КАК ДоговорКонтрагента,
Продажи.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи
Если мы оборачиваем этот запрос во вложенный, нам нужно явно добавить владельца:
ВЫБРАТЬ
ВложенныйЗапрос.ДоговорКонтрагента КАК ДоговорКонтрагента,
ВложенныйЗапрос.ДоговорКонтрагентаВладелец КАК ДоговорКонтрагентаВладелец,
ВложенныйЗапрос.Сумма КАК Сумма
ИЗ
(ВЫБРАТЬ
Продажи.ДоговорКонтрагента КАК ДоговорКонтрагента,
Продажи.ДоговорКонтрагента.Владелец КАК ДоговорКонтрагентаВладелец,
Продажи.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи) КАК ВложенныйЗапрос
Таким образом, мы предоставляем СКД прямой доступ к полю, которое она пытается вычислить автоматически. Это снимает неопределенность при генерации итогового макета компоновки.
Если правка текста запроса не помогает или нежелательна, следует обратиться к закладке «Наборы данных» в схеме компоновки. Выясним, как настройки ролей влияют на возникновение ошибки.
ДоговорКонтрагента).Иногда полезно полностью очистить роль поля, если система ошибочно назначила ему свойства родителя или части иерархии, которые не могут быть вычислены в текущей структуре запроса.
Рассмотрим ситуацию, когда включенная опция «Автозаполнение» играет против разработчика. При включенном автозаполнении СКД пытается проанализировать все таблицы запроса и автоматически добавить доступные поля и их свойства в схему. Во вложенных запросах этот механизм часто ошибается, пытаясь «дотянуться» до полей, которые были доступны в исходных таблицах, но не выбраны во вложенном запросе.
Попробуем следующий алгоритм действий:
Этот метод требует больше времени на настройку, но он гарантирует, что компоновщик не будет пытаться искать «поля замены», о которых вы его не просили.
Выясним причину ошибки, если она возникает при использовании Внешних наборов данных. В этом случае ошибка «Не найдено поле замены» часто свидетельствует о проблемах типизации в передаваемой ТаблицеЗначений.
Проанализируем типичный сценарий: в схеме СКД описано поле ДоговорКонтрагента с типом СправочникСсылка.ДоговорыКонтрагентов. Если в программном коде при формировании таблицы значений в эту колонку попадает значение Неопределено или NULL без явного указания типа колонки, компоновщик не сможет определить метаданные ссылки. В результате он не сможет построить виртуальное поле для владельца договора.
Для решения проблемы в коде 1С необходимо жестко типизировать колонки таблицы значений:
ТаблицаДанных = Новый ТаблицаЗначений;
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("СправочникСсылка.ДоговорыКонтрагентов"));
ОписаниеТиповДоговор = Новый ОписаниеТипов(МассивТипов);
// Явное указание типа колонки крайне важно для СКД
ТаблицаДанных.Колонки.Добавить("ДоговорКонтрагента", ОписаниеТиповДоговор);
Также убедитесь, что при заполнении набора данных вы передаете именно ту таблицу, которая соответствует описанию в схеме:
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ИмяВашегоНабораДанных", ТаблицаДанных);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
Проанализируем еще один скрытый фактор — RLS. Если отчет запускается под пользователем с ограниченными правами, система автоматически модифицирует запрос, добавляя условия проверки прав. Эти условия часто обращаются к полям-владельцам (например, для проверки доступа к Контрагенту через его Договор).
Если ваш вложенный запрос «обрезает» возможность получения владельца, механизм RLS не сможет вставить свою проверку в итоговый SQL-запрос. Это приведет к ошибке инициализации именно в момент выполнения метода СкомпоноватьРезультат. В этом случае решение аналогично Методу 1: необходимо обеспечить наличие всех полей, участвующих в правилах доступа, во всех частях запроса.
Резюмируем: при появлении ошибки «Не найдено поле замены», в первую очередь проверьте, не «спрятали» ли вы ссылочное поле внутри вложенного запроса, лишив СКД возможности автоматически определить его свойства. Используйте явное описание полей, проверяйте роли в наборах данных и не забывайте про типизацию внешних источников данных. Это позволит создавать стабильные и производительные отчеты даже со сложной структурой запросов.