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