Часто разработчики сталкиваются с досадной особенностью платформы 1С:Предприятие 8.3. Когда пользователь нажимает в документе кнопку «Провести и закрыть», система выполняет программный код, записывает объект, выводит важные предупреждения в окно сообщений и... тут же закрывает форму. Вместе с закрытием формы исчезают и все выведенные сообщения, так как они были привязаны к контексту этого окна. В итоге пользователь просто не успевает их прочитать.
В этой статье мы подробно разберем, почему это происходит, и рассмотрим несколько надежных способов заставить сообщения «выживать» после закрытия формы, чтобы они отображались в главном окне программы или в родительском списке.
Разберем ситуацию: когда мы вызываем метод Сообщение.Сообщить() на сервере (например, в модуле объекта или в подписке на событие), платформа создает объект типа СообщениеПользователю. По умолчанию это сообщение имеет свойство ИдентификаторНазначения, которое указывает на уникальный идентификатор текущей активной формы. Как только форма закрывается, платформа считает, что все связанные с ней уведомления больше не актуальны, и очищает их из интерфейса.
Проанализируем основные способы решения этой задачи — от простых до продвинутых.
Рассмотрим наиболее изящный метод, который подходит для большинства конфигураций на управляемых формах. Мы будем использовать обработчики формы ПослеЗаписиНаСервере и ПослеЗаписи для временного хранения очереди сообщений.
Для реализации этого метода выполним следующие шаги в модуле формы документа:
ПослеЗаписиНаСервере перехватим все сообщения, возникшие в процессе транзакции записи.ПараметрыЗаписи, которая передается обратно на клиент.ПослеЗаписи выведем их повторно, изменив привязку.Посмотрим на пример кода:
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
// Извлекаем все сообщения, которые накопились в очереди сервера в процессе записи
МассивСообщений = ПолучитьСообщенияПользователю(Истина);
ПараметрыЗаписи.Вставить("СообщенияДляПовторногоВывода", МассивСообщений);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Если ПараметрыЗаписи.Свойство("СообщенияДляПовторногоВывода") Тогда
// Создаем пустой уникальный идентификатор, чтобы «отвязать» сообщение от формы
ПустойУИД = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
Для Каждого Сообщение Из ПараметрыЗаписи.СообщенияДляПовторногоВывода Цикл
// Перенаправляем сообщение в общее окно сообщений системы
Сообщение.ИдентификаторНазначения = ПустойУИД;
Сообщение.Сообщить();
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Важное примечание: Установка пустого идентификатора 00000000-0000-0000-0000-000000000000 заставляет платформу вывести сообщение в «общий стек», который доступен пользователю даже после того, как текущее окно закрыто. Оно отобразится в нижней панели сообщений или в центре оповещений «Такси». Для этой задачи есть инструменты улучшения интерфейса и коммуникаций в 1С.
Рассмотрим более сложную ситуацию, когда нам нужно гарантированно показать сообщения, даже если форма закрывается не через кнопку «Записать и закрыть», а в результате программного вызова метода Закрыть(). Для этого мы создадим на форме вспомогательный реквизит СписокСообщений с типом СписокЗначений.
Разберем алгоритм по шагам:
ПриЗакрытии проверяем, остались ли в списке необработанные сообщения.Проанализируем пример реализации:
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
// Сохраняем сообщения в реквизит формы (СписокСообщений - тип СписокЗначений)
ОбъектныеСообщения = ПолучитьСообщенияПользователю(Истина);
Для Каждого Сообщение Из ОбъектныеСообщения Цикл
СписокСообщений.Добавить(Сообщение);
// Не забываем вернуть сообщение обратно в текущую сессию, если форма НЕ закроется
Сообщение.Сообщить();
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
Если ЗавершениеРаботы Тогда
Возврат; // При выходе из программы сообщения не нужны
КонецЕсли;
ПустойУИД = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
Для Каждого ЭлементСписка Из СписокСообщений Цикл
ТекСообщение = ЭлементСписка.Значение;
ТекСообщение.ИдентификаторНазначения = ПустойУИД;
ТекСообщение.Сообщить();
КонецЦикла;
КонецПроцедуры
Выясним причину использования СписокЗначений: это позволяет хранить сериализованные объекты СообщениеПользователю прямо в контексте формы между серверными и клиентскими вызовами.
Если информация носит ознакомительный характер, мы можем использовать механизм оповещений («всплывашек» в углу экрана) — для этого подойдёт универсальная настройка уведомлений и оповещений внутри базы. Они не привязаны к конкретной форме и живут своей жизнью до тех пор, пока не истечет время их отображения.
Однако помните: метод ПоказатьОповещениеПользователя() работает только на клиенте. Если проверка выполняется глубоко на сервере (например, в модуле объекта), нам все равно придется сначала передать текст на клиент через ПараметрыЗаписи или ДополнительныеСвойства объекта.
Пример вызова оповещения:
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Если ПараметрыЗаписи.Свойство("ТекстУведомления") Тогда
ПоказатьОповещениеПользователя(
"Внимание!",
"http://ссылка_на_документ",
ПараметрыЗаписи.ТекстУведомления,
БиблиотекаКартинок.Важное);
КонецЕсли;
КонецПроцедуры
Стоит отметить, что в современных версиях платформы (начиная с релиза 8.3.15) разработчики 1С значительно улучшили автоматическую доставку сообщений. В интерфейсе «Такси» многие «осиротевшие» сообщения после закрытия формы теперь автоматически попадают в Центр оповещений (иконка колокольчика в верхней панели). Тем не менее, для 100% уверенности в том, что пользователь увидит текст здесь и сейчас, программные методы перехвата через ИдентификаторНазначения остаются наиболее надежными.
Мы проанализировали несколько подходов к решению проблемы исчезающих сообщений. Подведем итоги:
ПараметрыЗаписи), если вам нужно простое и современное решение для управляемых форм — поможет настройка контроля ввода данных при записи объекта.ПриЗакрытии), если вы хотите гарантировать вывод сообщений при любом сценарии закрытия окна.ИдентификаторНазначения в пустое значение UUID для вывода в общесистемное окно.Отказ = Истина в процедуре ПередЗаписью или ПередЗакрытием.Применяя эти методы, мы обеспечиваем качественный пользовательский интерфейс, в котором ни одна важная системная подсказка не будет потеряна.