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