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