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

Рис. 3.75. Назначение свойств объекта «СообщениеПользователю»
Выполнение любого программного кода всегда начинается на клиенте – из какой-нибудь активной формы или из основного окна.
Все сообщения пользователю платформа выводит в окно формы, идентификатор которой задан в свойстве ИдентификаторНазначения. Если идентификатор не указан, то сообщения выводятся в активное окно.
Когда сообщение оказывается в окне какой-либо формы, первым делом анализируется ссылка, содержащаяся в свойстве КлючДанных.
Затем ищется реквизит формы, имя которого указано в свойстве ПутьКДанным.
После этого ищется подчиненный реквизит, имя которого указано в свойстве Поле.
В результате после двойного щелчка на сообщении оно отображается рядом с нужным элементом формы.
Это означает, что сообщение предназначено другой форме – основной форме того объекта, ссылка на который содержится в ключе данных.
В этом случае после двойного щелчка на сообщении будет открыта основная форма объекта, ссылка на который содержится в ключе данных, а далее сообщение будет спозиционировано рядом с нужным элементом формы по принципу, описанному выше.
Кроме того, выполняется перенос всех сообщений со свойством КлючДанных, равным значению этого свойства текущего сообщения, из окна текущей формы в открытую форму.
Оба рассмотренных случая, когда заполнены все три свойства сообщения, являются наиболее общими и универсальными. Они позволяют правильно воспользоваться сообщением независимо от того, в какой форме оно оказалось.
Но могут быть и другие варианты.
Например, если точно известно, что сообщение может оказаться только в одной-единственной форме (например, когда оно формируется в этой самой форме), можно не указывать КлючДанных. В этом случае будет считаться, что сообщение относится именно к той форме, в которой оно оказалось. При этом если сообщение должно быть привязано не к реквизиту основного объекта, а к реквизиту формы, то также не нужно указывать ПутьКДанным, достаточно указать только Поле.
Есть и еще одна ситуация, которая может возникнуть при работе с сообщениями. Может оказаться так, что для сообщения будут указаны КлючДанных и Поле. А свойство ПутьКДанным не будет заполнено. В этой ситуации сообщение автоматически заполнит свойство ПутьКДанным именем основного реквизита формы. Той, в которой оно оказалось (если ключ данных совпадает), или той, которая будет открыта при двойном щелчке на сообщении.
Сообщения, формируемые при помощи объекта СообщениеПользователю, рекомендуется использовать только для информирования об ошибочных действиях. Если требуется проинформировать пользователя о каком-либо событии, то рекомендуется использовать для этого метод ПоказатьОповещениеПользователя(), о котором рассказывается в разделе .
Теперь рассмотрим все эти ситуации на небольшом практическом примере.
Примечание
Пример можно посмотреть в демонстрационной базе «Сообщение пользователю».
Допустим, у нас есть обработка ПроведениеДокументов. Задача этой обработки – провести (перепровести) выбранный документ некоторым специальным образом, который не требуется для обычного ежедневного учета. То есть при таком проведении помимо «обычных» данных формируются дополнительные данные, требуемые для расчетов с отдельными поставщиками. Алгоритм расчета этих данных сложен, таких поставщиков немного, данные требуются эпизодически. Поэтому существует отдельная обработка для получения этих данных.
Наша задача будет заключаться:
Обработка имеет реквизит Документ, в котором содержится ссылка на обрабатываемый документ Накладная. Кроме того, форма обработки имеет реквизит Комментарий, в который должен быть записан произвольный комментарий при интерактивном выполнении обработки (рис. 3.76).

