Как ограничить права доступа к одному документу и его отдельным реквизитам в 1С:Бухгалтерия 3.0?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет
← На главную

При работе с системой 1С:Предприятие 3.0 часто возникает необходимость тонкой настройки прав доступа, выходящей за рамки стандартных ролей. Рассмотрим задачу, когда пользователю требуется предоставить доступ только к одному конкретному документу, например, «Поступление товаров и услуг», и при этом разрешить изменение лишь определенных его реквизитов, таких как флаг «Оригинал получен» и поле «Комментарий», запретив изменение всего остального. Выясним, как добиться такого детального ограничения прав, используя современные подходы и методы разработки в 1С.

Почему типовые средства не подходят для такой детализации?

Прежде чем перейти к решению, проанализируем, почему стандартные инструменты 1С:Бухгалтерии 3.0 не позволяют реализовать столь узкие требования к правам доступа:

  1. Настройка на уровне видов объектов: В типовых конфигурациях 1С права доступа к документам устанавливаются на уровне вида документа в целом. Это означает, что если мы даем пользователю доступ к документу «Поступление товаров и услуг», он получает его ко всем экземплярам этого вида документа и ко всем его реквизитам (в рамках предоставленных прав на Чтение, Изменение, Добавление). Типовые роли, такие как «Добавление и изменение данных бухгалтерии», предоставляют широкий доступ, что не соответствует нашей задаче.
  2. Ограничение доступа к отдельным экземплярам документа: Стандартные механизмы групп доступа и профилей позволяют настраивать права на объекты метаданных (справочники, документы, отчеты) в целом, но не на конкретный экземпляр документа. То есть, мы не можем средствами интерфейса или стандартными ролями сказать «этот пользователь видит только документ №5 от 01.01.2024, а остальные – нет».
  3. Ограничение доступа к реквизитам: Типовые роли не позволяют разграничить права на отдельные реквизиты документа. Если пользователь имеет право на изменение документа, он может изменять все реквизиты, доступные для редактирования. Невозможно указать, что можно менять только «Комментарий», а остальные поля, например, «Сумма документа» или «Номенклатура», должны быть заблокированы.
  4. Ограничение на уровне записей (RLS): Механизм ограничения доступа на уровне записей (Row-Level Security, RLS) позволяет ограничивать доступ к данным на основе определенных условий (например, по организации, подразделению, ответственному). Теоретически, можно было бы попробовать настроить RLS так, чтобы конкретный пользователь видел только определенный документ. Однако для этого документ должен содержать реквизит, по которому мы могли бы однозначно идентифицировать «разрешенный» документ для этого пользователя. Настройка RLS требует изменения конфигурации (добавления шаблонов ограничений в роли или создания новых ролей с такими шаблонами) и может быть достаточно сложной, особенно если речь идет о единичном документе без явного признака. При этом RLS обычно используется для массовых ограничений, а не для одного-единственного документа. Важно отметить, что RLS не предназначен для разрешения изменения определенных реквизитов и запрета других – он контролирует видимость и доступность записи целиком.

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

Решение проблемы: Использование расширений и программной доработки

Наиболее гибкий и рекомендуемый способ решения этой задачи в 1С:Предприятие 3.0 – это создание нового расширения с собственной ролью и программными доработками.

Шаг 1: Создание расширения

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

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

Шаг 2: Создание новой роли в расширении

Для нашего пользователя мы создадим специальную роль, которая будет обладать минимально необходимыми правами. Это позволит нам контролировать доступ к документу на самом базовом уровне.

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

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

Шаг 3: Программное ограничение доступа к конкретному документу

Чтобы пользователь мог видеть только один конкретный документ, нам потребуется программно фильтровать доступ. Это можно сделать несколькими способами:

  1. Модификация формы списка документа:

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

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

    Важно: Метод Пользователи.РольДоступна() требует, чтобы функция РольДоступна была доступна на клиенте. Возможно, потребуется перенести проверку роли на сервер или использовать параметр сеанса. Убедитесь, что пользователь не может создать новый документ, если это запрещено.

  2. Перехват открытия документов:

    Можно использовать подписку на событие ОбработкаПолученияФормы для документа «Поступление товаров и услуг». В этой подписке мы можем проверить, какая ссылка передается для открытия формы. Если это не разрешенный документ для нашего пользователя с особой ролью, мы можем отменить открытие или перенаправить на сообщение об ошибке.

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

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

Шаг 4: Программное ограничение доступа к реквизитам документа

Это самый важный этап, где мы будем контролировать, какие реквизиты пользователь может изменять, а какие нет. Для этого нам потребуется заместить форму документа «Поступление товаров и услуг» в расширении и добавить код в её модуль.

  1. Замещение формы документа:

    В расширении найдите документ «Поступление товаров и услуг». Щелкните по нему правой кнопкой мыши и выберите «Добавить в расширение». Затем перейдите к его формам и для основной формы документа (например, ФормаДокумента) выберите «Добавить в расширение с замещением». Это позволит нам модифицировать её поведение.

  2. Модификация модуля формы документа:

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

    Пример кода в процедуре ПриСозданииНаСервере:

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

    Примечание по Пользователи.РольДоступна(): Эта функция по умолчанию доступна на сервере. Если вам нужно выполнить проверку на клиенте, вызовите её через серверный вызов или установите параметр сеанса при входе пользователя.

    Пример кода в процедуре ПередЗаписьюНаСервере:

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

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

    Важно: Функция СравнитьЗначенияОбъектов – это пример. Для полноценного сравнения объектов, особенно с табличными частями, потребуется более сложная и универсальная реализация, которая учитывает все типы реквизитов и вложенные структуры.

  3. Условное оформление формы:

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

    1. Откройте замещенную форму в конфигураторе.
    2. Перейдите в «Условное оформление» (кнопка с изображением карандаша на панели формы).
    3. Добавьте новый элемент условного оформления.
    4. В поле «Условие» укажите проверку роли, например: ТекущийПользователь.РольДоступна("ДоступКОдномуПоступлению") (эта функция должна быть доступна на клиенте или использоваться с параметром сеанса).
    5. В поле «Оформление» выберите «Только просмотр» и установите его в «Истина».
    6. В поле «Элементы» выберите все элементы, которые нужно заблокировать, кроме «ОригиналПолучен» и «Комментарий».

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

Шаг 5: Дополнительные настройки и проверка

  1. Тестирование:

    После всех настроек обязательно тщательно протестируйте поведение системы под новым пользователем. Убедитесь, что он:

    • Видит только разрешенный документ в списке.
    • Не может создавать новые документы.
    • Может открыть разрешенный документ.
    • Может изменять только поля «Оригинал получен» и «Комментарий».
    • Не может изменять другие поля.
    • Не может сохранить документ, если были изменены неразрешенные поля.
  2. Обработка исключений:

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

Заключение

Решение задачи по ограничению прав доступа для одного документа и его реквизитов в 1С:Бухгалтерия 3.0 требует комплексного подхода с использованием расширений. Мы рассмотрели, как создать новую роль, заместить формы документов и добавить программный код для контроля доступа как на клиентской, так и на серверной стороне. Этот подход позволяет достичь необходимой гибкости в управлении правами, сохраняя при этом основную конфигурацию на поддержке, что является лучшей практикой в современных версиях 1С.

Помните, что каждый случай индивидуален, и предоставленные примеры кода могут потребовать адаптации под конкретные особенности вашей конфигурации и бизнес-процессов.

← На главную