Как защитить объект 1С от программного изменения при открытой управляемой форме?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Приветствуем вас, уважаемые коллеги! Сегодня мы детально разберем крайне интересную и частую задачу, с которой сталкиваются разработчики при проектировании интерфейсов в «1С:Предприятие 8». Речь пойдет о блокировке данных формы и защите объектов от параллельного программного изменения сторонним кодом.

Представим стандартную ситуацию: пользователь открыл управляемую форму элемента справочника или документа и начал редактирование. В этот же момент запускается регламентное задание или внешняя обработка, которая программно считывает этот же объект, меняет его реквизиты и выполняет метод Записать(). По умолчанию платформа позволяет выполнить такую запись, что приводит к затиранию данных или возникновению конфликтов блокировок. Сегодня мы разберем, почему так происходит, и как реализовать надежный механизм защиты.

Суть проблемы: почему интерактивная пессимистическая блокировка «пропускает» программную запись?

Для начала проанализируем, как работает стандартный метод платформы ЗаблокироватьДанныеФормыДляРедактирования(). Этот метод устанавливает пессимистическую блокировку на уровне интерфейса (через сервис блокировок кластера 1С). Его главная задача — запретить другим пользователям интерактивно открыть эту же форму для редактирования. Если второй пользователь попытается открыть элемент, платформа переведет его форму в режим «Только просмотр».

Однако на программный код, исполняемый на сервере, эта блокировка по умолчанию не распространяется. Если сторонняя обработка выполняет код:


ОбъектСсылка = Справочники.Номенклатура.НайтиПоКоду("00001");
Объект = ОбъектСсылка.ПолучитьОбъект();
Объект.Наименование = "Новое наименование";
Объект.Записать();

то эта запись успешно выполнится. Платформа при программном вызове метода Записать() не проверяет наличие интерактивных пессимистических блокировок на этот объект. В результате пользователь, у которого открыта форма, столкнется с необходимостью перечитывать данные, а его изменения могут быть потеряны.

Пошаговое решение: реализуем жесткую блокировку от программного изменения

Рассмотрим изящное решение, к которому пришли участники обсуждения на форуме. Идея заключается в том, чтобы связать событие записи на сервере с флагом в коллекции ДополнительныеСвойства объекта и проверять блокировку непосредственно в модуле объекта перед записью.

Разберем по шагам, как настроить этот механизм в вашей конфигурации.

Шаг 1. Настройка модуля управляемой формы

В модуле формы элемента (например, справочника) нам необходимо решить две задачи: заблокировать форму при открытии и пометить, что запись производится интерактивно (именно из этой формы). Для этого используем процедуры ПриСозданииНаСервере и ПередЗаписьюНаСервере.


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Пытаемся заблокировать данные формы для редактирования. 
    // Если объект уже заблокирован другим сеансом, переводим форму в режим просмотра.
    Попытка
        ЗаблокироватьДанныеФормыДляРедактирования();
    Исключение
        ТолькоПросмотр = Истина;
    КонецПопытки;
    
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Помещаем в ДополнительныеСвойства признак того, что запись инициирована из этой формы
    ТекущийОбъект.ДополнительныеСвойства.Вставить("РазрешитьЗаписьИзФормы", Истина);
    
КонецПроцедуры

Шаг 2. Реализация проверки в модуле объекта

Теперь перейдем в модуль объекта (справочника или документа). В обработчике события ПередЗаписью мы должны перехватить любую попытку записи. Если запись идет программно (флаг РазрешитьЗаписьИзФормы отсутствует), мы принудительно проверяем, заблокирован ли объект в данный момент формой.


