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