В этой статье мы подробно разберем, как эффективно использовать функционал УстановитьПривилегированныйРежим в 1С:Предприятии, особенно когда вы сталкиваетесь с ошибками "недостаточно прав" при работе с внешними обработками. Мы выясним, почему эта системная процедура иногда не срабатывает и как правильно организовать код, чтобы операции, требующие полных административных прав, выполнялись без проблем.
Представим ситуацию: вы разработали внешнюю обработку для удобного перемещения малоценных основных средств (МЦ.04) по ответственным лицам в конфигурации 1С:Бухгалтерия Предприятия 2.0. При попытке сохранить сформированные проводки в регистр бухгалтерии, пользователь, не обладающий полными административными правами, получает ошибку "недостаточно прав". Естественным желанием является использование процедуры УстановитьПривилегированныйРежим(Истина), но зачастую это не приносит желаемого результата.
Ключевая причина: Контекст выполнения.
Функция УстановитьПривилегированныйРежим(Истина) является системной процедурой, которая временно отменяет все ограничения прав доступа для текущего сеанса или определенной части кода. Однако, ее действие ограничено: привилегированный режим может быть установлен только в серверном контексте выполнения. Это означает, что если вы попытаетесь вызвать ее из клиентского модуля (например, из модуля формы внешней обработки, которая запускается на клиенте), она просто не сработает, независимо от типа вашей базы (файловой или клиент-серверной).
Прежде чем перейти к основному решению, давайте рассмотрим два обязательных условия, без которых работа привилегированного режима будет невозможна или некорректна.
СведенияОВнешнейОбработке вашей внешней обработки свойство БезопасныйРежим установлено в Ложь. Это является необходимым, но не достаточным условием для включения привилегированного режима.
Функция СведенияОВнешнейОбработке() Экспорт
РегистрационныеДанные = Новый Структура;
РегистрационныеДанные.Вставить("Наименование", "Перемещение МЦ04 по МОЛ");
РегистрационныеДанные.Вставить("БезопасныйРежим", Ложь); // Вот это ключевой момент!
РегистрационныеДанные.Вставить("Вид", "ДополнительнаяОбработка");
РегистрационныеДанные.Вставить("Версия", "1.0");
РегистрационныеДанные.Вставить("Информация", "Перемещение МЦ04 по МОЛ");
ТЗКоманд = Новый ТаблицаЗначений;
ТЗКоманд.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Строка"));
ТЗКоманд.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
ТЗКоманд.Колонки.Добавить("Модификатор", Новый ОписаниеТипов("Строка"));
ТЗКоманд.Колонки.Добавить("ПоказыватьОповещение", Новый ОписаниеТипов("Булево"));
ТЗКоманд.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка"));
СтрокаКоманды = ТЗКоманд.Добавить();
СтрокаКоманды.Идентификатор = "ПеремещениеМЦ04ПоМОЛ";
СтрокаКоманды.Представление = "Перемещение МЦ04 по МОЛ";
СтрокаКоманды.ПоказыватьОповещение = Истина;
СтрокаКоманды.Использование = "ОткрытиеФормы";
РегистрационныеДанные.Вставить("Команды", ТЗКоманд);
Возврат РегистрационныеДанные;
КонецФункции
УстановитьПривилегированныйРежим.
Процедура УстановитьПривилегированныйРежим не возвращает значения. Ошибка "Обращение к процедуре как к функции" возникает, если вы пытаетесь вызвать ее так, будто она должна что-то вернуть (например, присваивая результат переменной), или с некорректным синтаксисом. Правильный вызов выглядит так:
УстановитьПривилегированныйРежим(Истина);
// ... ваш код, требующий привилегий ...
УстановитьПривилегированныйРежим(Ложь);
Как мы уже выяснили, главный принцип работы УстановитьПривилегированныйРежим – это выполнение на сервере. Для внешних обработок это означает, что нам нужно "передать" выполнение операций, требующих полных прав, на сервер. Это реализуется через серверный общий модуль конфигурации.
Клиент (обычное приложение): Снят (т.е. флажок не установлен)Клиент (управляемое приложение): Снят (т.е. флажок не установлен)Сервер: Установлен (т.е. флажок установлен)Вызов сервера: Установлен (т.е. флажок установлен)Привилегированный: Установлен (т.е. флажок установлен)Привилегированный для общего модуля означает, что весь код, который выполняется внутри этого модуля, будет выполняться с полными правами, игнорируя настройки прав доступа текущего пользователя. В этом случае, явный вызов УстановитьПривилегированныйРежим(Истина) внутри функций этого модуля становится опциональным, так как модуль уже работает в привилегированном режиме.
МояПривилегированнаяЛогикаСервер:
// Модуль: МояПривилегированнаяЛогикаСервер
// Свойства модуля: Сервер=Да, ВызовСервера=Да, Привилегированный=Да
// Процедура для записи движений по МЦ.04
Процедура ЗаписатьДвиженияМЦ04(Знач ПараметрыДвижений, Знач ДокументРегистратор, Знач РаботникДт, Знач РаботникКт, Знач ОрганизацияНаименование) Экспорт
// Так как данный общий модуль помечен как "Привилегированный",
// весь его код уже выполняется с полными правами.
// Явный вызов УстановитьПривилегированныйРежим(Истина) в этом случае избыточен.
// Однако, если бы модуль не был Привилегированным, но имел свойство "Сервер" и "Вызов сервера",
// тогда вызов УстановитьПривилегированныйРежим(Истина) был бы необходим здесь.
// УстановитьПривилегированныйРежим(Истина);
Движения = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
Движения.Отбор.Регистратор.Установить(ДокументРегистратор);
Движения.ОбменДанными.Загрузка = Истина; // Рекомендуется для ускорения записи и отключения проверок
Для Каждого СтрИзПараметров Из ПараметрыДвижений Цикл
НовСтр = Движения.Добавить();
НовСтр.СчетДт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("МЦ.04");
НовСтр.СчетКт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("МЦ.04");
// Установка субконто
// Мы используем НайтиПоКоду для видов субконто, чтобы быть уверенными,
// что обращаемся к нужным видам.
// Или можно использовать НовСтр.СчетДт.ВидыСубконто[Индекс].ВидСубконто
// как в исходном коде, если порядок видов субконто гарантирован.
НовСтр.СубконтоДт.Вставить(ПланыСчетов.Хозрасчетный.МЦ04.ВидыСубконто.Номенклатура, СтрИзПараметров.Номенклатура);
НовСтр.СубконтоДт.Вставить(ПланыСчетов.Хозрасчетный.МЦ04.ВидыСубконто.Партии, СтрИзПараметров.Партии);
НовСтр.СубконтоДт.Вставить(ПланыСчетов.Хозрасчетный.МЦ04.ВидыСубконто.Сотрудники, РаботникДт);
НовСтр.СубконтоКт.Вставить(ПланыСчетов.Хозрасчетный.МЦ04.ВидыСубконто.Номенклатура, СтрИзПараметров.Номенклатура);
НовСтр.СубконтоКт.Вставить(ПланыСчетов.Хозрасчетный.МЦ04.ВидыСубконто.Партии, СтрИзПараметров.Партии);
НовСтр.СубконтоКт.Вставить(ПланыСчетов.Хозрасчетный.МЦ04.ВидыСубконто.Сотрудники, РаботникКт);
НовСтр.КоличествоДт = СтрИзПараметров.Количество;
НовСтр.КоличествоКт = СтрИзПараметров.Количество;
НовСтр.Сумма = СтрИзПараметров.Сумма;
НовСтр.Регистратор = ДокументРегистратор;
НовСтр.Период = ДокументРегистратор.Дата;
НовСтр.Организация = Справочники.Организации.НайтиПоНаименованию(ОрганизацияНаименование);
Если НовСтр.Организация = Неопределено Тогда
Сообщить("Организация с наименованием '" + ОрганизацияНаименование + "' не найдена. Движения не будут записаны.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
КонецЦикла;
Попытка
Движения.Записать();
// УстановитьПривилегированныйРежим(Ложь); // Также избыточно, если модуль Привилегированный
Исключение
Сообщить("Ошибка при записи движений в привилегированном режиме: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
// Пример из сообщения 13: получение сведений об организации
// Эта функция также может быть размещена в привилегированном серверном модуле.
Функция ПолучитьСведенияОбОрганизацииПривилегированно(Знач Организация, Знач НаДату, Знач СтрП) Экспорт
// Если модуль, в котором находится эта функция, помечен как "Привилегированный",
// то вызовы УстановитьПривилегированныйРежим(Истина/Ложь) внутри него становятся излишними,
// так как весь код модуля уже выполняется с полными правами.
// УстановитьПривилегированныйРежим(Истина);
// Пример обращения к серверной функции, которая также может требовать привилегий.
// Если "РегламентированнаяОтчетностьВызовСервера" - это другой серверный модуль,
// и он не помечен как привилегированный, то УстановитьПривилегированныйРежим(Истина)
// будет иметь смысл перед вызовом его функций, чтобы их выполнение также шло с полными правами.
ОргСведения = РегламентированнаяОтчетностьВызовСервера.ПолучитьСведенияОбОрганизации(Организация, НаДату, СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(СтрП, "*"));
// УстановитьПривилегированныйРежим(Ложь);
Возврат ОргСведения;
КонецФункции
Номенклатуры, ссылки на ДокументРегистратор, Работников и Организацию) в качестве параметров в серверную процедуру. На сервере эти данные будут использоваться для создания и записи движений. Мы также добавили базовую обработку ошибок, чтобы в случае проблем можно было получить информацию о них.
Пример кода в модуле формы внешней обработки:
// Модуль формы внешней обработки
Процедура КнопкаВыполнитьНажатие(Кнопка)
// УстановитьПривилегированныйРежим(Истина); // Здесь это не сработает, так как это клиентский контекст!
// Проверка заполненности необходимых полей на клиенте
Если Документ = Неопределено Тогда
Сообщить("Не выбран документ-основание для регистрации.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
Если РаботникДт = Неопределено ИЛИ РаботникКт = Неопределено Тогда
Сообщить("Не указаны работники (материально-ответственные лица).", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
Если Номенклатура.Количество() = 0 Тогда
Сообщить("Табличная часть 'Номенклатура' для перемещения не заполнена.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Собираем параметры для передачи на сервер в виде ТаблицыЗначений.
// Важно: на сервер передаются копии значений, а не ссылки на элементы формы.
ПараметрыДляСервера = Новый ТаблицаЗначений;
ПараметрыДляСервера.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ПараметрыДляСервера.Колонки.Добавить("Партии", Новый ОписаниеТипов("СправочникСсылка.Партии")); // Уточните тип вашей партии
ПараметрыДляСервера.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", ,Новый КвалификаторыЧисла(15, 3)));
ПараметрыДляСервера.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", ,Новый КвалификаторыЧисла(15, 2)));
Для Каждого СтрИзТЧ Из Номенклатура Цикл
НоваяСтрока = ПараметрыДляСервера.Добавить();
НоваяСтрока.Номенклатура = СтрИзТЧ.Номенклатура;
НоваяСтрока.Партии = СтрИзТЧ.Партии;
НоваяСтрока.Количество = СтрИзТЧ.Количество;
НоваяСтрока.Сумма = СтрИзТЧ.Сумма;
КонецЦикла;
// Наименование организации для поиска на сервере
ОрганизацияНаименование = "Общество с ограниченной ответств ""Маш"""; // Или берем из реквизита формы
// Или, что лучше, передавать ссылку на объект Организация, если она уже есть на форме
// ОрганизацияСсылка = Справочники.Организации.НайтиПоНаименованию(ОрганизацияНаименование);
Попытка
// Вызываем серверную процедуру из общего модуля.
// Имя общего модуля и процедуры используются напрямую.
МояПривилегированнаяЛогикаСервер.ЗаписатьДвиженияМЦ04(ПараметрыДляСервера, Документ, РаботникДт, РаботникКт, ОрганизацияНаименование);
Сообщить("Движения успешно записаны в учетную систему.", СтатусСообщения.Информация);
Исключение
Сообщить("Произошла ошибка при попытке записи движений: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
КонецПопытки;
КонецПроцедуры
Таким образом, мы инициируем процесс на клиенте, собираем и подготавливаем данные, а затем передаем их на сервер для выполнения самой критичной операции – записи в регистры, где благодаря привилегированному общему модулю уже действует привилегированный режим.
Чтобы ваша работа с привилегированным режимом была максимально эффективной и безопасной, учтем следующие моменты:
УстановитьПривилегированныйРежим (если вызывается явно) работает только в серверном контексте. В файловой базе это может быть несколько менее очевидно, так как часть кода выполняется на том же компьютере, что и клиент, но для платформы все равно существует четкое разделение на клиентский и серверный контексты выполнения. Поэтому описанное архитектурное решение с серверным общим модулем применимо в обоих случаях.
УстановитьПривилегированныйРежим(Истина) внутри не-привилегированного серверного модуля (например, для выполнения только части кода с привилегиями), всегда вызывайте УстановитьПривилегированныйРежим(Ложь) после завершения операций, требующих полных прав. Это принципиально важная практика безопасности, которая минимизирует время работы в режиме с неограниченными правами и снижает потенциальные риски.
Аналогичная проблема может возникать и с внешними печатными формами, как было показано в сообщении 13. Если печатная форма пытается получить данные (например, о руководителе или главном бухгалтере) из справочника, на который у текущего пользователя нет прав, решение будет тем же: вынести логику получения таких данных в привилегированный серверный общий модуль. Обращение к УстановитьПривилегированныйРежим(Истина) внутри такого модуля может быть избыточным, если сам модуль уже помечен как "Привилегированный", но не повредит, если вы хотите явно контролировать границы действия привилегий.
Правильная установка привилегированного режима в 1С:Предприятии для внешних обработок требует четкого понимания клиент-серверной архитектуры и контекстов выполнения кода. Отключив безопасный режим для обработки и перенеся операции, требующие повышенных прав, в привилегированный серверный общий модуль, мы гарантируем успешное выполнение задач, которые иначе были бы заблокированы из-за недостатка прав.
Помните, что безопасность превыше всего. Используйте привилегированный режим осознанно и только для тех операций, которые действительно его требуют, минимизируя область его действия. Это не только поможет избежать ошибок "недостаточно прав", но и обеспечит стабильную и безопасную работу вашей информационной системы.