Предупреждения, сообщения и диалоги во встроенном языке 1С

· 9 мин

Содержание

Самый короткий способ показать предупреждение пользователю во встроенном языке 1С — вызвать Предупреждение("Текст"). Это блокирующее модальное окно с кнопкой «ОК». Но в тонком и веб-клиенте модальные вызовы по умолчанию запрещены, поэтому в современных конфигурациях вместо него используют асинхронный ПоказатьПредупреждение() или конструкцию Ждать ПредупреждениеАсинх().

Кроме окна с «ОК», предупреждение можно оформить как вопрос с кнопками Вопрос(), как привязанное к полю сообщение СообщениеПользователю, как всплывающее оповещение в углу или как собственную форму. Ниже — все рабочие варианты с кодом: когда какой уместен и как не поймать ошибку модальности.

Что именно нужно показать пользователю?

Классическое окно с кнопкой «ОК»: функция Предупреждение()

Функция Предупреждение() выводит модальное окно с текстом и единственной кнопкой «ОК». Пока пользователь её не нажмёт, выполнение кода стоит на месте.

Предупреждение("Сохраните документ перед закрытием.");

У функции есть ещё два параметра — таймаут автозакрытия (в секундах) и заголовок окна:

Предупреждение("Сеанс завершится через 10 секунд.", 10, "Внимание");

Если таймаут равен нулю (значение по умолчанию), окно ждёт нажатия сколько угодно. При положительном значении окно закроется само по истечении срока — это удобно для информационных сообщений, которые не должны «ловить» пользователя.

Предупреждение() — синхронный модальный вызов. В тонком и веб-клиенте при свойстве конфигурации «Режим использования модальности» = «Не использовать» такой вызов вызовет ошибку компиляции или выполнения. Для новых конфигураций используйте асинхронные методы из следующего раздела.

Проверить режим можно так: откройте в Конфигураторе свойства корня конфигурации и найдите свойство Режим использования модальности. Три варианта — «Использовать», «Использовать с предупреждениями» и «Не использовать». В типовых конфигурациях на управляемых формах стоит «Не использовать».

Неблокирующие вызовы: ПоказатьПредупреждение() и Ждать

Асинхронная замена — процедура ПоказатьПредупреждение(). Она не останавливает код: окно показывается, управление сразу возвращается, а то, что должно выполниться после нажатия «ОК», выносится в отдельную процедуру-обработчик оповещения.