Рис. 3.76. Реквизиты формы
Документ является реквизитом обработки, для того чтобы была возможность программного запуска обработки без использования ее формы.
Для выполнения нашего алгоритма в форме обработки существует локальная команда ПровестиДокументы, которая вызывает процедуру ПровестиДокументы в модуле формы (листинг 3.51).
Листинг 3.51. Обработчик локальной команды формы
&НаКлиенте
Процедура ПровестиДокументы(Команда)
// Использовать стандартный механизм проверки заполнения,
// реализуемый платформой для обработки.
Если ПроверитьЗаполнение() Тогда
// Выполнить собственный алгоритм обработки данных.
НаСервере();
КонецЕсли;
КонецПроцедуры
В этой процедуре сначала вызывается стандартная проверка заполнения, реализуемая платформой, – Если ПроверитьЗаполнение() Тогда.
Метод формы ПроверитьЗаполнение() вызывает сначала проверку заполнения в форме, а затем – в объекте обработки. Аналогично тому, как выполняется проверка заполнения при интерактивной записи объектов из формы. Эта тема подробно рассматривалась в .
Сначала будет вызвано событие Обработка проверки заполнения на сервере у формы. В обработчике этого события нам нужно проверить заполненность реквизита формы Комментарий и вывести сообщение (листинг 3.52).
Листинг 3.52. Обработчик события «Обработка проверки заполнения на сервере»
&НаСервере
Процедура ОбработкаПроверкиЗаполненияНаСервере(Отказ, ПроверяемыеРеквизиты)
ИндексКомментария = ПроверяемыеРеквизиты.Найти("Комментарий");
Если ИндексКомментария <> Неопределено Тогда
ПроверяемыеРеквизиты.Удалить(ИндексКомментария);
КонецЕсли;
Если СокрЛП(Комментарий) = "" Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Поле = "Комментарий";
Сообщение.Текст = "Нужно написать комментарий.";
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Сначала исключаем комментарий из массива проверяемых реквизитов. Затем, если комментарий «пустой», формируем и выводим сообщение.
В сообщении мы заполняем только свойство Поле. В данном случае активной формой будет форма обработки, и сообщение попадет в нее. Поэтому достаточно указать лишь поле, остальные свойства сообщения не понадобятся (рис. 3.77, 3.78).

Рис. 3.77. Заполнение свойств сообщения

Рис. 3.78. Сообщение, привязанное к реквизиту формы
После того как будет обработано событие в форме, платформа вызовет аналогичное событие у объекта обработки – Обработка проверки заполнения. В обработчике этого события нам нужно проверить заполненность реквизита обработки Документ и вывести сообщение (листинг 3.53).
Листинг 3.53. Обработчик события «Обработка проверки заполнения»
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
ПроверяемыеРеквизиты.Очистить();
Если Документ = Документы.Накладная.ПустаяСсылка() Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Поле = "Документ";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Текст = "Нужно выбрать какую-нибудь накладную.";
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Сначала мы очищаем массив проверяемых реквизитов, т. к. все проверки будем выполнять самостоятельно.
Затем проверяем заполненность реквизита Документ и, если он не заполнен (Если Документ = Документы.Накладная.ПустаяСсылка()), формируем и выводим сообщение.
Тут все уже несколько сложнее, чем в форме. Проверка заполнения в модуле обработки может быть вызвана и из формы обработки, и программно, без использования формы обработки. А это значит, что формируемое сообщение может попасть не только в форму обработки, для которой оно предназначено, но и в какую-нибудь другую форму.
Поэтому нужно заполнить все свойства сообщения, чтобы, попав в другую форму, сообщение «знало», что нужно открыть форму обработки и именно в ней спозиционироваться возле поля Документ.
Свойство Поле мы заполняем самостоятельно: Сообщение.Поле = "Документ". А для того чтобы заполнить два других свойства, КлючДанных и ПутьКДанным, мы воспользуемся возможностями, предоставляемыми платформой.
Дело в том, что при выполнении некоторых определенных действий платформа запоминает соответствие объекта, который отображается в форме, и имени основного реквизита этой формы. Например, когда в форме объекта выполняется стандартная команда записи или когда в форме вызывается метод ПроверитьЗаполнение().
«Запоминанием» этого соответствия занимается расширение формы, определяемое основным реквизитом. Соответствие запоминается для того, чтобы в модуле объекта можно было бы им воспользоваться для правильного формирования сообщения. Ведь в общем случае, попав в модуль объекта, мы не знаем, из какой формы мы в него попали, не знаем, как в этой форме называется основной реквизит формы. Поэтому, прежде чем перейти в модуль объекта, платформа запоминает, какие данные (ссылку) содержит основной реквизит формы и как он называется.
В случае с обработкой платформа запомнит только имя основного реквизита формы обработки. В платформе не существует типа ссылки на обработку, поэтому запоминать просто нечего.
Затем в модуле обработки мы просто пишем Сообщение.УстановитьДанные(ЭтотОбъект); – и у сообщения будет установлено свойство ПутьКДанным – "Объект". КлючДанных останется незаполненным, так как заполнить его просто нечем.
В результате сообщение окажется в форме обработки с незаполненным свойством КлючДанных. Поэтому платформа будет пытаться (и это ей удастся) привязать его к форме обработки, используя значения свойств ПутьКДанным и Поле (рис. 3.79, 3.80).

