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

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

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

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

Выясняем причину: почему сообщения исчезают?

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

По умолчанию платформа привязывает накопленные сообщения к активному контексту — то есть к той форме, из которой был инициирован серверный вызов. Если в процессе обработки или сразу после возврата на клиент вызывается метод Закрыть(), текущая форма уничтожается. Вместе с контекстом закрытой формы платформа безвозвратно уничтожает и предназначенную ей панель сообщений. Именно поэтому пользователь видит лишь пустоту вместо ожидаемого уведомления.

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

Способ №1. Использование свойства ИдентификаторНазначения (Рекомендуемый системный подход)

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

Рассмотрим подробнее реализацию этого механизма:

  1. При открытии всплывающего или подчиненного окна (например, вопроса «Это копия базы?») передадим в параметры этой формы уникальный идентификатор вызывающей (родительской) формы. Получить его можно через свойство ЭтаФорма.УникальныйИдентификатор.
  2. В подчиненной форме сохраним этот идентификатор в реквизит формы (например, с именем ИдентификаторРодительскойФормы).
  3. При выполнении серверного кода, перед тем как вызвать метод Сообщить(), явно укажем, куда именно должно быть доставлено сообщение.

Посмотрим на пример кода серверной процедуры:


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

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

Способ №2. Возврат результата выполнения на клиент (Best Practice)

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

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

Разберем по шагам, как это реализовать:

Шаг 1. Серверная функция выполняет работу и возвращает структуру с результатом:


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

Шаг 2. Клиентский обработчик получает результат, корректно закрывает текущую форму и выводит всплывающее облако через метод ПоказатьОповещениеПользователя():


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

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

Способ №3. Накопление сообщений в массив и вывод на клиенте

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

В этом случае мы можем применить паттерн накопления сообщений. Проанализируем пример схемы:

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

Поскольку контекст старой формы уже уничтожен, платформа автоматически перенаправит все вызванные на клиенте методы Сообщить() в то окно, которое стало активным на текущий момент (например, на Начальную страницу или форму списка).

Пример реализации сбора сообщений:


&НаСервере
Функция ВыполнитьГрупповуюОбработкуНаСервере()
    
    СписокСообщений = Новый Массив;
    
    Для Каждого СтрокаПараметров Из Объект.ПараметрыОбмена Цикл
        Попытка
            ОбработатьСтрокуНастройки(СтрокаПараметров);
            СписокСообщений.Добавить("Настройка " + СтрокаПараметров.Имя + " успешно изменена.");
        Исключение
            СписокСообщений.Добавить("Ошибка обработки: " + КраткоеПредставлениеОшибки(ОписаниеОшибки()));
        КонецПопытки;
    КонецЦикла;
    
    Возврат СписокСообщений;
    
КонецФункции

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

Способ №4. Обходные пути из практики разработчиков (Использование форм-пустышек)

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

Важный нюанс при использовании Библиотеки Стандартных Подсистем (БСП)

Если вы работаете в типовой конфигурации 1С (такой как 1С:ERP, 1С:Комплексная автоматизация или 1С:Управление торговлей), обязательно учитывайте стандарты разработки БСП.

Устаревший метод ОбщегоНазначенияКлиентСервер.СообщитьПользователю() во многих современных версиях библиотек разделен на контекстные вызовы:

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

← На главную