Как сделать дополнительные реквизиты доступными для изменения только определенным пользователям?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с типовыми конфигурациями 1С, построенными на базе Библиотеки стандартных подсистем (БСП), разработчики часто сталкиваются с необходимостью тонкой настройки прав доступа. Одна из классических задач — ограничить возможность редактирования конкретного дополнительного реквизита (свойства) для определенных пользователей или ролей, при этом оставив его доступным для просмотра. И все это нужно сделать без изменения типовой конфигурации, используя механизм расширений.

В этой статье мы подробно разберем, почему возникает сложность с поиском автосоздаваемых элементов на форме, как правильно идентифицировать нужный дополнительный реквизит программно и как обеспечить надежную защиту данных не только на уровне интерфейса (клиент), но и на уровне бизнес-логики (сервер).


В чем сложность: архитектура динамических свойств БСП

Дополнительные реквизиты и сведения не создаются в конфигураторе как постоянные реквизиты объекта. Они хранятся в табличной части ДополнительныеРеквизиты справочников или документов, а на форму выводятся динамически в процессе ее создания на сервере.

Для каждого дополнительного реквизита платформа генерирует элемент формы (обычно поле ввода или флажок) с уникальным синтетическим именем. Это имя выглядит примерно так: ДополнительныйРеквизитЗначение_73DC2B5Dx188Cx11E9x81E7x001E67A34F7F_E0FA67CDx93EAx11E9x81F4x001E67A34F7F. В этой строке закодированы внутренние идентификаторы (GUID) самого свойства и набора свойств.

Пытаться обращаться к элементам формы напрямую по таким именам в коде расширения — грубейшая ошибка. Синтетическое имя элемента может измениться при:

Нам необходим надежный и универсальный способ связи между реальным элементом ПВХ ДополнительныеРеквизитыИСведения и динамическим элементом управляемой формы.


Шаг 1. Поиск элемента на форме через служебную таблицу БСП

При инициализации формы БСП программно создает специальный служебный реквизит формы — таблицу значений под названием Свойства_ОписаниеДополнительныхРеквизитов (в некоторых старых версиях БСП это может быть список значений, но структура поиска аналогична). Эта таблица является своеобразным мостом между метаданными свойств и элементами интерфейса.

Давайте проанализируем структуру этой таблицы. В ней содержатся две ключевые колонки, которые нам интересны:

  1. Свойство — содержит ссылку на элемент плана видов характеристик ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.
  2. ИмяРеквизитаЗначение — строковое имя того самого синтетического элемента формы, который создала платформа для отображения значения этого свойства.

Зная это, мы можем легко перехватить процедуру создания формы в расширении, найти нужное свойство и заблокировать соответствующий ему элемент формы.

Создадим расширение для формы документа (или справочника), в котором отображается наш реквизит. Нам потребуется перехватить процедуру ПриСозданииНаСервере с помощью аннотации &После.

Рассмотрим пример кода, реализующий эту логику:


