В процессе разработки в среде 1С:Предприятие часто возникает задача информирования ответственных лиц о тех или иных событиях в системе, например, о проведении документа производства или поступления товаров. Для решения этой задачи можно использовать механизм подписок на события, регистры сведений и обработчики ожидания. Однако начинающие разработчики часто сталкиваются с ошибкой "Попытка передачи с клиента на сервер мутабельного значения". Разберем пошагово, как правильно выстроить архитектуру такой системы и устранить технические ошибки.
Прежде чем переходить к коду, рассмотрим общую схему работы системы уведомлений. Нам необходимо создать механизм, который будет фиксировать событие на сервере и отображать его пользователю на клиенте. Проанализируем основные компоненты:
Выясним причину возникновения ошибки, с которой столкнулся автор темы. Ошибка Попытка передачи с клиента на сервер мутабельного значения возникает, когда система пытается передать между контекстами (например, с клиента на сервер) объект, который может быть изменен (мутабельный объект). К таким объектам относятся ДокументОбъект, СправочникОбъект и другие.
В подписках на события параметр Источник всегда является объектом (например, ДокументОбъект.ОтчетПроизводстваМембран). Если общий модуль, в котором находится обработчик подписки, имеет неправильные настройки (например, включен флаг Глобальный или код пытается вызвать клиентский метод), платформа выдает ошибку. Для исправления ситуации необходимо:
Рассмотрим код обработчика подписки на событие ОбработкаПроведения. Наша задача — определить ответственного пользователя и сделать запись в регистр сведений ОП_ОповещениеПользователей. Обратите внимание на оптимизированный подход к работе с объектом Источник.
Процедура ОП_ОповещениеПользователейОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт
// Проверяем настройку через константу
Если Не Константы.ОП_ОповещениеПользователей.Получить() Тогда
Возврат;
КонецЕсли;
// Получаем ссылку на основание напрямую из реквизитов объекта Источник
// ВАЖНО: Обращаемся к реквизитам через Источник.ИмяРеквизита
ЗаданиеСсылка = Источник.ЗаданиеНаМеханическийУчасток;
Если ЗначениеЗаполнено(ЗаданиеСсылка) Тогда
ПользовательПолучатель = ЗаданиеСсылка.Ответственный;
// Подготовим набор записей для записи в регистр
НаборЗаписей = РегистрыСведений.ОП_ОповещениеПользователей.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ДокументСобытие.Установить(Источник.Ссылка);
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.ДокументСобытие = Источник.Ссылка;
НоваяЗапись.ДополнительныйДокумент = ЗаданиеСсылка;
НоваяЗапись.Пользователь = ПользовательПолучатель;
НоваяЗапись.ТекстСообщения = "Проведен документ: " + Строка(Источник.Ссылка);
НоваяЗапись.ОповещениеВыполнено = Ложь;
Попытка
НаборЗаписей.Записать();
Исключение
ЗаписьЖурналаРегистрации("Оповещение", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Для того чтобы пользователь увидел сообщение, в модуле управляемого приложения (или обычного приложения) необходимо подключить обработчик ожидания. Рассмотрим, как это сделать в процедуре ПриНачалеРаботыСистемы.
Процедура ПриНачалеРаботыСистемы()
// Подключаем проверку каждые 5 минут (300 секунд)
ПодключитьОбработчикОжидания("ОП_ПроверитьНовыеОповещения", 300, Ложь);
КонецПроцедуры
Затем в клиентском общем модуле опишем процедуру ОП_ПроверитьНовыеОповещения. Вместо блокирующего окна ПоказатьВопрос, которое использовал автор, проанализируем использование метода ПоказатьОповещениеПользователя. Это более современный и удобный для пользователя подход.
Процедура ОП_ПроверитьНовыеОповещения() Экспорт
// Получаем данные с сервера
ДанныеОповещений = ОП_ОповещениеПользователейСервер.ПолучитьНепрочитанныеОповещения();
Для Каждого СтрокаОповещения Из ДанныеОповещений Цикл
// Формируем всплывающее окно
Текст = СтрокаОповещения.ТекстСообщения;
СсылкаНаОбъект = СтрокаОповещения.ДокументСобытие;
ПоказатьОповещениеПользователя("Новое событие",
ПолучитьНавигационнуюСсылку(СсылкаНаОбъект),
Текст,
БиблиотекаКартинок.ИнформационныйСборка);
// Помечаем в регистре, что уведомление доставлено
ОП_ОповещениеПользователейСервер.ПометитьКакПрочитанное(СсылкаНаОбъект);
КонецЦикла;
КонецПроцедуры
Проанализируем предложенный автором механизм и внесем несколько важных улучшений для стабильной работы системы:
ПараметрыСеанса.ТекущийПользователь. Это работает быстрее, чем вызов функций из БСП или поиск в справочнике.ОбработкаПроведения выполняется внутри транзакции. Если ваша запись в регистр завершится неудачей, документ не проведется. Оборачивайте запись в Попытка...Исключение, чтобы критическая ошибка в системе уведомлений не блокировала работу пользователей.Системы взаимодействий. Это встроенный механизм, позволяющий отправлять сообщения пользователям мгновенно (через WebSocket), без использования периодических опросов регистра через обработчики ожидания. Это значительно снижает нагрузку на сервер БД.ЗаметкиПользователя или ТекущиеДела.Таким образом, для исправления ошибки мутабельного значения достаточно корректно настроить свойства общего модуля (флаг Сервер) и следить за тем, чтобы ДокументОбъект не покидал пределы серверного контекста подписки. Использование ссылочных типов (ДокументСсылка) при передаче данных в другие процедуры полностью решает эту проблему.