Как отправить интерактивное сообщение или вопрос другому пользователю при проведении документа в 1С:УПП 1.3

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

В процессе автоматизации бизнес-процессов на платформе «1С:Предприятие 8» часто возникает задача оперативного информирования сотрудников о ключевых событиях в базе данных. Например, при проведении приходной накладной менеджером склада необходимо мгновенно отправить уведомление или интерактивный вопрос сотруднику отдела закупок: «Поступила новая продукция! Желаете ознакомиться с документом?».

Главная сложность этой задачи в том, что в системе 1С процессы проведения документов выполняются транзакционно и синхронно. Мы не можем напрямую из транзакции проведения вызвать диалоговое окно или предупреждение на экране другого пользователя. Любые модальные вызовы заблокируют работу текущего сеанса, а до сеанса получателя сигнал просто не дойдет. В этой статье мы подробно разберем, как решить эту задачу в конфигурации «Управление производственным предприятием 1.3» (обычные формы) с помощью нескольких надежных архитектурных подходов.

Почему нельзя просто вызвать диалог? Разбор архитектурного тупика

Давайте проанализируем ситуацию. Когда пользователь «А» проводит документ (например, ПоступлениеТоваровУслуг), код выполняется на сервере (или в серверной части файловой базы) в контексте его сеанса. Пользователь «Б» в этот момент работает в своем изолированном сеансе. Прямого канала передачи данных между активными сеансами клиентов (особенно в обычных формах без использования современных веб-сокетов системы взаимодействия) не существует.

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

  1. При проведении документа сеанс «А» записывает информацию о событии во временное или постоянное общее хранилище (базу данных).
  2. Сеанс «Б» периодически опрашивает это хранилище на предмет наличия новых сообщений, адресованных именно ему.
  3. При обнаружении записи сеанс «Б» считывает ее, выводит сообщение пользователю на экран и делает пометку в хранилище о том, что сообщение прочитано.

Выясним, какие инструменты предоставляет нам платформа 1С для реализации этой схемы в рамках конфигурации УПП 1.3.

---

Решение 1. Собственный регистр сведений и обработчик ожидания (Классический метод)

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

Шаг 1. Создание метаданных

В конфигураторе создадим новый непериодический независимый регистр сведений. Назовем его, например, _ОповещенияПользователей.

Настроим структуру регистра:

Шаг 2. Запись оповещения при проведении документа

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


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

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

Шаг 3. Подключение периодической проверки у пользователя

Теперь нам нужно заставить клиентское приложение каждого пользователя периодически заглядывать в наш регистр. Для этого используется метод ПодключитьОбработчикОжидания().

Так как мы работаем в обычном приложении, инициализацию обработчика нужно производить в Модуле обычного приложения в процедуре ПриНачалеРаботыСистемы. Проанализируем пример кода:


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

Шаг 4. Реализация процедуры проверки и интерактивного диалога

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

Разберем детально логику этой процедуры:


Процедура ПроверитьНовыеОповещения() Экспорт
    
    #Если Клиент Тогда
        
        // Получаем текущего авторизованного пользователя системы
        ТекущийПользователь = ПараметрыСеанса.ТекущийПользователь;
        
        // Формируем запрос к регистру сведений для поиска непрочитанных записей
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |	Оповещения.ДокументСсылка КАК ДокументСсылка,
            |	Оповещения.ТекстСообщения КАК ТекстСообщения
            |ИЗ
            |	РегистрСведений._ОповещенияПользователей КАК Оповещения
            |ГДЕ
            |	Оповещения.Получатель = &ТекущийПользователь
            |	И НЕ Оповещения.Прочитано";
        
        Запрос.УстановитьПараметр("ТекущийПользователь", ТекущийПользователь);
        
        ТаблицаОповещений = Запрос.Выполнить().Выгрузить();
        
        Для Каждого СтрокаОповещения Из ТаблицаОповещений Цикл
            
            // Задаем интерактивный вопрос пользователю
            Ответ = Вопрос(СтрокаОповещения.ТекстСообщения, РежимДиалогаВопрос.ДаНет, 0,, "Новое уведомление!");
            
            Если Ответ = КодВозвратаДиалога.Да Тогда
                // Открываем форму проведенного документа
                ОткрытьЗначение(СтрокаОповещения.ДокументСсылка);
            КонецЕсли;
            
            // Фиксируем факт прочтения в базе данных
            ЗафиксироватьПрочтениеОповещения(ТекущийПользователь, СтрокаОповещения.ДокументСсылка);
            
        КонецЦикла;
        
    #КонецЕсли;
    
КонецПроцедуры

