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