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

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

Архитектура механизма оповещений

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

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

Причина ошибки «Мутабельное значение»

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

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

Реализация серверной части (Запись в регистр)

Рассмотрим код обработчика подписки на событие ОбработкаПроведения. Наша задача — определить ответственного пользователя и сделать запись в регистр сведений ОП_ОповещениеПользователей. Обратите внимание на оптимизированный подход к работе с объектом Источник.


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

    // Получаем ссылку на основание напрямую из реквизитов объекта Источник
    // ВАЖНО: Обращаемся к реквизитам через Источник.ИмяРеквизита
    ЗаданиеСсылка = Источник.ЗаданиеНаМеханическийУчасток;

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

Реализация клиентской части (Отображение уведомления)

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


Процедура ПриНачалеРаботыСистемы()
    // Подключаем проверку каждые 5 минут (300 секунд)
    ПодключитьОбработчикОжидания("ОП_ПроверитьНовыеОповещения", 300, Ложь);
КонецПроцедуры

Затем в клиентском общем модуле опишем процедуру ОП_ПроверитьНовыеОповещения. Вместо блокирующего окна ПоказатьВопрос, которое использовал автор, проанализируем использование метода ПоказатьОповещениеПользователя. Это более современный и удобный для пользователя подход.


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

Рекомендации по оптимизации

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

  1. Использование Параметров Сеанса: При получении текущего пользователя на сервере используйте ПараметрыСеанса.ТекущийПользователь. Это работает быстрее, чем вызов функций из БСП или поиск в справочнике.
  2. Транзакционность: Помните, что код в подписке ОбработкаПроведения выполняется внутри транзакции. Если ваша запись в регистр завершится неудачей, документ не проведется. Оборачивайте запись в Попытка...Исключение, чтобы критическая ошибка в системе уведомлений не блокировала работу пользователей.
  3. Система взаимодействий: Если вы работаете на платформе 8.3.10 и выше, рассмотрите возможность использования Системы взаимодействий. Это встроенный механизм, позволяющий отправлять сообщения пользователям мгновенно (через WebSocket), без использования периодических опросов регистра через обработчики ожидания. Это значительно снижает нагрузку на сервер БД.
  4. Центр оповещений БСП: Если ваша конфигурация построена на базе БСП, в ней уже есть механизмы для работы с уведомлениями и задачами. Изучите подсистему ЗаметкиПользователя или ТекущиеДела.

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

← На главную