В процессе разработки расширений для типовых конфигураций 1С часто возникает задача ограничить доступ к определенным документам на основании значений их реквизитов, предварительно выполнив просмотр ролей для объекта. Например, если в документе "Счет на оплату" установлен флаг "Счет закрыт", рядовые пользователи не должны иметь возможности редактировать или даже открывать этот документ, в то время как менеджеры с особыми правами должны сохранять полный доступ.
При реализации такой логики начинающие разработчики часто сталкиваются со специфической ошибкой платформы: "Запрещено изменение параметра, отменяющего действие". Давайте разберем, почему она возникает, как правильно использовать события формы в расширениях и как корректно настроить права доступа.
Предположим, мы добавили в расширение формы документа новый реквизит (галочку) СчетЗакрыт. Наша цель — реализовать следующую логику при открытии формы:
СчетЗакрыт.ДоступМенеджер.Рассмотрим код, который часто пытаются использовать разработчики и который приводит к ошибке. В расширении создается обработчик события ПриОткрытии с аннотацией &После.
&НаКлиенте
Процедура Расш1_ПриОткрытииПосле(Отказ)
Если Объект.СчетЗакрыт И НЕ ЕстьПраво() Тогда
Отказ = Истина; // ОШИБКА ЗДЕСЬ
КонецЕсли;
КонецПроцедуры
При попытке открыть документ система выдаст ошибку: "Запрещено изменение параметра, отменяющего действие (параметр номер 1 в процедуре)".
Давайте разберемся в причинах. Жизненный цикл событий в расширениях 1С имеет строгий порядок:
&Перед.&После.Параметр Отказ предназначен для того, чтобы прервать действие (в данном случае — открытие формы). Если событие уже свершилось (то есть мы находимся в обработчике &После), отменять его поздно. Форма фактически уже создана и открывается. Платформа блокирует изменение параметра Отказ в пост-обработчиках, чтобы избежать логических противоречий.
Чтобы иметь возможность запретить открытие формы, мы должны перехватить управление до того, как форма окончательно откроется. Для этого необходимо использовать аннотацию &Перед.
Давайте исправим код. Нам понадобится серверная функция для проверки прав и клиентская процедура для управления открытием.
&НаСервере
Функция ЕстьПравоНаРедактирование()
// Проверяем наличие специальной роли
Возврат РольДоступна("ДоступМенеджер");
КонецФункции
&НаКлиенте
Процедура Расш1_ПриОткрытииПеред(Отказ, СтандартнаяОбработка)
// Если счет закрыт и у пользователя нет прав - запрещаем открытие
Если Объект.СчетЗакрыт И НЕ ЕстьПравоНаРедактирование() Тогда
// Сообщаем пользователю причину
ПоказатьПредупреждение(, "Доступ к закрытому счету запрещен!", 10);
// Отменяем открытие формы
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
В этом случае мы вмешиваемся в процесс до его завершения, и изменение параметра Отказ отработает корректно.
Часто полный запрет на открытие документа является слишком жесткой мерой. Пользователю может потребоваться посмотреть содержимое счета, даже если он не может его менять. В этом случае вместо блокировки открытия (Отказ = Истина) лучше перевести форму в режим "Только просмотр".
Важно отметить: свойство ЭтаФорма.Доступность = Ложь делает форму полностью "мертвой" — нельзя даже скопировать текст или прокрутить список. Правильнее использовать свойство ТолькоПросмотр.
Посмотрим, как это реализовать. В данном случае лучше использовать событие ПриСозданииНаСервере, так как оно выполняется один раз при инициализации формы и не требует лишних серверных вызовов с клиента.
&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Проверяем условие блокировки
СчетЗакрыт = Объект.СчетЗакрыт; // Получаем значение реквизита
ЕстьПрава = РольДоступна("ДоступМенеджер");
Если СчетЗакрыт И НЕ ЕстьПрава Тогда
// Устанавливаем режим "Только чтение" для всей формы
ЭтаФорма.ТолькоПросмотр = Истина;
КонецЕсли;
// Дополнительная логика для блокировки самой галочки "СчетЗакрыт"
// Чтобы даже менеджер не мог случайно снять её, или наоборот - разрешить только ему.
// Если у пользователя нет прав менеджера, он вообще не должен менять этот статус.
Элементы.СчетЗакрыт.ТолькоПросмотр = НЕ ЕстьПрава;
КонецПроцедуры
В обсуждении проблемы автор столкнулся с ситуацией, когда элемент формы оставался недоступным даже для пользователя с полными правами. Это часто происходит из-за логических ошибок в условиях.
Рассмотрим пример управления конкретным элементом формы СчетЗакрыт. Допустим, мы хотим, чтобы ставить и снимать эту галочку могли только менеджеры, независимо от текущего состояния документа.
// Плохой пример (логическая ошибка)
Если Объект.СчетЗакрыт = Истина Тогда
Элементы.СчетЗакрыт.Доступность = ЕстьПраво;
КонецЕсли;
// Здесь, если галочка снята (Ложь), код не выполнится,
// и доступность останется в значении по умолчанию (или как настроено в конфигураторе).
Правильный подход — устанавливать свойство безусловно, опираясь только на права:
// Правильный пример
// Элемент доступен только если есть право, значение самой галочки не важно
Элементы.СчетЗакрыт.Доступность = ЕстьПравоНаРедактирование();
Хотя управление формой (видимость, доступность, отказ при открытии) решает задачу визуально, это не обеспечивает 100% защиты данных. Продвинутый пользователь может открыть список документов, воспользоваться групповой обработкой или отчетом и увидеть/изменить данные.
Для надежного ограничения доступа в 1С используется механизм RLS (Record Level Security) — ограничение доступа на уровне записей.
Если задача стоит именно в жесткой безопасности (никто кроме менеджера не должен видеть суммы закрытых счетов), следует настроить RLS в роли пользователя:
Примерная логика шаблона ограничения:
ГДЕ (СчетЗакрыт = ЛОЖЬ) ИЛИ (СчетЗакрыт = ИСТИНА И &ТекущийПользователь В (ВЫБРАТЬ ... ИЗ Менеджеры ...))
Однако настройка RLS требует хорошего знания языка запросов и понимания производительности системы. Для простых задач, таких как «защита от случайного редактирования», метод с ТолькоПросмотр в форме является вполне приемлемым и простым в реализации.
Для решения задачи автора необходимо выполнить следующие действия:
ПриОткрытииПосле в ПриОткрытииПеред (если нужно запретить открытие) или в ПриСозданииНаСервереПосле (если нужно включить режим "Только просмотр").ТолькоПросмотр вместо Доступность для всей формы, чтобы сохранить интерактивность интерфейса.