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