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