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