Как программно вывести сообщение пользователю на управляемых формах 1С 8.3 из серверного кода или фонового задания?

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

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

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

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

Решение 1: Вывод сообщения после завершения клиентского запроса к серверу

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

Порядок действий:

  1. На клиентской стороне вызываем серверную процедуру.
  2. Серверная процедура выполняет свою логику.
  3. Результат операции (например, успешность или тип сообщения) сервер передает обратно клиенту. Это можно сделать через возвращаемое значение функции или путем записи информации во временное хранилище, а затем получения ее на клиенте.
  4. Клиентская процедура, которая инициировала вызов, после получения результата, анализирует его и вызывает соответствующие клиентские функции для показа сообщения.

Рассмотрим пример кода:

Допустим, у нас есть кнопка на форме, которая вызывает серверную процедуру для обработки данных.


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

// Клиентская процедура, которая вызывает серверную и обрабатывает ответ
// Она может быть вызвана, например, по событию "ПриКомпоновкеРезультата" или "ОбработкаВыбора"
Процедура ПослеПринятияДанныхНаСервере() Экспорт
    // Вызываем серверную процедуру
    ПринятьДанныеНаСервере();

    // Получаем оповещение из временного хранилища
    // Необходимо передать тот же ключ, который использовался при помещении
    Оповещение = ПолучитьИзВременногоХранилища(УникальныйИдентификаторТекущейФормы);

    Если Оповещение <> Неопределено Тогда
        Если Оповещение.ИмяМетода = "ПоказатьУспехНаКлиенте" Тогда
            // Используем ПоказатьОповещениеПользователя для неблокирующего уведомления
            ПоказатьОповещениеПользователя(Оповещение.Параметры,,, "Успех");
        ИначеЕсли Оповещение.ИмяМетода = "ПоказатьОшибкаНаКлиенте" Тогда
            // Используем ПоказатьПредупреждение для блокирующего сообщения об ошибке
            ПоказатьПредупреждение(, Оповещение.Параметры);
        КонецЕсли;
        // Очищаем временное хранилище после использования
        УдалитьИзВременногоХранилища(УникальныйИдентификаторТекущейФормы);
    КонецЕсли;
КонецПроцедуры

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

Важный момент: идентификатор временного хранилища (УникальныйИдентификаторТекущейФормы в примере) должен быть уникальным для каждого сеанса и, возможно, для каждой формы, чтобы сообщения не путались между разными пользователями или открытыми формами.

Решение 2: Вывод сообщения из фонового задания или автономного серверного кода через механизм опроса

Более сложный, но часто встречающийся сценарий — это когда сообщение нужно показать пользователю, но нет прямого клиентского вызова, который ожидает ответа. Например, когда завершаются длительные операции в БСП, или если вы выстраиваете сложный конвейер обработки задач на сервере. В этом случае клиент должен сам периодически "спрашивать" сервер, нет ли для него новых сообщений.

Этот механизм называют "пульсацией" или "опросом". Он требует организации хранилища сообщений на сервере и механизма периодического опроса на клиенте.

Порядок действий:

  1. На сервере (в фоновом или регламентном задании) формируется сообщение для конкретного пользователя.
  2. Это сообщение записывается в какое-либо хранилище, доступное для сервера и клиента. Это может быть:
    • Временное хранилище, связанное с сеансом пользователя.
    • Регистр сведений, специально предназначенный для хранения сообщений пользователям (как, например, в Библиотеке Стандартных Подсистем - БСП).
  3. На клиенте настраивается обработчик ожидания, который с заданной периодичностью (например, каждые 5-10 секунд) вызывает серверную функцию.
  4. Серверная функция проверяет наличие новых сообщений для текущего пользователя в хранилище, возвращает их клиенту и, как правило, удаляет их из хранилища.
  5. Клиентская функция принимает сообщения и отображает их пользователю.

Разберем реализацию с использованием временного хранилища и обработчика ожидания.

Шаг 1: Подготовка хранилища сообщений на сервере

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


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

    // Уникальный идентификатор для текущего сеанса
    ИдентификаторСеанса = ПараметрыСеанса.УникальныйИдентификаторСеанса; 
    
    // Получаем текущий массив сообщений из временного хранилища
    МассивСообщений = ПолучитьИзВременногоХранилища(ИдентификаторСеанса);
    Если МассивСообщений = Неопределено Тогда
        МассивСообщений = Новый Массив;
    КонецЕсли;

    // Добавляем новое сообщение
    НовоеСообщение = Новый Структура("Текст, Тип, Пользователь", ТекстСообщения, ТипСообщения, ПользовательИБ);
    МассивСообщений.Добавить(НовоеСообщение);

    // Помещаем обновленный массив обратно во временное хранилище
    ПоместитьВоВременноеХранилище(МассивСообщений, ИдентификаторСеанса);
