Сообщения, предупреждения и вопросы в 1С 8.3: какой метод выбрать и как не сломать форму
Содержание
Чтобы в 1С вывести сообщение пользователю, в простейшем случае хватит одной строки: Сообщить("Документ проведён");. Текст появится в окне сообщений внизу формы. Если нужно окно, которое пользователь обязан закрыть, на клиенте вызывают ПоказатьПредупреждение(), а если нужен ответ «Да/Нет» — ПоказатьВопрос() с обработчиком через ОписаниеОповещения.
Типичная ситуация: разработчик дописывает проверку в документ реализации, ставит старый добрый Предупреждение("Не заполнен склад"), а у бухгалтера в тонком клиенте вылетает ошибка «Использование модальных окон в данном режиме запрещено». Дальше разберём все способы вывода информации в 1С 8.3 и правила, по которым выбирается нужный метод: где выполняется код, нужен ли ответ, надо ли подсветить конкретное поле.
Сообщить, СообщениеПользователю и оповещения: базовые способы вывода информации
У платформы 1С:Предприятие 8.3 есть несколько механизмов. Они отличаются тем, мешают ли работе пользователя и где их можно вызвать.
| Метод | Что видит пользователь | Где вызывается | Ждёт реакции |
|---|---|---|---|
Сообщить() | строку в окне сообщений формы | клиент и сервер | нет |
СообщениеПользователю | строку с привязкой к полю формы | клиент и сервер | нет |
ПоказатьОповещениеПользователя() | всплывающее окно в углу экрана | только клиент | нет |
Состояние() | текст и индикатор в строке состояния | только клиент | нет |
ПоказатьПредупреждение() | окно с кнопкой «ОК» | только клиент | да, закрыть окно |
ПоказатьВопрос() | окно с кнопками ответа | только клиент | да, выбрать ответ |
Сообщить()
Самый короткий способ. Первый параметр — текст, второй (необязательный) — статус сообщения из системного перечисления СтатусСообщения.
Сообщить("Загружено строк: " + Формат(КоличествоСтрок, "ЧГ=0"));
Сообщить("Цена не указана", СтатусСообщения.Внимание);
Числа склеивайте со строкой через Формат(Число, "ЧГ=0") или СтрШаблон(). Иначе 12500 превратится в «12 500» с неразрывным пробелом, и такой текст неудобно копировать.
Объект СообщениеПользователю
Это полноценная замена Сообщить(). Объект умеет привязывать текст к реквизиту: пользователь кликает по сообщению, и курсор встаёт в нужное поле.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = НСтр("ru = 'Не заполнен контрагент'");
Сообщение.Поле = "Контрагент";
Сообщение.ПутьКДанным = "Объект";
Сообщение.Сообщить();
Основные свойства:
- 📝
Текст— сам текст сообщения; - 🎯
Поле— имя реквизита или путь к ячейке табличной части; - 📂
ПутьКДанным— где лежит реквизит, в форме обычно"Объект"; - 🔑
КлючДанных— ссылка на объект, если сообщение формируется вне формы; - 🪪
ИдентификаторНазначения— уникальный идентификатор формы, в которую нужно доставить сообщение.
Обёртка из БСП
В типовых конфигурациях (БП 3.0, ЗУП 3.1, УТ 11 и других на Библиотеке стандартных подсистем) удобнее процедура ОбщегоНазначения.СообщитьПользователю(). На клиенте — её аналог ОбщегоНазначенияКлиент.СообщитьПользователю(). Внутри она создаёт тот же СообщениеПользователю, а заодно умеет взводить флаг Отказ.
ОбщегоНазначения.СообщитьПользователю(
НСтр("ru = 'Не указан склад'"), , "Склад", "Объект", Отказ);
Порядок параметров: текст, ключ данных, поле, путь к данным, отказ.
Оповещение и строка состояния
ПоказатьОповещениеПользователя() выводит ненавязчивое окно, которое само исчезает. Подходит для фраз вроде «Файл выгружен», когда прерывать работу не нужно.
ПоказатьОповещениеПользователя("Выгрузка завершена", ,
"Файл сохранён в папку обмена", БиблиотекаКартинок.Информация);
Состояние() пишет текст в строку состояния и умеет показывать прогресс от 0 до 100. Его вызывают в длинном клиентском цикле, чтобы пользователь видел, что программа не зависла.
Состояние("Обработка строк", Процент, "Строка " + Формат(Номер, "ЧГ=0"));
Диалог с пользователем: предупреждение, вопрос и получение ответа
В платформе 8.3 есть два поколения диалогов: модальные и немодальные. Модальные Предупреждение() и Вопрос() останавливают выполнение кода до ответа. В веб-клиенте и при запрете модальности в свойствах конфигурации они вызывают ошибку.
ПоказатьПредупреждение
&НаКлиенте
Процедура ПроверитьДату(Команда)
Если Объект.Дата > ТекущаяДата() Тогда
ПоказатьПредупреждение(, "Дата документа больше текущей", 10, "Проверка");
КонецЕсли;
КонецПроцедуры
Параметры: описание оповещения о закрытии (можно пропустить), текст, таймаут в секундах, заголовок окна. При таймауте 10 окно закроется само через 10 секунд.
ПоказатьВопрос и обработка ответа
Код после ПоказатьВопрос() выполняется сразу, не дожидаясь ответа. Поэтому всё, что зависит от выбора пользователя, переносят в отдельную экспортную процедуру.
&НаКлиенте
Процедура УдалитьСтроки(Команда)
Оповещение = Новый ОписаниеОповещения("УдалитьСтрокиЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Удалить все строки табличной части?",
РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет, "Подтверждение");
КонецПроцедуры
&НаКлиенте
Процедура УдалитьСтрокиЗавершение(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Объект.Товары.Очистить();
КонецЕсли;
КонецПроцедуры
Наборы кнопок РежимДиалогаВопрос: ДаНет, ДаНетОтмена, ОК, ОКОтмена, ПовторитьОтмена, ПрерватьПовторитьПропустить. Если стандартных подписей мало, вместо режима передают СписокЗначений: значение — что вернётся в Результат, представление — надпись на кнопке.
Кнопки = Новый СписокЗначений;
Кнопки.Добавить("Перезаполнить", "Перезаполнить");
Кнопки.Добавить("Добавить", "Добавить к существующим");
Кнопки.Добавить(КодВозвратаДиалога.Отмена, "Отмена");
Процедура-обработчик обязательно объявляется с ключевым словом Экспорт и находится в модуле, переданном во втором параметре ОписаниеОповещения. Без Экспорт ответ пользователя просто потеряется.
Ввод значения
Получить от пользователя не выбор, а значение помогают ПоказатьВводЧисла(), ПоказатьВводСтроки(), ПоказатьВводДаты() и ПоказатьВводЗначения(). Они работают по той же схеме: результат приходит в процедуру из ОписаниеОповещения, при отказе — Неопределено.
Асинхронный синтаксис: Ждать ВопросАсинх
С платформы 8.3.18 можно писать без цепочек обработчиков. Процедуру помечают словом Асинх, а результат диалога получают оператором Ждать.
&НаКлиенте
Асинх Процедура Перезаполнить(Команда)
Ответ = Ждать ВопросАсинх("Перезаполнить табличную часть?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
ПерезаполнитьНаСервере();
КонецЕсли;
КонецПроцедуры
Для окна без вопроса есть ПредупреждениеАсинх(). Код читается сверху вниз, как в старом модальном стиле, но модальных окон не создаёт.
Асинхронный синтаксис требует, чтобы в свойствах конфигурации режим совместимости был не ниже 8.3.18. В типовых конфигурациях, которые поддерживают более ранние версии, его не встретить — там по-прежнему ОписаниеОповещения.
Сообщения в управляемых формах: клиент, сервер и фоновые задания
В управляемом приложении код живёт на клиенте и на сервере. Отсюда главное правило: диалоговые окна существуют только на клиенте. На сервере доступны лишь Сообщить() и СообщениеПользователю.
Сообщения с сервера
Сообщение, сформированное в процедуре &НаСервере, копится и приходит на клиент после возврата из серверного вызова. Пользователь увидит его в окне сообщений той формы, из которой шёл вызов.
Если нужно выбрать форму явно (например, вызов идёт из общего модуля), задают ИдентификаторНазначения:
&НаСервере
Процедура ПроверитьНаСервере()
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Остатка не хватает";
Сообщение.ИдентификаторНазначения = УникальныйИдентификатор;
Сообщение.Сообщить();
КонецПроцедуры
Привязка к строке табличной части
Чтобы курсор встал в конкретную ячейку, в Поле указывают путь с индексом строки. Индекс начинается с нуля, а НомерСтроки — с единицы, отсюда вычитание.
Для Каждого Строка Из Объект.Товары Цикл
Если Строка.Количество = 0 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Строка %1: не указано количество", Строка.НомерСтроки);
Сообщение.Поле = СтрШаблон("Товары[%1].Количество", Формат(Строка.НомерСтроки - 1, "ЧГ=0"));
Сообщение.ПутьКДанным = "Объект";
Сообщение.Сообщить();
КонецЕсли;
КонецЦикла;
Для строки с НомерСтроки = 3 поле получится Товары[2].Количество — это третья строка таблицы.
Сообщения в модуле объекта
В модуле объекта документа нет формы. Чтобы сообщение всё равно подсветило реквизит, вызывают Сообщение.УстановитьДанные(ЭтотОбъект) и указывают Поле без пути:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если Не ЗначениеЗаполнено(Склад) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Укажите склад";
Сообщение.Поле = "Склад";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Фоновые задания
Сообщения из фонового задания на экран сами не попадают — у задания нет окна. Они накапливаются в задании, а клиент забирает их методом ПолучитьСообщенияПользователю() объекта ФоновоеЗадание. Параметр Истина удаляет полученные сообщения, чтобы при следующем опросе они не дублировались.
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
Для Каждого Сообщение Из Задание.ПолучитьСообщенияПользователю(Истина) Цикл
Сообщение.Сообщить();
КонецЦикла;
В типовых конфигурациях эту работу выполняет механизм длительных операций БСП, руками опрашивать задание обычно не приходится.
Ошибки и исключения: как сообщить, что пошло не так
Сообщение и исключение решают разные задачи. Сообщение информирует, а исключение прерывает выполнение.
- 🛑
ВызватьИсключение "Текст";— останавливает код. Если исключение никто не перехватил, платформа покажет текст в окне ошибки, а транзакция записи откатится. - 🧯
Попытка ... Исключение ... КонецПопытки— перехват, когда ошибку нужно обработать, а не показывать «как есть». - 📓
ЗаписьЖурналаРегистрации()— техническая подробность для администратора, а не для бухгалтера.
Попытка
ЗагрузитьФайл(ИмяФайла);
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Загрузка прайса", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Инфо));
ОбщегоНазначения.СообщитьПользователю(
"Не удалось загрузить файл: " + КраткоеПредставлениеОшибки(Инфо));
КонецПопытки;
Пользователь видит короткую понятную причину. Полный стек вызовов уходит в журнал регистрации (Администрирование → Обслуживание → Журнал регистрации в БП 3.0).
Пустой блок Исключение КонецПопытки — худший вариант. Ошибка исчезает бесследно, пользователь уверен, что всё загрузилось, а данных нет.
Отказ вместо исключения
В обработчиках ПередЗаписью, ОбработкаПроведения, ОбработкаПроверкиЗаполнения проще установить Отказ = Истина и вывести сообщение. Запись отменится, а пользователь увидит в окне сообщений полный список проблем. Исключение остановилось бы на первой.
Собственная форма вместо стандартного окна
Стандартный вопрос показывает только текст и кнопки. Когда нужно больше — таблица расхождений, флажок «Больше не показывать», выбор склада — делают свою форму.
Порядок действий:
- В конфигураторе создайте общую форму или форму обработки, например
ВыборВариантаЗаполнения. - Разместите на ней нужные элементы и кнопки.
- В обработчике каждой кнопки вызовите
Закрыть(Значение)— это значение вернётся вызывающему коду. - Откройте форму с описанием оповещения и режимом блокировки владельца.
&НаКлиенте
Процедура ВыбратьВариант(Команда)
Параметры = Новый Структура("КоличествоСтрок", Объект.Товары.Количество());
Оповещение = Новый ОписаниеОповещения("ВыбратьВариантЗавершение", ЭтотОбъект);
ОткрытьФорму("ОбщаяФорма.ВыборВариантаЗаполнения", Параметры, ЭтаФорма, , , ,
Оповещение, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура ВыбратьВариантЗавершение(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат; // окно закрыли крестиком
КонецЕсли;
ЗаполнитьПоВарианту(Результат);
КонецПроцедуры
В самой форме кнопка выглядит так:
&НаКлиенте
Процедура Перезаполнить(Команда)
Закрыть("Перезаполнить");
КонецПроцедуры
Режим БлокироватьОкноВладельца не делает окно модальным в смысле платформы. Он лишь закрывает доступ к форме-владельцу, поэтому работает и при запрете модальности, и в веб-клиенте.
В БСП есть готовый вопрос с флажком «Больше не задавать этот вопрос» — процедура ОбщегоНазначенияКлиент.ПоказатьВопросПользователю(). Перед тем как рисовать свою форму, проверьте, не закрывает ли задачу она.
Частые ошибки при выводе сообщений и как их избежать
Ещё несколько правил, которые экономят время на сопровождении:
- 🔤 Тексты для пользователя оборачивайте в
НСтр("ru = '...'"). Даже если перевод не планируется, так тексты проще найти и проверить. - 🧮 Не показывайте сообщение внутри цикла на 5 000 строк. Соберите ошибки в массив или строку и выведите один итог.
- 🙊 Не выводите на экран технику: GUID, текст запроса, стек вызовов. Бухгалтеру нужно «что не так и что сделать», остальное — в журнал регистрации.
- 🪟 Не злоупотребляйте вопросами. Если окно «Вы уверены?» появляется на каждое действие, пользователь перестаёт читать и жмёт «Да» не глядя.
- 🧹 Перед долгой операцией очищайте старые сообщения методом глобального контекста
ОчиститьСообщения(), чтобы прошлые ошибки не путались с новыми.