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