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