Как правильно вернуться на клиент из серверного обработчика оповещения в 1С?

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

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

Почему нельзя использовать серверные процедуры в ОписанииОповещения

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

  1. Нарушение асинхронной модели: Механизм оповещений (ОписаниеОповещения) предназначен для возврата управления в клиентский поток. Серверный вызов — это всегда контекстный переход. Если платформа пытается выполнить callback напрямую на сервере, вы теряете возможность интерактивного взаимодействия с пользователем (например, вызов метода Показать() для табличного документа).
  2. Требования EDT и стандартов 1С: Среда разработки 1С:EDT и встроенные механизмы валидации кода помечают использование серверного метода в оповещении как критическую ошибку. В будущих версиях платформы такая возможность может быть полностью заблокирована, что приведет к отказу работоспособности конфигурации.
  3. Проблемы Веб-клиента: В браузере (Веб-клиент) работа жестко завязана на JavaScript и асинхронность. Попытка совершить неявный серверный переход в момент закрытия окна гарантированно приведет к ошибкам десериализации или зависанию интерфейса.

Проанализируем правильную схему реализации, которую рекомендуют стандарты разработки 1С и Библиотека стандартных подсистем (БСП).

Решение проблемы: Трехзвенная архитектура вызова

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

Шаг 1. Инициация вызова на клиенте. В основной форме (например, внешней обработке) мы создаем оповещение, указывая клиентскую процедуру в качестве обработчика.


&НаКлиенте
Процедура Печать(ИдентификаторКоманды, ОбъектыНазначенияМассив) Экспорт
    ПараметрыФормы = Новый Структура;
    // ... заполнение параметров ...
    
    // Указываем КЛИЕНТСКУЮ процедуру "ПечатьЗавершение"
    Оповещение = Новый ОписаниеОповещения("ПечатьЗавершение", ЭтаФорма, ОбъектыНазначенияМассив);
    
    ОткрытьФорму("ВнешняяОбработка.СчетНаОплату.Форма.ФормаВвода", 
        ПараметрыФормы, , , , , Оповещение, РежимОткрытияОкнаФормы.БлокироватьВесьИнтерфейс);
КонецПроцедуры

Шаг 2. Возврат данных из вспомогательной формы. В вызываемой форме (ФормаВвода) при завершении работы (например, по нажатию кнопки «ОК») мы передаем результат через метод Закрыть().


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

Шаг 3. Обработка на клиенте и вызов сервера. Теперь мы находимся в клиентской процедуре-обработчике. Здесь мы уже можем решить: нужно ли нам идти на сервер для тяжелых вычислений.


&НаКлиенте
Процедура ПечатьЗавершение(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = Неопределено Тогда 
        // Пользователь просто закрыл форму (нажал крестик или Отмена)
        Возврат;
    КонецЕсли;

    // Вызываем серверную функцию для формирования ТабличногоДокумента
    ТабДок = СформироватьПечатнуюФормуНаСервере(Результат, ДополнительныеПараметры);
    
    // Мы снова на клиенте и можем показать документ!
    ТабДок.Показать("Печать счета");
КонецПроцедуры

Как передать ТабличныйДокумент с сервера на клиент

Выясним причину, по которой у многих возникают сложности на этом этапе. ТабличныйДокумент является сериализуемым объектом. Это значит, что его можно просто вернуть из функции &НаСервере как результат. Рассмотрим пример кода серверной части:


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

Важный нюанс: Если ваш табличный документ содержит огромный объем данных (десятки тысяч строк с картинками), прямая передача объекта может привести к кратковременному «подтормаживанию» интерфейса в момент передачи данных по сети. В таких критических случаях рассмотрим альтернативный вариант через Временное хранилище.

Вариант с использованием Временного хранилища

Если нужно передать очень большой объект или адрес для последующего использования в других механизмах, используем следующую схему:

  1. На сервере помещаем ТабличныйДокумент в хранилище и возвращаем адрес (строку).
  2. На клиенте получаем этот адрес и используем его.

&НаСервере
Функция ПолучитьАдресТабДока(Данные)
    ТабДок = Новый ТабличныйДокумент;
    // ... заполнение ...
    
    // Помещаем во временное хранилище, привязанное к текущей форме
    Возврат ПоместитьВХранилище(ТабДок, ЭтаФорма.УникальныйИдентификатор);
КонецФункции

&НаКлиенте
Процедура ПечатьЗавершение(Результат, Доп) Экспорт
    Если Результат <> Неопределено Тогда
        Адрес = ПолучитьАдресТабДока(Результат);
        
        // Получаем объект из хранилища на клиенте
        РезультатТабДок = ПолучитьИзХранилища(Адрес);
        РезультатТабДок.Показать();
    КонецЕсли;
КонецПроцедуры

Подведение итогов

Проанализировав ситуацию, мы видим, что «возврата» с сервера в чистом виде не существует — есть только завершение серверного вызова и возврат в ту точку клиентского кода, откуда этот вызов был инициирован.

Основные правила:

Соблюдение этих простых правил гарантирует, что ваша обработка будет стабильно работать в любом типе клиента 1С, будет соответствовать стандартам разработки и не вызовет проблем при обновлении платформы или переходе на 1С:EDT.

← На главную