&После("ПриСозданииНаСервере")
Процедура Расш_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Шаг 1. Безопасный поиск свойства в ПВХ.
    // Рекомендуется искать по "Имени для разработчиков" (свойство "Имя" в ПВХ), 
    // чтобы избежать привязки к текстовому наименованию, которое пользователи могут изменить.
    ИмяДляРазработчиков = "ПолеОсобогоКонтроля";
    СсылкаНаСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", ИмяДляРазработчиков);
    
    Если СсылкаНаСвойство.Пустая() Тогда
        // Если имя для разработчиков не заполнено, можно выполнить поиск по синониму/наименованию
        СсылкаНаСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Особый контроль");
    КонецЕсли;
    
    // Если свойство не найдено в базе данных, прекращаем выполнение, чтобы избежать ошибок
    Если СсылкаНаСвойство.Пустая() Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Поиск соответствия в служебной таблице формы БСП
    ПараметрыПоиска = Новый Структура("Свойство", СсылкаНаСвойство);
    НайденныеСтроки = Свойства_ОписаниеДополнительныхРеквизитов.НайтиСтроки(ПараметрыПоиска);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        СтрокаСвойства = НайденныеСтроки[0];
        ИмяЭлементаФормы = СтрокаСвойства.ИмяРеквизитаЗначение;
        
        // Шаг 3. Проверка прав текущего пользователя.
        // Ограничиваем доступ, если у пользователя нет специализированной роли.
        Если НЕ РольДоступна("АдминистраторСистемы") И НЕ РольДоступна("ПравоИзмененияОсобыхРеквизитов") Тогда
            
            // Проверяем существование элемента на форме на всякий случай
            ЭлементФормы = Элементы.Найти(ИмяЭлементаФормы);
            Если ЭлементФормы <> Неопределено Тогда
                ЭлементФормы.ТолькоПросмотр = Истина;
            КонецЕсли;
            
        КонецЕсли;
    КонецЕсли;

КонецПроцедуры

Шаг 2. Нюанс с динамическим обновлением зависимостей в БСП

Если вы ограничите доступность элемента только в процедуре ПриСозданииНаСервере, вы можете столкнуться с неприятным эффектом. В БСП встроен механизм зависимостей дополнительных реквизитов. Если для свойств настроены условия видимости, доступности или обязательности, система периодически вызывает процедуру ОбновитьЗависимостиДополнительныхРеквизитов на клиенте или сервере при изменении других реквизитов объекта.

При этом вызове БСП может проанализировать свои стандартные настройки и принудительно вернуть свойство ТолькоПросмотр или Доступность элемента формы в состояние Ложь, проигнорировав наш код в ПриСозданииНаСервере.

Чтобы обойти эту проблему, у нас есть два пути:

  1. Интеграция в механизм обновления зависимостей: Перехватить в расширении общий модуль БСП, отвечающий за управление свойствами (например, УправлениеСвойствамиПереопределяемый или УправлениеСвойствамиКлиентСервер), и внедрить проверку прав туда.
  2. Повторная установка свойства при изменении данных формы: Использовать обработчик ожидания или перехватывать события изменения ключевых полей формы, чтобы гарантированно удерживать свойство ТолькоПросмотр в значении Истина для заблокированных элементов.

Наиболее чистым решением является перехват процедуры управления доступностью в общем модуле БСП, но если требования не слишком жесткие, достаточно продублировать установку свойства ТолькоПросмотр при чтении объекта на сервере (в обработчике ПриЧтенииНаСервере).


Шаг 3. Гарантированная защита данных на уровне сервера (Важно!)

Помните золотое правило разработки в 1С: ограничение видимости или доступности элементов на форме — это не защита данных, а лишь элемент удобства интерфейса (UX).

Пользователь с минимальными техническими навыками может легко обойти блокировку на форме. Например, он может:

Чтобы гарантировать безопасность данных, мы обязаны продублировать проверку прав на сервере непосредственно в момент записи объекта базы данных. Для этого мы создадим расширение для модуля самого документа (или справочника) и перехватим событие ПередЗаписью. Для настройки подобного контроля без написания кода можно использовать расширение для автоматической блокировки записи объектов по условию.

Рассмотрим алгоритм проверки при записи:

  1. Проверим, не выполняется ли загрузка данных (обмен данными). В этом случае проверки обычно отключаются.
  2. Если это новый объект, то старого значения в базе еще нет. Проверяем только заполнение, если это необходимо.
  3. Если объект уже существовал, делаем запрос к базе данных, чтобы получить старое значение дополнительного реквизита, сохраненное в БД.
  4. Находим новое значение этого реквизита в табличной части записываемого объекта.
  5. Сравниваем старое и новое значения. Если они различаются, а у текущего пользователя нет необходимых прав (ролей) — отменяем транзакцию записи и выводим понятное сообщение об ошибке.