Процедура ПередЗаписью(Отказ)
    
    // Обязательно проверяем режим загрузки данных (для обменов и РИБ)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Проверяем наличие флага интерактивной записи
    Если НЕ ДополнительныеСвойства.Свойство("РазрешитьЗаписьИзФормы") Тогда
        
        // Если запись идет программно, проверяем, не заблокирован ли объект интерактивно.
        // Для этого пытаемся вызвать метод Заблокировать() для текущей ссылки.
        Если ЗначениеЗаполнено(Ссылка) Тогда
            Попытка
                // Если форма открыта у другого пользователя, этот метод вызовет исключение,
                // так как платформа не позволит установить вторую пессимистическую блокировку.
                Заблокировать();
            Исключение
                Отказ = Истина;
                ВызватьИсключение("Не удалось записать объект! Данный элемент в настоящий момент редактируется пользователем в форме.");
            КонецПопытки;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Как это работает на практике?

Проанализируем логику совместной работы этих двух обработчиков:

  1. Пользователь открывает форму элемента. Вызывается ЗаблокироватьДанныеФормыДляРедактирования(). На сервере 1С регистрируется пессимистическая блокировка этого объекта для данного сеанса.
  2. Если пользователь нажимает «Записать» в форме, срабатывает событие ПередЗаписьюНаСервере, в объект передается свойство РазрешитьЗаписьИзФормы = Истина. Процедура модуля объекта ПередЗаписью видит это свойство и пропускает запись без дополнительных проверок.
  3. Если сторонняя обработка пытается записать объект программно, свойство РазрешитьЗаписьИзФормы отсутствует. Код переходит к блоку проверки и вызывает метод Заблокировать().
  4. Поскольку на объект уже наложена интерактивная блокировка первым пользователем, метод Заблокировать() в сторонней обработке вызывает исключение. Программная запись прерывается, данные в базе остаются в безопасности, а пользователь в форме продолжает комфортную работу.

Важные риски и архитектурные нюансы использования метода

Хотя предложенное решение является рабочим и эффективно решает поставленную задачу, при его внедрении в реальные высоконагруженные системы необходимо учитывать несколько критических факторов:

1. Влияние на планы обмена и распределенные базы данных (РИБ)

При обмене данными объекты записываются программно. Если в момент синхронизации у какого-то пользователя будет открыта форма изменяемого элемента, то без должной проверки обмен завершится ошибкой. Именно поэтому в процедуре ПередЗаписью критически важно первым делом проверять свойство ОбменДанными.Загрузка. Если оно равно Истина, любые блокировки должны игнорироваться, так как системный обмен имеет приоритет перед действиями пользователей.

2. Проблема регламентных и фоновых заданий

Системные процессы (например, групповое перепроведение документов, регламентное закрытие месяца, расчет себестоимости) часто выполняются в фоновом режиме. Если фоновое задание споткнется о блокировку из-за того, что один из пользователей ушел на обед, оставив форму открытой, это может парализовать работу всей компании. Рекомендуем тщательно взвесить целесообразность установки таких жестких блокировок на критически важные справочники и документы.

3. Поведение платформы при зависших сессиях

Если у пользователя аварийно завершится сеанс (например, пропадет интернет или отключится компьютер), интерактивная блокировка может удерживаться кластером серверов 1С еще некоторое время (до истечения таймаута или удаления "спящего" сеанса). В этот период объект будет полностью заблокирован для любых программных изменений.

Оптимистическая блокировка — встроенная альтернатива от 1С

Не забывайте, что в платформе «1С:Предприятие 8» по умолчанию функционирует механизм оптимистической блокировки, работающий на основе версионирования данных (свойство ВерсияДанных). Даже без написания дополнительного кода, если сторонняя обработка успеет изменить элемент справочника, пока у пользователя открыта форма:

  1. Обработка запишет новые данные, изменив версию объекта в базе данных.
  2. При попытке пользователя нажать «Записать» в форме, платформа сравнит исходную версию (считанную при открытии) с текущей в БД.
  3. Обнаружив несовпадение версий, система выдаст ошибку: «Данные были изменены или удалены другим пользователем», предотвращая затирание сторонних изменений.

Таким образом, предложенное решение с ручной пессимистической блокировкой в модуле объекта стоит применять только тогда, когда вам действительно необходимо гарантировать абсолютную неизменяемость данных в процессе интерактивного редактирования и вы готовы к потенциальному усложнению логики работы фоновых процессов.

← На главную