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