КонецПроцедуры

// Функция для получения сообщений клиентом
Функция ПолучитьСообщенияДляПользователяНаСервере() Экспорт
    ИдентификаторСеанса = ПараметрыСеанса.УникальныйИдентификаторСеанса;
    МассивСообщений = ПолучитьИзВременногоХранилища(ИдентификаторСеанса);

    Если МассивСообщений = Неопределено Тогда
        Возврат Новый Массив;
    КонецЕсли;

    // Важно: удаляем сообщения из хранилища после того, как они были получены клиентом,
    // чтобы они не показывались повторно
    УдалитьИзВременногоХранилища(ИдентификаторСеанса);
    Возврат МассивСообщений;
КонецФункции

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


// Пример вызова из фонового задания
Процедура ВыполнитьФоновоеЗадание() Экспорт
    Попытка
        // ... логика фонового задания ...
        Если УспехВыполнения Тогда
            // Сообщаем текущему пользователю, запустившему задание
            // Или конкретному пользователю, если его ИД был передан в параметры задания
            МойОбщийМодуль.ЗаписатьСообщениеДляПользователя("Фоновое задание успешно завершено!", "Успех");
        Иначе
            МойОбщийМодуль.ЗаписатьСообщениеДляПользователя("Ошибка при выполнении фонового задания. Подробности в журнале.", "Ошибка");
        КонецЕсли;
    Исключение Э
        МойОбщийМодуль.ЗаписатьСообщениеДляПользователя("Критическая ошибка в фоновом задании: " + Э.Описание, "Ошибка");
    КонецПопытки;
КонецПроцедуры

Шаг 2: Настройка обработчика ожидания на клиенте

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


// В модуле управляемого приложения или в модуле основной формы (которая всегда открыта, например, рабочее место)
Перем мИдентификаторОбработчикаОжидания Экспорт; // Переменная для хранения идентификатора обработчика

// Процедура, которая вызывается при старте клиентского приложения
Процедура ПриНачалеРаботыСистемы()
    // Подключаем обработчик ожидания, который будет вызываться каждые 5 секунд (3000 мс)
    // Третий параметр (Истина) означает, что обработчик будет отключаться после каждого вызова
    // и его нужно будет подключать заново (или передать Ложь, если он должен работать постоянно)
    // Однако, для проверки сообщений лучше переподключать, чтобы избежать накопления вызовов
    мИдентификаторОбработчикаОжидания = ПодключитьОбработчикОжидания("ПроверитьСообщенияСервера", 5); 
    // Вместо 5 секунд, можно использовать 10, 15 секунд в зависимости от нужной оперативности и нагрузки.
КонецПроцедуры

// Процедура, вызываемая обработчиком ожидания на клиенте
Процедура ПроверитьСообщенияСервера() Экспорт
    // Сначала отключаем текущий обработчик, чтобы не создавать дубли при длительном выполнении серверного вызова
    ОтключитьОбработчикОжидания(мИдентификаторОбработчикаОжидания);

    // Вызываем серверную функцию для получения сообщений
    Сообщения = МойОбщийМодульКлиентСервер.ПолучитьСообщенияДляПользователяНаСервере(); // Вызов серверной функции

    Если Сообщения.Количество() > 0 Тогда
        Для Каждого Сообщение Из Сообщения Цикла
            Если Сообщение.Тип = "Ошибка" Тогда
                ПоказатьПредупреждение(, Сообщение.Текст);
            ИначеЕсли Сообщение.Тип = "Предупреждение" Тогда
                 ПоказатьПредупреждение(, Сообщение.Текст, СтандартнаяКартинка.Предупреждение);
            Иначе
                ПоказатьОповещениеПользователя(Сообщение.Текст,,, Сообщение.Тип);
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    
    // После обработки сообщений, снова подключаем обработчик ожидания
    мИдентификаторОбработчикаОжидания = ПодключитьОбработчикОжидания("ПроверитьСообщенияСервера", 5);
КонецПроцедуры