Процедура ЗафиксироватьПрочтениеОповещения(Пользователь, Документ)
    
    НаборЗаписей = РегистрыСведений._ОповещенияПользователей.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Получатель.Установить(Пользователь);
    НаборЗаписей.Отбор.ДокументСсылка.Установить(Документ);
    
    НаборЗаписей.Прочитать();
    Если НаборЗаписей.Количество() > 0 Тогда
        НаборЗаписей[0].Прочитано = Истина;
        НаборЗаписей.Записать(Истина);
    КонецЕсли;
    
КонецПроцедуры

---

Решение 2. Использование типового механизма задач и бизнес-процессов

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

Посмотрим на алгоритм реализации через механизм задач:

Шаг 1. Программное создание задачи при проведении накладной

В конфигурации УПП 1.3 есть стандартный объект метаданных — задача ЗадачиИсполнителя. При проведении документа мы можем программно породить новую задачу для определенного адресата.


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

Шаг 2. Оповещение о новых задачах

В УПП 1.3 существует штатный список задач текущего пользователя. Чтобы пользователь мгновенно узнавал о появлении новой задачи, мы можем пойти двумя путями:

  1. Настроить в интерфейсе автоматическое открытие формы списка невыполненных задач при старте системы.
  2. Использовать аналогичный обработчик ожидания, который проверяет наличие невыполненных задач, адресованных текущему пользователю, и выводит красивое всплывающее окно.

Для реализации второго варианта используем метод ПоказатьОповещениеПользователя(), доступный в современных версиях платформы 8.3:


Процедура ПроверитьНовыеЗадачи() Экспорт
    
    #Если Клиент Тогда
        
        ТекущийПользователь = ПараметрыСеанса.ТекущийПользователь;
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |	Задачи.Ссылка КАК ЗадачаСсылка,
            |	Задачи.Наименование КАК Наименование,
            |	Задачи.Предмет КАК Предмет
            |ИЗ
            |	Задача.ЗадачиИсполнителя КАК Задачи
            |ГДЕ
            |	Задачи.Исполнитель = &ТекущийПользователь
            |	И НЕ Задачи.Выполнена";
            
        Запрос.УстановитьПараметр("ТекущийПользователь", ТекущийПользователь);
        Выборка = Запрос.Выполнить().Выбрать();
        
        Пока Выборка.Следующий() Цикл
            
            // Формируем красивое ненавязчивое облачко-оповещение в углу экрана
            ТекстОповещения = "Нажмите для открытия задачи";
            НавигационнаяСсылка = ПолучитьНавигационнуюСсылку(Выборка.Предмет);
            
            ПоказатьОповещениеПользователя(
                "Вам назначена новая задача!",
                НавигационнаяСсылка,
                Выборка.Наименование,
                БиблиотекаКартинок.Информация
            );
            
        КонецЦикла;
        
    #КонецЕсли;
    
КонецПроцедуры

---

Решение 3. Использование встроенной подсистемы CRM и документа «Событие»

В УПП 1.3 встроена развитая CRM-подсистема. Оповещения сотрудников можно элегантно настроить с помощью встроенного механизма напоминаний по документу Событие.

Разберем, как активировать и использовать этот механизм:

  1. Включение напоминаний в настройках: Убедитесь, что у пользователя-получателя в его персональных настройках (меню Сервис — Настройки пользователя) установлен флаг «Использовать напоминания». При активном флаге система автоматически запускает фоновый опрос регистра напоминаний.
  2. Программное создание документа «Событие» при проведении: Напишем код, который сформирует документ события с типом «Напоминание»:

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

Результат: Стандартный внутренний механизм УПП сам обнаружит эту запись и выведет пользователю-получателю встроенную форму «Напоминания о событиях». Пользователю будет доступна кнопка перехода непосредственно к самой накладной.

---

Сравнение методов: какой вариант выбрать?

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

Критерий сравнения Собственный регистр + Обработчик ожидания Штатные Задачи (Бизнес-процессы) CRM-механизм (Документ "Событие")
Сложность разработки Средняя (требуется создавать новые метаданные и писать логику опроса) Низкая (используются готовые объекты конфигурации УПП) Минимальная (готовые объекты и штатный встроенный опросник)
Нагрузка на СУБД Может возрастать при некорректно настроенных индексах и малом интервале опроса Умеренная (индексы по задачам оптимизированы платформой) Минимальная (механизм глубоко протестирован разработчиками 1С)
Интерактивность Максимальная (можно задать любой сложный вопрос с выбором действий "Да/Нет/Отмена") Высокая (задача имеет ссылки на документы, исполнителей и историю) Средняя (выводится классическое окно напоминания 1С)
Возможность потери сообщения Минимальная (пока флаг Прочитано не установлен, система продолжит напоминать) Исключена (задача висит в списке до тех пор, пока ее не пометят выполненной) Средняя (пользователь может случайно «отложить все» или закрыть окно напоминания)

---

Важные правила и советы по оптимизации

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

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

← На главную