Рис. 3.79. Заполнение свойств сообщения

Рис. 3.80. Сообщение, привязанное к реквизиту обработки
Поскольку оба раза при проверке заполнения мы устанавливали параметр Отказ обработчиков в значение Истина, после обработки события Обработка проверки заполнения дальнейшее выполнение действий останавливается. Метод ПроверитьЗаполнение() возвращает Ложь, и наша процедура НаСервере() не выполняется.
Пока на этом остановимся и обратим внимание на документ Накладная.
В модуле документа в процедуре ОбработкаПроведения находится код, который формирует движения документа. А после него для примера формируются и выводятся два сообщения.
Предполагается, что наша накладная имеет «два повода» для проведения: обычное повседневное проведение и «специальное» проведение, выполняемое только из этой обработки.
Чтобы различать, каким образом в данный момент проводится накладная, в модуле накладной существует экспортируемая переменная ПроведениеИзОбработки (листинг 3.54).
Листинг 3.54. Экспортируемая переменная
Перем ПроведениеИзОбработки Экспорт;
При любом проведении значение этой переменной сначала устанавливается в Ложь (последняя строка модуля – тело модуля) – листинг 3.55.
Листинг 3.55. Инициализация экспортируемой переменной
ПроведениеИзОбработки = Ложь;
Это значение будет говорить нам о том, что проведение документа выполняется не из обработки. И в этом случае мы контролируем остатки и выводим сообщение, если товара на складе недостаточно (листинг 3.56).
Листинг 3.56. Сообщение в случае, когда проведение выполняется не из обработки
// Проверить, есть ли на складе достаточное количество товара
// в случае оперативного проведения.
// ...
Сообщение = Новый СообщениеПользователю;
Сообщение.Поле = "Товары[0].Количество";
Сообщение.Текст = "На складе есть только 5 единиц товара.";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
Здесь, как и в модуле обработки, мы используем возможность платформы самостоятельно устанавливать данные для сообщения (Сообщение.УстановитьДанные(ЭтотОбъект);).
Если проведение документа будет выполняться стандартной командой из формы документа, то расширение формы документа автоматически установит соответствие объекта и реквизита формы. Таким образом, КлючДанных и ПутьКДанным будут заполнены, и сообщение будет привязано к нужному полю (рис. 3.81, 3.82).

Рис. 3.81. Заполнение свойств сообщения

Рис. 3.82. Сообщение, привязанное к реквизиту документа
Если проведение будет выполняться, например, стандартной командой из формы списка (Еще – Провести), то такого соответствия автоматически установлено не будет. Выполнение метода Сообщение.УстановитьДанные(ЭтотОбъект); приведет лишь к тому, что будет установлено свойство КлючДанных – ссылка на объект документа. Свойство ПутьКДанным останется незаполненным. При этом сообщение попадет в основное окно приложения (рис. 3.83).