// При завершении работы системы или закрытии основной формы, обработчик можно отключить
Процедура ПриЗавершенииРаботыСистемы()
    ОтключитьОбработчикОжидания(мИдентификаторОбработчикаОжидания);
КонецПроцедуры

Нюансы и улучшения:

Решение 3: Использование типовых механизмов БСП (Библиотека Стандартных Подсистем)

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

Принцип работы типового механизма:

  1. Серверный код вызывает метод, который записывает сообщение в регистр сведений СообщенияДляПользователей для конкретного пользователя или группы пользователей.
  2. Клиентская часть (обычно в модуле управляемого приложения) имеет обработчик ожидания, который вызывает серверный метод из ОбщегоНазначенияКлиентСервер для получения новых сообщений.
  3. Серверный метод считывает сообщения из регистра, помечает их как прочитанные (или удаляет) и возвращает клиенту.
  4. Клиент отображает полученные сообщения.

Пример использования типовых механизмов (концептуально, детали могут отличаться в зависимости от версии БСП):


// На сервере (например, в модуле регламентного задания или фонового)
Процедура ОтправитьСообщениеЧерезБСП(ПользовательИБ, ТекстСообщения, ТипСообщения = "Информация") Экспорт
    Если ТипСообщения = "Ошибка" Тогда
        ТипСообщенияБСП = ТипыСообщений.Ошибка;
    ИначеЕсли ТипСообщения = "Предупреждение" Тогда
        ТипСообщенияБСП = ТипыСообщений.Предупреждение;
    Иначе
        ТипСообщенияБСП = ТипыСообщений.Информация;
    КонецЕсли;

    // Использование универсального метода для отправки сообщения пользователю
    // Этот метод записывает сообщение в регистр сведений или во временное хранилище
    // в зависимости от того, активен ли пользователь
    УведомленияПользователей.СообщитьПользователю(ТекстСообщения, ПользовательИБ, ТипСообщенияБСП);
КонецПроцедуры

// На клиенте (в модуле управляемого приложения)
// Глобальный обработчик для проверки и отображения уведомлений
Процедура ПриНачалеРаботыСистемы()
    // Подключаем глобальный обработчик ожидания для проверки уведомлений
    // Периодичность, например, 10 секунд
    ПодключитьОбработчикОжидания("ПроверитьИПоказатьУведомленияБСП", 10, Истина);
КонецПроцедуры

Процедура ПроверитьИПоказатьУведомленияБСП() Экспорт
    // Получаем список новых уведомлений для текущего пользователя
    МассивУведомлений = УведомленияПользователей.ПолучитьУведомленияДляТекущегоПользователя();

    Для Каждого Уведомление Из МассивУведомлений Цикла
        Если Уведомление.ТипУведомления = ТипыСообщений.Ошибка Тогда
            ПоказатьПредупреждение(, Уведомление.Текст);
        ИначеЕсли Уведомление.ТипУведомления = ТипыСообщений.Предупреждение Тогда
            ПоказатьПредупреждение(, Уведомление.Текст, СтандартнаяКартинка.Предупреждение);
        Иначе
            ПоказатьОповещениеПользователя(Уведомление.Текст,,, Уведомление.ТипУведомления);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Преимущества типовых механизмов БСП:

Общие рекомендации и важные моменты

При реализации вывода сообщений из серверного кода всегда учитывайте следующие моменты:

  1. Контекст пользователя: ПоказатьОповещениеПользователя и ПоказатьПредупреждение работают только в клиентском контексте.
  2. Качество кода: При разработке сложных механизмов взаимодействия полезно проводить анализ конфигурации на наличие ошибок, чтобы избежать некорректных вызовов экспортных процедур.
  3. Ресурсы сервера и клиента: Механизм опроса (ПодключитьОбработчикОжидания) потребляет ресурсы. Оптимально выбирайте интервал опроса.
  4. Управление сеансами: Если пользователь закрывает приложение, убедитесь, что сообщения обрабатываются корректно. Типовые механизмы БСП обычно учитывают это.

Мы рассмотрели различные подходы к выводу сообщений пользователям из серверного кода и фоновых заданий в 1С 8.3 на управляемых формах. Выбирайте подходящий метод в зависимости от требований к оперативности и сложности вашей системы. Для большинства профессиональных задач рекомендуется использовать механизмы, подобные тем, что реализованы в Библиотеке Стандартных Подсистем.

← На главную