Как открыть форму выбора из общего модуля и вернуть результат на сервер?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С:Предприятие 8 часто возникает необходимость вынести повторяющийся функционал в общие модули. Одной из типичных задач является интерактивный выбор пользователем некоторого значения (например, элемента справочника), обработка этого значения запросом на сервере и возврат готовых данных обратно в интерфейс. Однако при попытке реализовать этот сценарий разработчики нередко сталкиваются с архитектурным барьером разделения клиентского и серверного контекстов.

Разберем по шагам, почему нельзя открыть форму на стороне сервера, как правильно выстроить взаимодействие между клиентом и сервером и каким образом организовать универсальный код с использованием общих модулей.

Архитектурные ограничения: почему сервер не открывает формы

Проанализируем ключевое правило клиент-серверной архитектуры управляемого приложения 1С. Любые визуальные операции — открытие окон, диалогов, предупреждений или форм выбора — физически могут выполняться только на стороне клиента. Серверный контекст полностью изолирован от пользовательского интерфейса: на сервере нет экранных форм, и код выполняется в фоновой службе кластера серверов, где отображать интерфейс некому.

Попытка вызвать метод открытия формы из чисто серверного общего модуля приведет к синтаксической или рантайм-ошибке, поскольку серверный контекст не содержит соответствующих методов. Инициатором открытия формы всегда обязан выступать клиент.

Также важно помнить об ограничении динамически создаваемых кнопок: в свойстве Действие команды формы нельзя напрямую указать процедуру общего модуля. Обработчик команды формы должен располагаться в модуле самой формы, а уже из него можно вызывать экспортные процедуры общего клиентского модуля.

Правильная схема взаимодействия

Рассмотрим последовательность действий, которая позволяет решить задачу чисто и без нарушения архитектуры платформы:

  1. Пользователь нажимает кнопку на форме, инициируя клиентский обработчик.
  2. Обработчик формы вызывает процедуру общего клиентского модуля.
  3. Клиентский общий модуль открывает форму выбора с указанием обработчика оповещения о закрытии (в асинхронном режиме).
  4. Пользователь производит выбор элемента в открывшемся окне.
  5. Управление возвращается в процедуру оповещения общего клиентского модуля.
  6. Из клиентской процедуры выбранное значение передается в функцию общего серверного модуля с установленным признаком Вызов сервера.
  7. Серверная функция выполняет запрос к базе данных и возвращает результат на клиент.

Способ 1. Реализация через ОписаниеОповещения (для всех версий управляемого интерфейса)

Посмотрим на пример организации двух общих модулей. Первый модуль назовем РаботаСФормамиКлиент (установлен флаг Клиент (управляемое приложение)), а второй — РаботаСФормамиСервер (установлены флаги Сервер и Вызов сервера).

В модуле формы, где находится кнопка, создадим клиентский обработчик, который делегирует работу общему модулю:


&НаКлиенте
Процедура ОбработкаКомандыВыбора(Команда)
    РаботаСФормамиКлиент.ЗапуститьВыборНоменклатуры(ЭтотОбъект);
КонецПроцедуры

Теперь рассмотрим код клиентского общего модуля РаботаСФормамиКлиент. Здесь мы формируем оповещение, открываем форму выбора и описываем процедуру, которая примет результат:


// Процедура открывает форму выбора
Процедура ЗапуститьВыборНоменклатуры(ФормаВладелец) Экспорт
    
    // Передаем форму-владелец в дополнительные параметры, если потребуется обновить ее данные
    ДополнительныеПараметры = Новый Структура("ФормаВладелец", ФормаВладелец);
    
    Оповещение = Новый ОписаниеОповещения("ПослеВыбораНоменклатуры", ЭтотОбъект, ДополнительныеПараметры);
    
    ПараметрыВыбора = Новый Структура;
    ПараметрыВыбора.Вставить("ЗакрыватьПриВыборе", Истина);
    
    ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора", ПараметрыВыбора, ФормаВладелец, , , , Оповещение);
    
КонецПроцедуры

