Как вывести сообщение пользователю из серверной процедуры, если форма документа закрывается?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

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

Метод 1. Перехват сообщений через ДополнительныеСвойства и перенаправление владельцу

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

Проанализируем ситуацию пошагово:

  1. В модуле объекта (в процедуре проведения или перед записью) мы выполняем вызов веб-сервиса. Если получаем ошибку, записываем её в дополнительные свойства объекта.

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

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

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

Метод 2. Использование регистра сведений и глобального оповещения

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

Рассмотрим алгоритм реализации:

  1. Создадим непериодический регистр сведений ОчередьУведомленийПользователей. Поля: Пользователь (Ресурс), ТекстСообщения (Ресурс), ДатаСобытия (Измерение).
  2. В серверном коде проведения, если возникла ошибка интеграции, просто записываем данные в этот регистр.

// В модуле объекта
Запись = РегистрыСведений.ОчередьУведомленийПользователей.СоздатьМенеджерЗаписи();
Запись.Пользователь = ПараметрыСеанса.ТекущийПользователь;
Запись.ТекстСообщения = "Ошибка при создании заказа в базе №2: " + ОписаниеОшибки;
Запись.ДатаСобытия = ТекущаяДата();
Запись.Записать();
  1. В Модуле управляемого приложения создадим обработчик ожидания, который будет периодически проверять этот регистр и выводить сообщения через ПоказатьОповещениеПользователя.

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

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

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

Метод 3. Система взаимодействия (Collaboration System)

Проанализируем современный способ, доступный в платформе 8.3 — Систему взаимодействия. Если в вашей организации настроен сервер взаимодействия, это самый правильный путь для оперативных уведомлений.

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


Обсуждение = СистемаВзаимодействия.ПолучитьОбсуждение(КлючОбсуждения); 
НовоеСообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
НовоеСообщение.Текст = "Ошибка синхронизации заказа " + Объект.Номер;
НовоеСообщение.Записать();

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

Важное замечание про веб-сервисы в транзакции

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

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

Такой подход обеспечивает высокую отказоустойчивость системы и не заставляет пользователя ждать окончания сетевого запроса при каждом нажатии кнопки "Провести".

Резюме

Если вам нужно быстрое решение для типовой конфигурации вроде УТ 11.4 без изменения архитектуры обмена:

Помните, что использование ВызватьИсключение в данном контексте недопустимо, так как это приведет к отмене проведения заказа в первой базе, что противоречит вашей бизнес-задаче.

← На главную