Реализуем этот механизм в модуле объекта с помощью расширения:


&Перед("ПередЗаписью")
Процедура Расш_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Если идет загрузка данных (например, обмен через РИБ или конвертацию), проверку не выполняем
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Если объект новый, то изменения существующего значения быть не могло
    Если ЭтоНовый() Тогда
        Возврат;
    КонецЕсли;
    
    // Определяем целевое свойство
    ИмяДляРазработчиков = "ПолеОсобогоКонтроля";
    СсылкаНаСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", ИмяДляРазработчиков);
    
    Если СсылкаНаСвойство.Пустая() Тогда
        СсылкаНаСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Особый контроль");
    КонецЕсли;
    
    Если СсылкаНаСвойство.Пустая() Тогда
        Возврат; // Свойство не настроено в системе
    КонецЕсли;
    
    // 1. Получаем старое значение из базы данных с помощью запроса
    СтароеЗначение = Неопределено;
    
    Запрос = Новый Запрос;
    // Внимание: замените "Документ.ИмяВашегоДокумента" на реальное имя вашего объекта метаданных
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ДопРеквизиты.Значение КАК Значение
        |ИЗ
        |	Документ.ИмяВашегоДокумента.ДополнительныеРеквизиты КАК ДопРеквизиты
        |ГДЕ
        |	ДопРеквизиты.Ссылка = &Ссылка
        |	И ДопРеквизиты.Свойство = &Свойство";
        
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    Запрос.УстановитьПараметр("Свойство", СсылкаНаСвойство);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующая() Тогда
        СтароеЗначение = Выборка.Значение;
    КонецЕсли;
    
    // 2. Получаем текущее (новое) значение из табличной части объекта, подготовленного к записи
    СтрокаДопРеквизита = ДополнительныеРеквизиты.Найти(СсылкаНаСвойство, "Свойство");
    НовоеЗначение = Неопределено;
    Если СтрокаДопРеквизита <> Неопределено Тогда
        НовоеЗначение = СтрокаДопРеквизита.Значение;
    КонецЕсли;
    
    // 3. Сравниваем старое и новое значения
    Если СтароеЗначение <> НовоеЗначение Тогда
        
        // Проверяем наличие роли на редактирование
        Если НЕ РольДоступна("АдминистраторСистемы") И НЕ РольДоступна("ПравоИзмененияОсобыхРеквизитов") Тогда
            
            // Сообщаем пользователю о нарушении прав доступа
            ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
                НСтр("ru = 'У вас нет прав на изменение дополнительного реквизита ""Особый контроль""!'"),
                ЭтотОбъект,
                ,
                ,
                Отказ
            );
            
            Отказ = Истина; // Блокируем запись объекта в базу данных
            
        КонецЕсли;
        
    КонецЕсли;
    
EndProcedures

Подведем итоги

Для реализации качественного разграничения прав доступа к дополнительным реквизитам БСП мы использовали двухконтурную систему защиты, которая считается стандартом разработки на платформе 1С:Предприятие 8.3:

  1. Интерфейсный уровень (Клиент/Сервер формы): Мы использовали служебную таблицу БСП Свойства_ОписаниеДополнительныхРеквизитов, чтобы динамически определить имя элемента формы и установить для него свойство ТолькоПросмотр = Истина. Это скрывает возможность редактирования от пользователя на уровне интерфейса и предотвращает случайные ошибки ввода.
  2. Данные и бизнес-логика (Сервер БД): Мы перехватили транзакцию записи в модуле объекта через обработчик ПередЗаписью, выполнили запрос к базе данных и сравнили старую версию реквизита с новой. Это гарантирует, что данные не будут изменены в обход формы (например, через групповые обработки или программный код внешних модулей).

Такой подход обеспечивает 100% надежность решения, стабильно работает при обновлениях конфигурации и легко переносится между базами с помощью расширений.

← На главную