Рис. 3.83. Сообщение в основном окне приложения
При двойном щелчке на этом сообщении будет открыта форма накладной, т. к. у сообщения установлен КлючДанных. Платформа автоматически заполнит свойство ПутьКДанным именем основного реквизита формы и правильно привяжет его к полю Количество (рис. 3.84, 3.85).

Рис. 3.84. Заполнение свойств сообщения

Рис. 3.85. Сообщение, привязанное к реквизиту формы
Таким образом, если при формировании сообщения мы используем метод УстановитьДанные() при любом способе проведения документа, сообщение отработает правильно, в какой бы форме оно ни оказалось.
Теперь вернемся к обработке, заполним поля документа и комментарий и нажмем кнопку Провести документы.
В модуле формы будет вызвана процедура НаСервере() (листинг 3.57).
Листинг 3.57. Серверная процедура в модуле формы обработки
&НаСервере
Процедура НаСервере()
// Получить объект документа.
ОбъектДокумента = Объект.Документ.ПолучитьОбъект();
// Провести документ.
ОбъектДокумента.ПроведениеИзОбработки = Истина;
ОбъектДокумента.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
В ней мы получаем объект документа от ссылки, хранящейся в реквизите Документ.
Затем устанавливаем значение экспортируемой переменной ПроведениеИзОбработки модуля документа в Истина. В процедуре обработки проведения документа это будет означать для нас, что проведение выполняется «сложным» способом, из обработки.
И после этого вызываем проведение документа в неоперативном режиме (листинг 3.58).
Листинг 3.58. Проведение документа в неоперативном режиме
ОбъектДокумента.Записать(РежимЗаписиДокумента.Проведение);
В модуле документа, в обработке проведения, анализируем значение переменной ПроведениеИзОбработки, выполняем уже другие проверки и выводим другое сообщение, не такое, как при обычном проведении (листинг 3.59).
Листинг 3.59. Сообщение в случае проведения из обработки
Если ПроведениеИзОбработки Тогда
// Проверить, подходит ли поставщик для выполнения задуманной операции.
Сообщение = Новый СообщениеПользователю;
Сообщение.Поле = "Документ";
Сообщение.ПутьКДанным = "Объект";
Сообщение.Текст = "Выбран неудачный документ. Расчеты с этим поставщиком были прекращены в прошлом году.";
Сообщение.Сообщить();
Мы хотим, чтобы это сообщение отображалось не в форме документа, а в форме обработки и было привязано к полю Документ.
Поэтому в свойстве Поле мы указываем "Документ", а в свойстве ПутьКДанным – Объект, так как Документ – это подчиненный реквизит объекта обработки. КлючДанных мы не указываем (рис. 3.86).

Рис. 3.86. Заполнение свойств сообщения
Во-первых, у нас его нет. Ключом данных должна быть ссылка на объект обработки, а такой тип в платформе отсутствует. Вообще говоря, это не очень хорошо, т. к. снижает универсальность нашего сообщения. Если оно попадет не в форму обработки, то не сможет правильно отработать.
С другой стороны, вероятность того, что оно попадет не в форму обработки, очень мала. Если предполагается, что обработка будет использоваться только интерактивно, тогда можно допустить, что в другую форму это сообщение не попадет никогда.
А раз так, то свойств Поле и ПутьКДанным будет достаточно для того, чтобы правильно привязать сообщение в форме обработки. Проверим (рис. 3.87).

Рис. 3.87. Сообщение, привязанное к реквизиту обработки
При использовании сообщений всегда следует помнить о том, что присутствие сообщений в форме не препятствует ее закрытию.
Поэтому если вывод сообщений используется в стандартных процедурах проверки заполнения, то всегда следует отказываться от продолжения работы. Для этого параметр Отказ этих обработчиков нужно устанавливать в значение Истина.
В других случаях следует самостоятельно заботиться о том, чтобы форма не была закрыта при появлении в ней сообщений – например, анализируя какой-нибудь служебный реквизит объекта в процедуре формы ПередЗакрытием().