Как настроить права на изменение табличной части без прав на изменение самого объекта в 1С?

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

В процессе разработки и настройки прав доступа в системе «1С:Предприятие» программисты часто сталкиваются с задачей: нужно разрешить пользователю редактировать только табличную часть документа (или справочника), но при этом запретить изменение реквизитов шапки (самого объекта). На первый взгляд кажется, что для этого достаточно настроить роли, однако стандартный механизм прав выдает ошибку или блокирует элементы интерфейса (например, кнопку выбора/открытия в полях табличной части).

В этой статье мы подробно разберем, почему платформа накладывает такое ограничение на уровне архитектуры, как на самом деле работает флаг «Независимые права подчиненных объектов» и какие существуют практические способы решения этой задачи.

Почему платформа 1С блокирует запись табличной части без прав на объект?

Выясним причину такого поведения системы. В реляционных СУБД (будь то MS SQL Server, PostgreSQL или IBM DB2) табличная часть документа или справочника физически хранится в виде отдельной таблицы, которая связана с основной таблицей объекта по уникальному идентификатору (ссылке).

Однако в объектной модели «1С:Предприятие» табличная часть не является самостоятельным объектом базы данных. Она не может существовать и быть записана в базу в отрыве от своего владельца. Когда пользователь или программный код вносит изменения в строки табличной части и пытается их сохранить, платформа всегда выполняет транзакционную запись всего объекта целиком через вызов метода Записать().

В этот момент происходят следующие процессы:

  1. Блокируется основная таблица объекта в СУБД.
  2. Пересчитывается и обновляется версия объекта для контроля объектной целостности.
  3. Последовательно вызываются обработчики событий ПередЗаписью и ПриЗаписи в модуле самого объекта.
  4. Платформа транзакционно перезаписывает как строки табличных частей, так и поля самого объекта.

Поскольку физически выполняется полная запись основного объекта, СУБД и внутренний механизм безопасности платформы требуют обязательного наличия права «Изменение» (Update) на сам объект-родитель. Если у роли пользователя этого права нет, транзакция записи будет отклонена еще до попытки модификации табличной части.

Как работает флаг «Независимые права подчиненных объектов»?

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

Таким образом, настроить такое разграничение прав исключительно галочками в дереве метаданных роли невозможно. Рассмотрим практические варианты решения этой проблемы.

Вариант А. Программное ограничение формы + Контроль транзакции (Рекомендуемый способ)

Разберем по шагам наиболее популярный и надежный способ реализации. Суть его заключается в том, что мы даем пользователю полноценную роль с правами на «Изменение» самого объекта, но программно пресекаем любые попытки изменить реквизиты шапки как на уровне интерфейса, так и при записи.

Шаг 1. Ограничение доступности элементов на форме.

В модуле формы документа (или справочника) в обработчике события ПриСозданииНаСервере мы анализируем роли пользователя и принудительно отключаем доступность реквизитов шапки:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем, относится ли пользователь к категории с ограниченными правами
    Если РольДоступна("РедактированиеТолькоТабличнойЧасти") Тогда
        
        // Блокируем изменение реквизитов шапки на форме
        Элементы.Организация.Доступность = Ложь;
        Элементы.Контрагент.Доступность = Ложь;
        Элементы.Договор.Доступность = Ложь;
        Элементы.Дата.Доступность = Ложь;
        
        // При этом сама табличная часть остается полностью доступной для редактирования
        Элементы.Товары.ТолькоПросмотр = Ложь;
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 2. Контроль на уровне базы данных (Защита от обхода формы).

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


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

Этот подход гарантирует 100% безопасность данных, при этом табличная часть остается полностью интерактивной для пользователя.

Вариант Б. Перенос данных в независимый Регистр сведений

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

Решение:

  1. Создадим независимый Регистр сведений (например, СведенияОСборкеЗаказов).
  2. В качестве измерений укажем ДокументСсылка (тип: Ссылка на наш документ) и КлючСтроки (числовой идентификатор строки).
  3. В качестве ресурсов добавим те поля, которые должен заполнять пользователь (например, КоличествоСобрано, СтатусСтроки, Исполнитель).
  4. Настроим роль пользователя: дадим право только на «Чтение» документа-владельца, но полные права («Чтение» и «Запись») на созданный Регистр сведений.
  5. На форму документа выведем динамический список или таблицу значений, связанную с этим регистром сведений.

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

Вариант В. Использование привилегированного режима (для разовых правок)

Посмотрим на пример, когда пользователю нужно лишь изредка вносить изменения в табличную часть закрытого для него документа (например, ставить галочку «Проверено аудитором» или изменять статус конкретной строки).

В этом случае мы оставляем документ полностью в режиме «Только чтение» для пользователя на уровне ролей. На форму документа выводим специальную команду (кнопку) «Обновить строки».

При нажатии на эту кнопку управление передается на сервер в модуль, где включен флаг Привилегированный (или вызывается метод УстановитьПривилегированныйРежим(Истина)):


&НаСервереБезКонтекста
Процедура ВнестиИзмененияПривилегированно(СсылкаНаДокумент, НомерСтроки, НовыйСтатус)
    
    // Включаем привилегированный режим, игнорируя права текущего пользователя
    УстановитьПривилегированныйРежим(Истина);
    
    ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    
    // Находим нужную строку и вносим изменения
    Если ДокументОбъект.Товары.Количество() >= НомерСтроки Тогда
        СтрокаТЧ = ДокументОбъект.Товары[НомерСтроки - 1];
        СтрокаТЧ.Статус = НовыйСтатус;
        
        // Записываем объект без проверки прав текущего сеанса
        ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
    КонецЕсли;
    
КонецПроцедуры

Такой подход позволяет точечно управлять записью данных, сохраняя строгие ограничения на весь остальной объект.

Подводя итоги, отметим: выбор конкретного варианта зависит от сложности вашей конфигурации и требований к производительности. Для большинства стандартных задач идеально подходит Вариант А, так как он сохраняет привычную пользователю работу в форме документа без кардинальной перестройки структуры метаданных.

← На главную