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

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

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

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

Что такое мутабельное значение и почему возникает ошибка

Проанализируем базовые принципы клиент-серверной архитектуры платформы 1С:Предприятие. Мутабельными (изменяемыми) называются сложные типы данных платформы, состояние которых непрерывно меняется в оперативной памяти и которые платформа не может автоматически сериализовать для прозрачной передачи по сетевому каналу между тонким/толстым клиентом и сервером.

К мутабельным типам относятся:

  1. Объекты прикладных данных: ДокументОбъект, СправочникОбъект, ПланВидовХарактеристикОбъект и другие.
  2. Коллекции значений: ТаблицаЗначений, ДеревоЗначений.
  3. Формы и их элементы управления.

Ошибка генерируется платформой в том случае, когда из клиентского контекста (с клиента или из модуля с признаком выполнения на клиенте) вызывается метод серверного общего модуля с флагом «Вызов сервера», и в качестве одного из параметров передается мутабельный объект (например, сам объект документа НовДок).

Посмотрим на типичный фрагмент проблемного кода в обработчике события ПередЗаписью:


Процедура ПередЗаписью(Объект, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    // Если модуль выполняется на клиенте, а ДляРасчетныхВедомостейСервер — на сервере:
    ДляРасчетныхВедомостейСервер.УпорядочитьСодержание(Объект); // Здесь возникает ошибка
КонецПроцедуры

Способ 1. Перенос логики создания и записи объекта на сервер

Рассмотрим наиболее архитектурно правильный подход. В клиент-серверном приложении создание, заполнение и запись прикладных объектов должны выполняться исключительно в серверном контексте. При взаимодействии «сервер-сервер» передача мутабельных значений не вызывает исключений, так как данные не пересекают границу сети.

Перенесем процедуру формирования документа в серверный модуль или процедуру формы с директивой &НаСервере:


&НаСервере
Процедура СоздатьИЗаписатьДокументНаСервере(НачПериода)
    НовДок = Документы.ВедомостьНачислений.СоздатьДокумент();
    НовДок.Дата = НачПериода;
    
    // Заполнение табличных частей и реквизитов документа
    // ...
    
    // Запись объекта на сервере
    НовДок.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры

&НаКлиенте
Процедура СформироватьВедомости(Команда)
    СоздатьИЗаписатьДокументНаСервере(ПериодРегистрации);
КонецПроцедуры

Способ 2. Использование дополнительных свойств для обхода вызова

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

В типовых конфигурациях часто предусмотрен параметр ЭтоЗагрузка в коллекции ДополнительныеСвойства. Передадим этот флаг перед выполнением записи:


НовДок = Документы.ВедомостьНачислений.СоздатьДокумент();
НовДок.Дата = НачПериода;

// Устанавливаем флаг загрузки в дополнительные свойства объекта
НовДок.ДополнительныеСвойства.Вставить("ЭтоЗагрузка", Истина);

НовДок.Записать();

Также можно воспользоваться стандартным свойством ОбменДанными.Загрузка. При установке флага в значение Истина платформенные и прикладные обработчики проверок отключаются:


НовДок.ОбменДанными.Загрузка = Истина;
НовДок.Записать();

Способ 3. Передача сериализуемых структур вместо ссылочных объектов

Если серверной процедуре действительно необходимо передать данные с клиента, разберем способ передачи через сериализуемые типы. Вместо передачи всего объекта ДокументОбъект передадим структуру с ключевыми полями или массив структур, представляющих строки табличной части.


// На клиенте подготавливаем структуру параметров
ПараметрыВызова = Новый Структура;
ПараметрыВызова.Вставить("Организация", ВыбраннаяОрганизация);
ПараметрыВызова.Вставить("Период", ДатаДокумента);

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

Если требуется передать уже существующий объект, передаем на сервер только ссылку ДокументСсылка, а на сервере получаем объект методом Ссылка.ПолучитьОбъект():


&НаСервере
Процедура ОбработатьДокументПоСсылке(СсылкаНаДокумент)
    ОбъектДокумента = СсылкаНаДокумент.ПолучитьОбъект();
    Если ОбъектДокумента <> Неопределено Тогда
        ДляРасчетныхВедомостейСервер.УпорядочитьСодержание(ОбъектДокумента);
        ОбъектДокумента.Записать();
    КонецЕсли;
КонецПроцедуры

Способ 4. Проверка и корректировка свойств общих модулей

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

  1. Откроем свойства общего модуля РасчетныеВедомости и целевого модуля (например, ДляРасчетныхВедомостейСервер).
  2. Убедимся, в каких контекстах модуль должен компилироваться. Если модуль помечен только как «Сервер» и «Вызов сервера», то прямой вызов с передачей мутабельных объектов из клиентских модулей запрещен.
  3. Если логика модуля не зависит исключительно от серверного окружения и требуется поддержка работы в толстом клиенте, установим флаг «Клиент (обычное приложение)» или «Клиент (управляемое приложение)» в зависимости от режима работы базы.

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

← На главную