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