&НаКлиенте
Процедура ПроверитьИПродолжить(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеПредупреждения", ЭтотОбъект);
    ПоказатьПредупреждение(Оповещение, "Операция завершена.");
КонецПроцедуры

&НаКлиенте
Процедура ПослеПредупреждения(ДополнительныеПараметры) Экспорт
    // Этот код выполнится после того, как пользователь нажмёт «ОК».
КонецПроцедуры

Объект ОписаниеОповещения хранит имя процедуры-обработчика и модуль, где она лежит. Третьим параметром в него можно передать произвольные данные — они придут в ДополнительныеПараметры.

В платформе 8.3.18 и новее появился более читаемый вариант — асинхронные методы с ключевыми словами Асинх и Ждать. Код после ожидания пишется прямо под вызовом, без отдельной процедуры:

&НаКлиенте
Асинх Процедура ПроверитьИПродолжить(Команда)
    Ждать ПредупреждениеАсинх("Операция завершена.");
    // Код продолжается здесь после нажатия «ОК».
КонецПроцедуры

Процедура-обработчик должна удовлетворять трём условиям, иначе платформа не сможет её вызвать: она лежит в том же модуле, что указан вторым параметром ОписаниеОповещения (ЭтотОбъект — модуль текущей формы), помечена Экспорт и имеет директиву компиляции &НаКлиенте. Несовпадение имени процедуры в строке и в объявлении — самая частая причина, почему «после окна ничего не происходит».

Если первый параметр ОписаниеОповещения передать как Неопределено, окно просто покажется, а продолжения не будет — это допустимо, когда после закрытия ничего делать не нужно.

Диалог с кнопками: Вопрос() и ПоказатьВопрос()

Когда нужно не просто уведомить, а получить ответ, подойдёт Вопрос(). Набор кнопок задаёт системное перечисление РежимДиалогаВопрос, а результат возвращается значением КодВозвратаДиалога.

Ответ = Вопрос("Провести документ перед закрытием?", РежимДиалогаВопрос.ДаНетОтмена);
Если Ответ = КодВозвратаДиалога.Да Тогда
    // провести
ИначеЕсли Ответ = КодВозвратаДиалога.Нет Тогда
    // закрыть без проведения
Иначе
    // отмена — ничего не делаем
КонецЕсли;

Основные наборы кнопок:

  • 🔹 ДаНет — две кнопки для бинарного выбора;
  • 🔹 ДаНетОтмена — ещё и возможность передумать;
  • 🔹 ОКОтмена — подтверждение действия;
  • 🔹 ПовторитьОтмена — после неудачной операции предложить повтор.

Асинхронный аналог — ПоказатьВопрос(). Результат приходит первым параметром обработчика:

&НаКлиенте
Процедура СпроситьИЗакрыть(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект);
    ПоказатьВопрос(Оповещение, "Закрыть без сохранения?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВопроса(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        Модифицированность = Ложь;
        Закрыть();
    КонецЕсли;
КонецПроцедуры

У Вопрос() есть дополнительные параметры: таймаут автозакрытия, кнопка по умолчанию (на которой стоит фокус) и заголовок окна. Для опасных операций ставьте по умолчанию безопасный вариант, чтобы случайный Enter не запустил удаление:

Ответ = Вопрос("Удалить документ без возможности отмены?",
    РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет, "Подтверждение");

В версиях 8.3.18+ есть и ВопросАсинх(), который возвращает КодВозвратаДиалога через Ждать — без отдельной процедуры обработки.

Сообщения без остановки работы: Сообщить() и СообщениеПользователю

Модальное окно уместно не всегда: если нужно просто вывести текст в служебную область, лучше подходит объект СообщениеПользователю. Он не блокирует интерфейс и умеет привязываться к полю формы.

Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не заполнен контрагент.";
Сообщение.Поле = "Контрагент";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();

Свойство Поле указывает реквизит, рядом с которым 1С подсветит сообщение и подставит красную рамку. Для табличной части путь к полю записывают с индексом строки — например, Товары[2].Количество. Это стандартный приём в процедуре ОбработкаПроверкиЗаполнения() объекта.

Старая глобальная функция Сообщить("Текст") тоже выводит строку в ту же область, но она признана устаревшей и привязку к полю не поддерживает. В новом коде вместо неё применяют СообщениеПользователю.

Канонический случай этого объекта — проверка заполнения в модуле объекта. В процедуре ОбработкаПроверкиЗаполнения() каждое нарушение выводят отдельным сообщением с указанием поля, а чтобы платформа не сформировала своё стандартное сообщение, реквизит исключают из массива проверяемых:

Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Выберите контрагента.";
        Сообщение.Поле = "Контрагент";
        Сообщение.УстановитьДанные(ЭтотОбъект);
        Сообщение.Сообщить();
        Отказ = Истина;
    КонецЕсли;
КонецПроцедуры

Для строки табличной части или элемента списка вместо (или вместе с) Поле заполняют свойство КлючДанных ссылкой на нужный объект — тогда по клику на сообщение платформа откроет именно его.

Сообщения, выведенные таким способом из серверного кода при записи объекта, автоматически попадут в окно сообщений формы, из которой шла запись.

Всплывающие оповещения в углу экрана

Когда действие прошло успешно и прерывать пользователя незачем, подойдёт ПоказатьОповещениеПользователя() — небольшая всплывающая плашка в правом нижнем углу окна 1С, которая гаснет сама.

ПоказатьОповещениеПользователя(
    "Документ записан: " + Объект.Номер,
    ПолучитьНавигационнуюСсылку(Объект.Ссылка),
    "Нажмите, чтобы открыть",
    ,
    СтатусОповещенияПользователя.Информация);

Параметры по порядку: текст, навигационная ссылка (куда перейти по клику), пояснение, картинка и статус. Со статусом СтатусОповещенияПользователя.Важное оповещение не исчезает автоматически — пользователь закроет его сам. Последним параметром можно передать ключ уникальности: оповещения с одинаковым ключом не дублируются, а обновляют уже показанное — это спасает, когда уведомление формируется в цикле.

Отдельно про «системные уведомления Windows»: встроенный язык 1С своего API к центру уведомлений операционной системы не имеет. Всплывающие оповещения показываются средствами самой платформы внутри окна приложения, а не в системном трее. Если нужна именно интеграция с уведомлениями ОС, это решается внешней компонентой — штатной функции для этого в платформе нет, поэтому не рассчитывайте на неё в типовом коде.

Своя форма вместо стандартного окна

Стандартные окна не настраиваются: нельзя добавить третью кнопку с произвольной надписью, поле ввода или картинку. Когда этого мало, делают отдельную общую форму и открывают её как модальный диалог.

&НаКлиенте
Процедура ПоказатьДиалог(Команда)
    Параметры = Новый Структура("Текст", "Удалить помеченные объекты?");
    Оповещение = Новый ОписаниеОповещения("ПослеДиалога", ЭтотОбъект);
    ОткрытьФорму("ОбщаяФорма.ДиалогПодтверждения", Параметры, ЭтотОбъект,
        , , , Оповещение, РежимОткрытияОкна.БлокироватьОкноВладельца);
КонецПроцедуры

&НаКлиенте
Процедура ПослеДиалога(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = Истина Тогда
        // пользователь подтвердил
    КонецЕсли;
КонецПроцедуры

Режим БлокироватьОкноВладельца делает окно модальным по отношению к форме-владельцу, но не ломается в веб-клиенте — в отличие от устаревшего ОткрытьФормуМодально(). Внутри своей формы кнопки возвращают результат через Закрыть(ЗначениеВозврата), и он приходит в обработчик оповещения.

Чтобы диалог вернул не просто «да / нет», а введённые значения, кнопки формы закрывают её структурой. Вызывающий код разбирает её в обработчике:

// В модуле формы диалога, по кнопке «Подтвердить»:
Результат = Новый Структура("Подтверждено, Комментарий", Истина, Комментарий);
Закрыть(Результат);
// В обработчике оповещения:
Если ТипЗнч(Результат) = Тип("Структура") И Результат.Подтверждено Тогда
    ТекстПричины = Результат.Комментарий;
КонецЕсли;

Такой подход оправдан, когда диалог должен собрать данные: например, выбрать причину списания и ввести комментарий одной формой. Если же пользователь закрыл окно крестиком, в обработчик придёт Неопределено — этот случай всегда проверяйте перед обращением к полям результата.

Предупреждения в фоновых заданиях

В фоновом задании интерфейса нет — ни одно из окон (Предупреждение, Вопрос, открытие формы) там не сработает, потому что некому нажимать кнопки. Это частая ошибка: код, который прекрасно работал на форме, в фоне падает или молча ничего не делает.

Что можно вместо этого:

  1. Писать ход работы в журнал регистрации (см. следующий раздел) — это надёжнее всего.
  2. Накапливать сообщения через СообщениеПользователю — после завершения задания их забирают функцией ПолучитьСообщенияПользователю(Истина) и показывают пользователю уже в основном сеансе.
  3. Возвращать результат и текст через параметр-структуру, если фоновое задание запущено из вашего кода и вы сами разбираете его итог.
// В теле фонового задания:
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не загружено 3 строки из файла.";
Сообщение.Сообщить();

Не пытайтесь «достучаться» до пользователя из фона модальным окном. Сеанс фонового задания изолирован, и попытка показать диалог либо вызовет исключение, либо зависнет без эффекта.

Запись предупреждений в журнал регистрации

Если предупреждение важно сохранить для разбора — а не только показать на секунду, — его пишут в журнал регистрации процедурой ЗаписьЖурналаРегистрации().

ЗаписьЖурналаРегистрации(
    "ЗагрузкаДанных.Проверка",
    УровеньЖурналаРегистрации.Предупреждение,
    ,
    ,
    "Пропущено 3 строки: не найден контрагент.");

Параметры: имя события (произвольная строка, по которой потом фильтруют), уровень важности, метаданные, ссылка на данные и текст комментария. Уровни задаёт перечисление УровеньЖурналаРегистрации: Информация, Предупреждение, Ошибка, Отказ.

Посмотреть записанное можно в Конфигураторе через меню Администрирование → Журнал регистрации, а в режиме 1С:Предприятие — в разделе администрирования соответствующей конфигурации. В журнале удобно отбирать записи по имени события и по уровню: так легко собрать все предупреждения конкретной обработки за день.

Сравнение способов по задаче:

СпособБлокирует работуВиден в фонеПривязка к полю
Предупреждение()данетнет
ПоказатьПредупреждение()нетнетнет
Вопрос()данетнет
СообщениеПользователюнетчастичнода
ПоказатьОповещениеПользователя()нетнетнет
ЗаписьЖурналаРегистрации()нетданет

Частые вопросы

Почему Предупреждение() выдаёт ошибку «использование модальных вызовов запрещено»?
В свойствах конфигурации стоит «Режим использования модальности» = «Не использовать». Это нормально для управляемого приложения. Замените вызов на ПоказатьПредупреждение() с обработчиком оповещения или на Ждать ПредупреждениеАсинх().
Как сделать предупреждение с кнопками «Да» и «Нет»?
Используйте Вопрос("Текст", РежимДиалогаВопрос.ДаНет) и сравните результат с КодВозвратаДиалога.Да. В асинхронном коде — ПоказатьВопрос() или ВопросАсинх().
Можно ли показать предупреждение из серверной процедуры?
Напрямую окно с сервера не открыть — оно показывается на клиенте. С сервера выводят СообщениеПользователю, а окна и вопросы вызывают из клиентских процедур формы.
Чем всплывающее оповещение отличается от окна Предупреждение()?
ПоказатьОповещениеПользователя() не прерывает работу и гаснет само через несколько секунд, а Предупреждение() держит фокус до нажатия «ОК». Первое — для успешных уведомлений, второе — когда пользователь обязан прочитать.
Как задать своё время автозакрытия окна?
У Предупреждение() и ПоказатьПредупреждение() есть параметр таймаута в секундах. Например, Предупреждение("Готово", 5) закроется через 5 секунд, даже если «ОК» не нажали.