Сообщения, вопросы и предупреждения в коде 1С 8.3
Содержание
Чтобы выдать сообщение пользователю в 1С, проще всего вызвать Сообщить("Текст") или создать объект СообщениеПользователю и вызвать у него метод Сообщить(). Текст появится в окне сообщений внизу формы. Если нужно остановить работу и дождаться реакции, используйте ПоказатьПредупреждение() или ПоказатьВопрос(), а на платформе 8.3.18 и выше — ПредупреждениеАсинх() и ВопросАсинх() с оператором Ждать.
Типичная ситуация: разработчик дописывает обработку перепроведения, хочет показать бухгалтеру «Готово, обработано 120 документов» — и получает ошибку про модальные окна, потому что вызвал старое Предупреждение(). Или сообщение из серверной процедуры «теряется», потому что его ждали в другом месте. Разберём, какой метод вывода сообщений для какой задачи, с рабочими примерами кода.
Сообщить и СообщениеПользователю: текст в окне сообщений
Окно сообщений — самый мягкий способ что-то сказать пользователю. Оно не блокирует работу, сообщения копятся списком, их можно прочитать позже.
Метод Сообщить
Глобальный метод Сообщить() работает и на клиенте, и на сервере:
Сообщить("Обработка завершена");
Сообщить("Не заполнен контрагент", СтатусСообщения.Важное);
Второй параметр — статус (Обычное, Важное, Внимание, Информация, ОченьВажное). В управляемом интерфейсе он почти не влияет на вид, поэтому его обычно не указывают.
В синтакс-помощнике Сообщить() давно помечен как не рекомендуемый для новых разработок. Он работает, но для сообщений, которые надо привязать к полю, его возможностей не хватает.
Объект СообщениеПользователю
Рекомендуемый способ — объект СообщениеПользователю:
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Укажите дату документа";
Сообщение.Сообщить();
У объекта есть свойства, которых нет у простого метода:
- 📌
Поле— имя реквизита, рядом с которым платформа покажет сообщение; - 📌
ПутьКДанным— путь к данным формы (например,"Объект"); - 📌
КлючДанных— ссылка на объект, к которому относится сообщение; - 📌
ИдентификаторНазначения— уникальный идентификатор формы, в которой нужно показать текст.
Числа в тексте сообщения
Склейка строки с числом через + даст разделители тысяч в неожиданном виде — например, «1 250» с неразрывным пробелом. Используйте Формат() или СтрШаблон():
Количество = 1250;
Сообщить(СтрШаблон("Обработано документов: %1", Формат(Количество, "ЧГ=0")));
Сообщения с сервера
Сообщение, выданное в серверной процедуре, пользователь увидит после возврата управления на клиент. Если сервер работает 10 минут, текст появится через 10 минут, а не по ходу выполнения. Для индикации процесса это не годится — нужен фоновый процесс и чтение его сообщений (ниже).
Очистить окно сообщений перед новой порцией можно на клиенте методом ОчиститьСообщения().
Сообщение возле поля формы: привязка к реквизиту
Когда пользователь не заполнил реквизит, полезнее подсветить конкретное поле, чем писать общий текст. Платформа показывает такое сообщение рядом с полем, а щелчок по нему в окне сообщений переводит курсор в это поле.
Пример для формы документа, реквизит Контрагент:
&НаКлиенте
Процедура ПроверитьКонтрагента()
Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не заполнен контрагент";
Сообщение.Поле = "Контрагент";
Сообщение.ПутьКДанным = "Объект";
Сообщение.Сообщить();
КонецЕсли;
КонецПроцедуры
В модуле объекта, например в ОбработкаПроверкиЗаполнения, ссылаются на сам объект через УстановитьДанные():
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если СуммаДокумента <= 0 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Сумма документа должна быть больше нуля";
Сообщение.Поле = "СуммаДокумента";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Для строки табличной части поле указывают с индексом: "Товары[2].Количество". Индекс начинается с нуля, поэтому для третьей строки (НомерСтроки = 3) нужен индекс 2.
Обёртка из БСП
В типовых конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и др.) есть готовая процедура, которая делает то же в одну строку:
ОбщегоНазначения.СообщитьПользователю("Не заполнен контрагент", , "Контрагент", "Объект", Отказ);
Параметры: текст, ключ данных, поле, путь к данным, флаг отказа. На клиенте — аналог ОбщегоНазначенияКлиент.СообщитьПользователю(). Если вы дописываете типовую конфигурацию
, лучше пользоваться обёрткой: так код единообразен с остальными модулями.
Модальные Предупреждение и Вопрос: почему их больше не пишут
Старые методы Предупреждение() и Вопрос() открывают модальное окно. Код останавливается на строке вызова и ждёт, пока пользователь нажмёт кнопку:
Ответ = Вопрос("Удалить строки?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
УдалитьСтроки();
КонецЕсли;
Выглядит удобно, но в веб-клиенте браузеры плохо дружат с модальными окнами. Поэтому в 8.3 у конфигурации появилось свойство «Режим использования модальности» (в палитре свойств корня конфигурации в Конфигураторе). Значения:
| Значение | Что будет при вызове Вопрос() |
|---|---|
| Использовать | Окно откроется как раньше |
| Использовать с предупреждением | Работает, но при проверке модулей выводится предупреждение |
| Не использовать | Ошибка выполнения: модальные окна запрещены |
Новые конфигурации создаются с режимом «Не использовать», а типовые давно переведены на него. Если ваша доработка встраивается в типовую конфигурацию, модальные методы в ней не сработают.
Не переключайте режим модальности в типовой конфигурации ради своей обработки. Это изменение снимает конфигурацию с полной поддержки и не решает проблему в веб-клиенте.
Немодальные диалоги через описание оповещения
Замена модальных методов — пара «метод Показать… + процедура-обработчик». Код не ждёт ответа: он вызывает диалог и идёт дальше, а реакцию на кнопку обрабатывает отдельная экспортная процедура.
ПоказатьПредупреждение
Если реакция не нужна, обработчик можно не передавать:
&НаКлиенте
Процедура Готово(Команда)
ПоказатьПредупреждение(, "Перепроведение завершено", 10, "Готово");
КонецПроцедуры
Параметры: описание оповещения, текст, таймаут в секундах (0 — без таймаута), заголовок. Здесь окно само закроется через 10 секунд.
ПоказатьВопрос с обработчиком
&НаКлиенте
Процедура УдалитьСтрокиКоманда(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Удалить отмеченные строки?", РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
УдалитьСтроки();
КонецЕсли;
КонецПроцедуры
Что важно в этой схеме:
- ✅ обработчик обязательно
Экспорт, иначе платформа его не найдёт; - ✅ у обработчика ровно два параметра: результат и дополнительные параметры;
- ✅ всё, что должно выполниться после ответа, переносится в обработчик — строки после
ПоказатьВопрос()выполнятся сразу, не дожидаясь нажатия; - ✅ данные для обработчика передают третьим параметром
ОписаниеОповещения(обычно структурой).
Свои кнопки вместо типовых «Да/Нет» задают списком значений вместо РежимДиалогаВопрос:
Кнопки = Новый СписокЗначений;
Кнопки.Добавить("Перезаписать", "Перезаписать");
Кнопки.Добавить("Пропустить", "Пропустить");
Кнопки.Добавить(КодВозвратаДиалога.Отмена, "Отмена");
ПоказатьВопрос(Оповещение, "Файл уже есть. Что сделать?", Кнопки);
В обработчик придёт значение выбранной кнопки — строка «Перезаписать» или код отмены.
Всплывающее оповещение и строка состояния
Ещё два немодальных способа, которые часто забывают:
- 🔔
ПоказатьОповещениеПользователя("Документ записан", ПолучитьНавигационнуюСсылку(Ссылка), "Счёт № 15")— всплывающее окно в углу экрана. Подходит для «записано», «выгружено», когда окно сообщений не нужно. - ⏳
Состояние("Обработка", 40, "Строка 40 из 100")— текст и индикатор в окне состояния при долгом клиентском цикле.
Асинх и Ждать: код без процедур-обработчиков
С платформы 8.3.18 в языке есть асинхронные функции. Они возвращают объект Обещание, а оператор Ждать внутри процедуры с модификатором Асинх дожидается результата. Код снова читается сверху вниз, как в эпоху Вопрос(), но окно при этом не модальное.
&НаКлиенте
Асинх Процедура УдалитьСтрокиКоманда(Команда)
Ответ = Ждать ВопросАсинх("Удалить отмеченные строки?", РежимДиалогаВопрос.ДаНет);
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
УдалитьСтроки();
Ждать ПредупреждениеАсинх("Строки удалены");
КонецПроцедуры
Есть и асинхронные варианты ввода: ВводСтрокиАсинх(), ВводЧислаАсинх(), ВводДатыАсинх().
Асинх и Ждать работают, только если режим совместимости конфигурации не ниже 8.3.18. Если в свойствах конфигурации стоит совместимость с более ранней версией, код не скомпилируется — останутся только описания оповещений.
Выбор простой: для своей конфигурации на свежей платформе пишите через Ждать, для доработок, которые должны работать на старых режимах совместимости, — через ОписаниеОповещения.
Запрос данных у пользователя: ввод значений и формы-диалоги
Иногда пользователю нужно не только прочитать текст, но и ввести значение: дату закрытия периода, количество, комментарий.
Готовые окна ввода
&НаКлиенте
Процедура ЗапроситьКомментарий(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВводаКомментария", ЭтотОбъект);
ПоказатьВводСтроки(Оповещение, "", "Комментарий к корректировке", 100, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВводаКомментария(Строка, ДополнительныеПараметры) Экспорт
Если Строка = Неопределено Тогда
Возврат; // пользователь нажал «Отмена»
КонецЕсли;
Объект.Комментарий = Строка;
КонецПроцедуры
Аналогично работают ПоказатьВводЧисла() (с длиной и точностью), ПоказатьВводДаты() (с выбором части даты: дата, время или оба) и ПоказатьВводЗначения() для ссылок и других типов. При отмене во все обработчики приходит Неопределено — проверяйте это первой строкой.
Своя форма как диалог
Когда полей несколько (период, организация, флажки), создайте общую форму или форму обработки и откройте её с обработчиком закрытия:
&НаКлиенте
Процедура ВыбратьПараметры(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияПараметров", ЭтотОбъект);
ОткрытьФорму("ОбщаяФорма.ПараметрыВыгрузки", , ЭтаФорма, , , , Оповещение,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗакрытияПараметров(Результат, ДополнительныеПараметры) Экспорт
Если ТипЗнч(Результат) = Тип("Структура") Тогда
ВыполнитьВыгрузку(Результат);
КонецЕсли;
КонецПроцедуры
В самой форме по кнопке «ОК» собирают структуру и вызывают Закрыть(Результат). Если пользователь закрыл окно крестиком, в обработчик придёт Неопределено.
БлокироватьОкноВладельца блокирует только форму, из которой открыт диалог, а остальной интерфейс остаётся доступным. Это не модальность в старом смысле, поэтому режим «Не использовать» ей не мешает.
Ошибки и исключения: как сообщить о сбое
ВызватьИсключение
Если продолжать нельзя, прервите выполнение исключением. Пользователь увидит окно ошибки с вашим текстом, транзакция откатится:
Если Не ЗначениеЗаполнено(Организация) Тогда
ВызватьИсключение "Не указана организация. Выгрузка невозможна.";
КонецЕсли;
Не используйте исключения для обычной проверки заполнения: при записи объекта для этого есть Отказ = Истина и сообщение у поля.
Попытка и понятный текст для пользователя
Технический текст ошибки («Ошибка при вызове метода контекста (Записать)…») пользователю мало что говорит. Показывайте краткое представление, а подробности пишите в журнал регистрации:
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Перепроведение", УровеньЖурналаРегистрации.Ошибка,
ДокументОбъект.Метаданные(), ДокументОбъект.Ссылка, ПодробноеПредставлениеОшибки(Инфо));
Сообщить(СтрШаблон("Не удалось провести %1: %2", ДокументОбъект.Ссылка, КраткоеПредставлениеОшибки(Инфо)));
КонецПопытки;
Так обработка не падает на первом сбойном документе, а в конце пользователь видит список проблемных документов в окне сообщений. Подробный стек остаётся в журнале регистрации (в БП 3.0 его открывают из раздела Администрирование → Обслуживание).
Сообщения из фонового задания
Длительные операции в типовых решениях запускают в фоне. Сообщения, выданные внутри фонового задания, сами на экран не попадают. Клиент периодически опрашивает задание и забирает их:
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
Если Задание <> Неопределено Тогда
Для Каждого Сообщение Из Задание.ПолучитьСообщенияПользователю(Истина) Цикл
Сообщение.Сообщить();
КонецЦикла;
КонецЕсли;
Этот код выполняется на сервере (вызов из клиентского обработчика ожидания). Параметр Истина удаляет полученные сообщения, чтобы при следующем опросе они не задвоились.
Какой способ вывода выбрать: сравнение
| Задача | Метод | Блокирует работу | Ограничения |
|---|---|---|---|
| Информационный итог, список проблем | СообщениеПользователю, Сообщить() | Нет | С сервера — после возврата на клиент |
| Ошибка заполнения конкретного поля | СообщениеПользователю с Поле или БСП | Нет | Нужно верно указать путь к данным |
| Короткое «записано / выгружено» | ПоказатьОповещениеПользователя() | Нет | Только клиент |
| Важное предупреждение | ПоказатьПредупреждение() / ПредупреждениеАсинх() | Окно ждёт нажатия | Только клиент |
| Подтверждение действия | ПоказатьВопрос() / ВопросАсинх() | Окно ждёт ответа | Логику после ответа — в обработчик или после Ждать |
| Ввод одного значения | ПоказатьВводСтроки/Числа/Даты() | Окно ждёт ввода | Проверять Неопределено |
| Несколько параметров | Своя форма + ОткрытьФорму() с оповещением | Окно владельца | Нужна отдельная форма |
| Продолжать нельзя | ВызватьИсключение | Прерывает операцию | Откатывает транзакцию |
Правило выбора по сути одно: чем меньше пользователь должен решить, тем мягче способ. Окно с кнопкой «ОК» после каждой строки обработки раздражает и не читается. Сто строк в окне сообщений — читаются.
Частые вопросы
Почему сообщение с сервера не появляется сразу?
Можно ли вызвать ПоказатьВопрос на сервере?
Почему обработчик оповещения не вызывается?
Экспорт или в ОписаниеОповещения указан не тот модуль. Для формы вторым параметром передают ЭтотОбъект, для общего клиентского модуля — имя модуля.Как показать сообщение в конкретной открытой форме?
СообщениеПользователю свойство ИдентификаторНазначения значением УникальныйИдентификатор нужной формы. Это полезно, когда сообщение формируется на сервере для формы, вызвавшей обработку.Чем Сообщить отличается от ПоказатьОповещениеПользователя?
Сообщить() пишет в окно сообщений формы, текст остаётся в списке. Оповещение — всплывающее окно в углу, оно исчезает через несколько секунд и годится только для коротких уведомлений.