В процессе разработки в среде «1С:Предприятие 8» часто возникает классическая задача: необходимо организовать хранение информации в регистре сведений таким образом, чтобы все записи создавались исключительно программно (например, при записи элемента определенного справочника). При этом пользователи ни в коем случае не должны иметь возможности зайти напрямую в форму списка этого регистра и вручную добавить, отредактировать или удалить строки.
В этой статье мы подробно разберем, как правильно реализовать подобное ограничение. Мы проанализируем различные подходы — от настройки прав доступа на уровне метаданных до манипуляций с управляемыми формами и программного контроля транзакций.
Это наиболее надежный, «чистый» и рекомендуемый платформой способ решения задачи. Он базируется на механизме ролей и не требует написания сложного кода для блокировки интерфейса.
Выясним, как это работает на практике. В конфигураторе 1С для нужного регистра сведений в настройках ролей пользователей выполним следующие действия:
Если убрать право Интерактивное добавление, кнопка «Создать» автоматически исчезнет из формы списка и формы записи регистра для этого пользователя. При этом программная запись под этой ролью все еще будет доступна, если она вызывается стандартным образом.
Если же мы полностью забираем у пользователя права на изменение регистра, то при попытке записать в него данные из справочника система выдаст ошибку нарушения прав доступа. Чтобы обойти это ограничение, программную запись из модуля справочника необходимо выполнять в привилегированном режиме.
Рассмотрим пример кода, который вызывается в модуле справочника для безопасной программной записи в регистр под управлением привилегированного модуля:
// Этот код должен располагаться в общем модуле с установленными свойствами "Сервер" и "Привилегированный"
Процедура ЗаписатьДанныеВРегистрПривилегированно(ПараметрыЗаписи) Экспорт
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Владелец.Установить(ПараметрыЗаписи.Владелец);
НоваяСтрока = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ПараметрыЗаписи);
// Запись происходит в привилегированном режиме, проверки прав игнорируются
НаборЗаписей.Записать();
КонецПроцедуры
Если по каким-то причинам изменять права доступа в ролях нежелательно, мы можем ограничить действия пользователя непосредственно на форме списка регистра сведений. Рассмотрим, какие настройки формы помогут нам решить задачу без программирования.
Откроем форму списка регистра сведений в конфигураторе и настроим следующие свойства:
ТолькоПросмотр в значение Истина для всей формы списка. Это переведет форму в режим чтения, исключая любое интерактивное редактирование данных.Важно понимать, что данные ограничения работают только на уровне конкретной формы. Если пользователь сможет открыть запись регистра через другую форму или внешнюю обработку, эти запреты не сработают. Поэтому интерфейсные методы рекомендуется совмещать с программными.
Проанализируем ситуацию: как сделать так, чтобы код в модуле регистра сведений сам понимал, кто пытается сделать запись — пользователь вручную или система программно из модуля справочника?
Для реализации этого сценария мы можем использовать встроенный механизм платформы — коллекцию ДополнительныеСвойства, которая присутствует у всех наборов записей.
Разберем этот алгоритм по шагам:
Шаг 1. В модуле справочника (например, в обработчике ПередЗаписью справочника) при формировании набора записей регистра мы принудительно внедряем специальный маркер программной записи:
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.СправочникСсылка.Установить(ЭтотОбъект.Ссылка);
// Добавляем маркер, сигнализирующий о том, что запись инициирована кодом справочника
НаборЗаписей.ДополнительныеСвойства.Вставить("ПрограммнаяЗапись", Истина);
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.СправочникСсылка = ЭтотОбъект.Ссылка;
НоваяЗапись.РеквизитДанных = ЭтотОбъект.РеквизитДанных;
НаборЗаписей.Записать();
Шаг 2. В модуле набора записей самого регистра сведений мы перехватываем событие записи и проверяем наличие нашего секретного маркера:
Процедура ПередЗаписью(Отказ, Замещение)
// Пропускаем запись, если она выполняется в процессе обмена данными
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Проверяем наличие и значение нашего признака
ПрограммныйВвод = Ложь;
Если ДополнительныеСвойства.Свойство("ПрограммнаяЗапись") Тогда
ПрограммныйВвод = ДополнительныеСвойства.ПрограммнаяЗапись;
КонецЕсли;
// Если запись инициирована не из справочника (вручную пользователем) — отменяем транзакцию
Если Не ПрограммныйВвод Тогда
Отказ = Истина;
ВызватьИсключение "Непосредственное изменение записей этого регистра запрещено. Запись возможна только из карточки справочника!";
КонецЕсли;
КонецПроцедуры
Данный подход гарантирует 100% защиту данных на уровне СУБД. Даже если пользователь найдет лазейку в интерфейсе или попытается применить стороннюю обработку для изменения регистра, система заблокирует транзакцию и вызовет исключение.
Если вы работаете в типовой конфигурации, находящейся на поддержке, напрямую менять код модуля регистра сведений крайне нежелательно. Чтобы не снимать объект с поддержки, воспользуемся Подпиской на событие.
Создадим новую подписку на событие со следующими параметрами:
РегистрСведенийНаборЗаписей.ИмяВашегоРегистраПередЗаписьюКод обработчика подписки будет практически идентичен коду из третьего метода:
Процедура ПередЗаписьюРегистраСведенийПередЗаписью(Источник, Отказ, Замещение) Экспорт
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
ПрограммныйВвод = Ложь;
Если Источник.ДополнительныеСвойства.Свойство("ПрограммнаяЗапись") Тогда
ПрограммныйВвод = Источник.ДополнительныеСвойства.ПрограммнаяЗапись;
КонецЕсли;
Если Не ПрограммныйВвод Тогда
Отказ = Истина;
Сообщить("Интерактивное изменение регистра запрещено конфигурацией!");
КонецЕсли;
КонецПроцедуры
В процессе обсуждения данной задачи часто возникает вопрос: «А как быть с удалением записей? Сможет ли пользователь удалить их вручную кнопкой Delete?».
Необходимо четко понимать важное отличие регистров от справочников или документов. Записи в регистрах сведений не помечаются на удаление — они удаляются физически и безвозвратно. При этом любое интерактивное удаление строки пользователем в форме списка технически является операцией записи пустого набора записей или записью набора с уменьшенным количеством строк.
Следовательно, описанный нами в Методе 3 и Методе 4 механизм контроля через событие ПередЗаписью автоматически защитит регистр и от ручного удаления строк. Поскольку при нажатии клавиши Delete пользователь инициирует интерактивную запись, в коллекции ДополнительныеСвойства не окажется флага ПрограммнаяЗапись, и система заблокирует операцию.
Если же требуется удалить запись регистра автоматически при удалении самого элемента справочника, мы должны прописать аналогичное программное удаление с передачей признака в обработчике ПередУдалением нашего справочника:
Процедура ПередУдалением(Отказ)
// Очищаем записи регистра при удалении элемента справочника
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.СправочникСсылка.Установить(ЭтотОбъект.Ссылка);
// Передаем признак программной записи для прохождения проверки контроля
НаборЗаписей.ДополнительныеСвойства.Вставить("ПрограммнаяЗапись", Истина);
// Записываем пустой набор, что фактически удаляет данные из БД
НаборЗаписей.Записать();
КонецПроцедуры
Мы рассмотрели все основные способы защиты регистра сведений от ручного вмешательства. Выбор конкретного метода зависит от архитектуры вашей системы: