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