// Обработчик завершения выбора
Процедура ПослеВыбораНоменклатуры(РезультатВыбора, ДополнительныеПараметры) Экспорт
    
    Если РезультатВыбора = Неопределено Тогда
        // Пользователь закрыл форму, не выбрав значение
        Возврат;
    КонецЕсли;
    
    // Передаем выбранную ссылку на сервер для выполнения запроса
    РезультатЗапроса = РаботаСФормамиСервер.ПолучитьСписокПоНоменклатуре(РезультатВыбора);
    
    // Дальнейшая работа с полученными данными на клиенте
    ПоказатьОповещениеПользователя("Успешно", , "Получено записей: " + РезультатЗапроса.Количество());
    
КонецПроцедуры

Разберем серверную часть в общем модуле РаботаСФормамиСервер. Здесь мы выполняем требуемый запрос и возвращаем данные на клиент (например, в виде массива или списка значений):


// Функция выполняется на сервере и возвращает результат клиенту
Функция ПолучитьСписокПоНоменклатуре(ВыбраннаяНоменклатура) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Цены.ВидЦены КАК ВидЦены,
        |   Цены.Цена КАК Цена
        |ИЗ
        |   РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, Номенклатура = &Номенклатура) КАК Цены";
    
    Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Результат = Новый СписокЗначений;
    Пока Выборка.Следующий() Цикл
        Результат.Добавить(Выборка.Цена, Строка(Выборка.ВидЦены));
    КонецЦикла;
    
    Возврат Результат;
    
КонецФункции

Способ 2. Использование асинхронных методов платформы (Асинх / Ждать)

Если в конфигурации используется современная версия платформы с поддержкой асинхронных функций, код можно сделать более компактным и линейным, избежав создания отдельной процедуры оповещения.

Рассмотрим, как выглядит этот сценарий в клиентском общем модуле:


&НаКлиенте
Асинх Процедура ВыполнитьВыборИОбработкуАсинх(ФормаВладелец) Экспорт
    
    ПараметрыВыбора = Новый Структура;
    ПараметрыВыбора.Вставить("ЗакрыватьПриВыборе", Истина);
    
    // Ожидаем интерактивный выбор пользователя
    ВыбранноеЗначение = Ждать ОткрытьФормуАсинх("Справочник.Номенклатура.ФормаВыбора", ПараметрыВыбора, ФормаВладелец);
    
    Если ВыбранноеЗначение = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Вызываем серверную функцию общего модуля
    СписокРезультатов = РаботаСФормамиСервер.ПолучитьСписокПоНоменклатуре(ВыбранноеЗначение);
    
    // Обрабатываем возвращенный список
    ПоказатьОповещениеПользователя("Готово", , "Данные успешно получены с сервера");
    
КонецПроцедуры

Альтернативный вариант: диалог ПоказатьВводЗначения

Бывают ситуации, когда открывать тяжелую форму выбора справочника избыточно, а требуется лишь предоставить пользователю возможность выбрать или ввести значение конкретного ссылочного типа. В этом случае удобно воспользоваться методом ПоказатьВводЗначения:


Процедура ЗапроситьЗначение(ФормаВладелец) Экспорт
    
    Оповещение = Новый ОписаниеОповещения("ПослеВводаЗначения", ЭтотОбъект);
    ТипВыбора = Новый ОписаниеТипов("СправочникСсылка.Номенклатура");
    
    ПоказатьВводЗначения(Оповещение, Неопределено, "Выберите номенклатуру", ТипВыбора);
    
КонецПроцедуры

Процедура ПослеВводаЗначения(ВыбранноеЗначение, ДополнительныеПараметры) Экспорт
    
    Если ВыбранноеЗначение <> Неопределено Тогда
        Данные = РаботаСФормамиСервер.ПолучитьСписокПоНоменклатуре(ВыбранноеЗначение);
    КонецЕсли;
    
КонецПроцедуры

Таким образом, разделение логики на клиентскую визуальную часть и серверную аналитическую часть позволяет легко реализовать переиспользуемый код в общих модулях без нарушения принципов работы управляемого приложения 1С.

← На главную