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