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