В процессе разработки и сопровождения конфигураций на платформе 1С:Предприятие 8 программисты часто сталкиваются с задачей ограничить изменение конкретных полей в документах или справочниках. Например, нам нужно запретить пользователям редактировать поле Вид оплаты в документе оплаты, чтобы избежать ошибок при синхронизации данных или закрытии отчетных периодов.
Решение этой задачи может показаться тривиальным: достаточно установить нужное свойство на форме. Однако на практике разработчики сталкиваются с тем, что пользователи находят обходные пути — используют групповое изменение реквизитов, обработку «Изменить выделенные» в списках или программные обработки. В этой статье мы подробно рассмотрим все возможные способы решения этой задачи: от простых визуальных блокировок до гарантированной защиты данных на уровне сервера.
Самый простой и быстрый способ закрыть поле от редактирования — изменить свойства элемента формы. Для этого мы можем использовать два основных свойства: Доступность (Enabled) и ТолькоПросмотр (ReadOnly).
Давайте разберем по шагам, чем отличаются эти свойства и как их правильно применять:
Рассмотрим, как программно установить эти свойства при открытии формы документа. Для этого откроем модуль формы и в обработчике события ПриСозданииНаСервере напишем следующий код:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Вариант 1: Полная блокировка доступности поля "Вид оплаты"
Элементы.ВидОплаты.Доступность = Ложь;
// Вариант 2: Перевод поля в режим "Только просмотр" (рекомендуется)
Элементы.ВидОплаты.ТолькоПросмотр = Истина;
КонецПроцедуры
Мы также можем накладывать эти ограничения по условию. Например, запретим редактировать поле только в том случае, если документ уже проведен или если текущий пользователь не обладает правами администратора:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Если документ проведен, запрещаем изменять вид оплаты всем, кроме администраторов
Если Объект.Проведен И Не РольДоступна("ПолныеПрава") Тогда
Элементы.ВидОплаты.ТолькоПросмотр = Истина;
КонецЕсли;
КонецПроцедуры
Как совершенно справедливо отметили участники обсуждения на форуме, блокировка элемента управления на форме защищает систему только от «случайного» изменения данных невнимательным пользователем. Она не создает реального барьера для подготовленного человека или для штатных механизмов платформы.
Выделим основные способы, которыми пользователи могут изменить заблокированный реквизит в обход формы:
Следовательно, для критически важных реквизитов визуальной блокировки на форме категорически недостаточно. Нам требуются серверные методы защиты.
Если мы работаем в типовой конфигурации 1С (такой как 1С:Бухгалтерия предприятия 3.0, 1С:ERP или 1С:Комплексная автоматизация), в нашем распоряжении есть мощный встроенный инструмент — подсистема Библиотеки стандартных подсистем (БСП) «Запрет редактирования реквизитов объектов».
Этот механизм предоставляет пользователям удобный интерфейс: ключевые реквизиты формы изначально заблокированы, но в меню «Еще» присутствует кнопка «Разрешить редактирование реквизитов». Доступ к этой кнопке регулируется ролями доступа (например, роль РедактированиеРеквизитовОбъектов).
Важнейшим преимуществом интеграции с БСП является то, что стандартные обработки группового изменения реквизитов учитывают эти блокировки. Если реквизит заблокирован через этот механизм, система не позволит изменить его через команду «Изменить выделенные» без наличия соответствующих прав.
Чтобы задействовать этот механизм для нашего документа, необходимо выполнить следующие действия в конфигураторе:
ОбъектСКонтролируемымиРеквизитами.ПриОпределенииНастроекБлокировкиРеквизитов, в которой укажем, какие именно реквизиты подлежат защите:
// В модуле менеджера документа
Процедура ПриОпределенииНастроекБлокировкиРеквизитов(Настройки) Экспорт
Реквизиты = Новый Массив;
Реквизиты.Добавить("ВидОплаты");
Реквизиты.Добавить("Контрагент");
Настройки.Вставить("ИмяРеквизита", Реквизиты);
КонецПроцедуры
После этого система автоматически заблокирует указанные поля на управляемой форме при условии, что документ уже сохранен в базе данных.
Если нам нужна абсолютная гарантия того, что реквизит не будет изменен ни при каких обстоятельствах (включая сторонний программный код, загрузку по XML, OData или групповые обработки), проверку необходимо реализовать на самом низком уровне — в транзакции записи базы данных.
Для этого мы переопределим обработчик события ПередЗаписью в модуле объекта нашего документа. Алгоритм проверки выглядит следующим образом:
ОбменДанными.Загрузка. Если идет загрузка данных из другой базы, проверку можно пропустить или, наоборот, сделать более строгой.Важное правило оптимизации: Чтобы получить старое значение реквизита из базы данных, никогда не используйте конструкцию ЭтотОбъект.Ссылка.ИмяРеквизита. Такое обращение является неявным запросом, который считывает из СУБД всю таблицу объекта целиком, что приводит к серьезным задержкам производительности и неоправданному расходу оперативной памяти сервера. Вместо этого мы напишем быстрый точечный запрос.
Проанализируем пример кода в модуле объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Если идет обмен данными, пропускаем стандартные проверки (при необходимости)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Для новых документов контроль изменения не требуется
Если ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// Запросом получаем только текущее значение реквизита "ВидОплаты" из БД
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОплатаПокупателя.ВидОплаты КАК ВидОплатыОригинал
|ИЗ
| Документ.ОплатаПокупателя КАК ОплатаПокупателя
|ГДЕ
| ОплатаПокупателя.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
// Если значение в памяти отличается от значения в базе данных
Если ЭтотОбъект.ВидОплаты <> Выборка.ВидОплатыОригинал Тогда
// Проверяем права пользователя. Разрешаем изменение только Администраторам.
Если Не РольДоступна("ПолныеПрава") И Не РольДоступна("Администрирование") Тогда
Отказ = Истина; // Отменяем транзакцию записи объекта в базу данных
// Выводим информативное сообщение пользователю
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Вы не имеете права изменять реквизит ""Вид оплаты"" в записанном документе!";
Сообщение.Поле = "ВидОплаты";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Данный подход является наиболее надежным стандартом разработки. Даже если пользователь найдет способ обойти форму, СУБД просто отклонит транзакцию записи и вернет ошибку.
Платформа 1С:Предприятие 8 позволяет настраивать права доступа не только к метаданным в целом (например, ко всему документу), но и детально разграничивать права на чтение и изменение отдельных реквизитов (полей) объекта.
Разберем, как настроить такое ограничение в конфигураторе:
ДобавлениеИзменениеДокументовОплаты).Документ.ОплатаПокупателя в списке прав роли.ВидОплаты.При использовании этого метода платформа берет на себя всю работу по защите данных. Если у пользователя нет роли с правом редактирования этого конкретного реквизита, он физически не сможет перезаписать его значение — ни вручную, ни через групповые обработки, ни программным кодом, запущенным под его сессией.
Важное предупреждение: Использование ограничений на уровне полей требует высокой квалификации разработчика. Если вы случайно закроете права на Чтение критически важного реквизита, стандартные отчеты и запросы в системе могут начать аварийно завершаться с ошибками нарушения прав доступа, если в них не используется конструкция РАЗРЕШЕННЫЕ.
Поскольку типовые конфигурации (включая 1С:Бухгалтерию предприятия) регулярно обновляются фирмой «1С», вносить изменения напрямую в код документов поставщика крайне нежелательно. Любое изменение усложнит последующее обновление программы.
Рассмотрим, как красиво решить задачу блокировки с помощью механизма Расширений конфигурации:
ОплатаПокупателя) и его реквизит ВидОплаты.ПередЗаписью в модуле объекта с аннотацией &Перед:
&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, изменен ли реквизит, используя логику из Способа 3
Если Не ЭтоНовый() И Не ОбменДанными.Загрузка Тогда
// Получаем старое значение из базы данных с помощью запроса
Запрос = Новый Запрос("ВЫБРАТЬ ВидОплаты ИЗ Документ.ОплатаПокупателя ГДЕ Ссылка = &Ссылка");
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Если ЭтотОбъект.ВидОплаты <> Выборка.ВидОплаты Тогда
Если Не РольДоступна("ПолныеПрава") Тогда
Отказ = Истина;
ВызватьИсключение "Редактирование поля ""Вид оплаты"" запрещено политикой безопасности!";
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Благодаря этому решению весь кастомный код будет изолирован внутри расширения. Типовая конфигурация останется полностью на поддержке, а обновления будут проходить в обычном автоматическом режиме.
Как отмечалось в сообщении 2 на форуме, иногда попытка решить чисто административные проблемы программным путем приводит к лишней бюрократии: разработчикам приходится постоянно открывать доступ по разовым заявкам. Если жесткий запрет нецелесообразен, отличной альтернативой является использование платформенного механизма История данных.
Этот механизм позволяет фиксировать любые изменения полей:
Чтобы задействовать этот механизм, достаточно открыть свойства нужного документа в конфигураторе и для свойства ИсторияДанных установить значение Использовать. После этого вы всегда сможете открыть историю версий объекта прямо из пользовательского режима, увидеть нарушителя и провести административную работу (лишить премии или вынести предупреждение), не усложняя программный код системы.
Для наглядности объединим все рассмотренные методы в общую таблицу рекомендаций:
| Метод защиты | Уровень надежности | Сложность реализации | Когда применять |
|---|---|---|---|
| ТолькоПросмотр / Доступность на форме | Низкий (обходится через групповую обработку) | Очень низкая | Для защиты от случайных ошибок ввода пользователем на форме. |
| Механизм блокировки реквизитов БСП | Средний (защищает форму и штатное групповое изменение) | Средняя | Рекомендуемый стандарт для типовых конфигураций 1С (БП, ERP, УТ). |
| Проверка ПередЗаписью в модуле объекта | Абсолютный (защищает на уровне СУБД) | Средняя | Для критически важных полей, изменение которых нарушит учет или обмены. |
| Ограничение прав доступа в Ролях (FLS) | Абсолютный (на уровне платформы) | Высокая (требует тщательного тестирования) | Для жесткого разделения прав между группами пользователей. |
| Платформенная История Данных | Информационный контроль | Очень низкая | Когда важен аудит изменений, а не жесткие технические запреты. |
Выбирайте подходящий инструмент исходя из бизнес-требований вашей задачи. Оптимальным решением для 1С:Бухгалтерии является совместное использование свойств формы (для удобства работы интерфейса) и обработчика ПередЗаписью в расширении конфигурации (для гарантированной безопасности ваших данных).