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