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