Почему сообщение в `ПриСозданииНаСервере` отображается в родительской форме при вводе на основании?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

В этой статье мы подробно разберем, почему так происходит, и найдем надежный способ отображать сообщения именно там, где они и должны быть.

Анализ проблемы: жизненный цикл формы и механика сообщений

Чтобы понять корень проблемы, нужно разобраться в том, как платформа 1С обрабатывает создание форм и вывод сообщений в клиент-серверной архитектуре. Этот процесс состоит из нескольких ключевых этапов.

Когда мы инициируем ввод на основании, происходит следующее:

  1. Выполняется серверный вызов для создания нового объекта и его первоначального заполнения (например, через обработчик ОбработкаЗаполнения в модуле объекта).
  2. Далее на сервере запускается процедура создания формы. Выполняется код в обработчике ПриСозданииНаСервере. На этом этапе объект формы уже существует в памяти сервера, у него даже сформирован уникальный идентификатор (УникальныйИдентификатор). Однако на стороне клиента эта форма еще не существует. Клиентское приложение еще не получило данные для её отрисовки и не создало соответствующее окно.
  3. После завершения работы серверного кода форма сериализуется и передается на клиент.
  4. Клиент получает данные, создает окно формы и выполняет код клиентских обработчиков, в частности, ПриОткрытии.

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

Когда клиентская часть платформы обрабатывает этот буфер, она пытается найти форму-получателя. Если ей это не удается (например, потому что окно для формы с указанным идентификатором еще не создано), платформа использует запасной вариант: она выводит сообщение в текущее активное окно. В нашем сценарии с вводом на основании активным окном является как раз та форма, из которой мы начали операцию.

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

Практическое решение: отложенный вывод сообщения через `ПриОткрытии`

Зная причину, мы можем сформулировать надежное решение. Идея заключается в том, чтобы не выводить сообщение немедленно на сервере, а "отложить" эту операцию до того момента, когда форма будет гарантированно готова на клиенте. Идеальный момент для этого — событие ПриОткрытии.

Разберем алгоритм по шагам:

  1. Создаем реквизит формы для хранения текста сообщения.

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

  2. На сервере сохраняем текст сообщения в этот реквизит.

    В обработчике ПриСозданииНаСервере вместо вызова Сообщить() мы будем записывать текст нашего сообщения в созданный реквизит.

    Посмотрим на пример:

    
    &НаСервере
    Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
        // Проверяем, что документ-основание передан
        Если ЗначениеЗаполнено(Параметры.Основание) Тогда
            
            // Выполняем проверку. Например, хотим предупредить, если основание уже проведено
            Если Параметры.Основание.Проведен Тогда
                // Вместо прямого вывода сообщения, сохраняем его текст в реквизит формы
                ТекстСообщенияПриОткрытии = "Внимание! Документ-основание уже проведен. Проверьте корректность данных.";
            КонецЕсли;
            
        КонецЕсли;
    
    КонецПроцедуры
    
  3. На клиенте выводим сообщение из реквизита.

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

    Пример кода:

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

Этот подход является надежным и рекомендованным, так как он четко разделяет логику подготовки данных на сервере и логику их отображения на клиенте, учитывая особенности жизненного цикла управляемой формы.

А что делать, если это критическая ошибка?

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

В таком случае использовать СообщениеПользователю нецелесообразно. Стандартным и правильным механизмом для таких ситуаций является генерация исключения.

На сервере следует использовать конструкцию ВызватьИсключение:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    Если Не ПроверкаПравДоступа() Тогда
        // Прерываем создание формы и выводим пользователю текст ошибки
        ВызватьИсключение "Недостаточно прав для создания документа!";
    КонецЕсли;

КонецПроцедуры

При вызове исключения выполнение кода на сервере прерывается, форма на клиент не передается и, соответственно, не открывается. Платформа сама позаботится о том, чтобы корректно отобразить текст исключения пользователю.

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

← На главную