Как проверить, что документ редактируется другим пользователем, и избежать ошибки несоответствия версий при пакетной обработке?

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

При реализации групповой обработки документов в форме списка часто возникает задача: необходимо проверить, не открыт ли документ другим пользователем для редактирования. Если пропустить этот момент, при вызове метода записи может возникнуть исключение: «Операция не может быть выполнена из-за несоответствия версии или отсутствия записи базы данных (возможно, запись была изменена или удалена)».

Разберем причины возникновения этой ошибки и выясним, какими программными методами можно проверить занятость объекта перед внесением изменений.

Почему возникает ошибка несоответствия версий

В платформе 1С:Предприятие по умолчанию действует механизм оптимистической блокировки. Рассмотрим принцип его работы:

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

Почему метод Заблокирован() не подходит для проверки

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

Метод Заблокирован() проверяет наличие блокировки объекта только текущим экземпляром объекта в текущем программном контексте. Он не сообщает, заблокирован ли объект базы данных кем-то другим в соседнем сеансе. Если другой пользователь открыл форму документа и изменил реквизит, вызов Объект.Заблокирован() в вашей обработке вернет значение Ложь.

Решение 1: Использование метода ЗаблокироватьДанныеДляРедактирования

Для явного контроля редактирования объектов в платформе 1С предусмотрен механизм пессимистической блокировки. Когда пользователь в интерактивном режиме начинает изменять данные в открытой форме (свойство формы Модифицированность становится равным Истина), форма автоматически устанавливает пессимистическую блокировку объекта.

Чтобы проверить занятость документа в пакетной обработке, необходимо попытаться наложить пессимистическую блокировку в блоке Попытка ... Исключение с помощью глобального метода ЗаблокироватьДанныеДляРедактирования() (или метода Заблокировать() у объекта).

Разберем по шагам реализацию пакетной обработки документов:


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

Нюансы работы с пессимистической блокировкой

Рассмотрим важные особенности поведения платформы:

  1. Документ открыт без изменений: если пользователь просто открыл форму документа для просмотра и не правил реквизиты (Модифицированность = Ложь), платформа не устанавливает пессимистическую блокировку. В таком случае метод ЗаблокироватьДанныеДляРедактирования() выполнится успешно. Однако при последующей попытке пользователя сохранить изменения в этой открытой форме ошибка несоответствия версий возникнет уже у него.
  2. Оповещение интерфейса: после успешного пакетного изменения объектов рекомендуется на клиенте вызывать метод ОповеститьОбИзменении(Ссылка). Это позволит автоматически обновить открытые динамические списки и формы документов.
  3. Освобождение блокировок: если вызов ЗаблокироватьДанныеДляРедактирования() происходит внутри транзакции, платформа автоматически снимет все установленные блокировки при фиксации или отмене транзакции. Если же обработка выполняется вне транзакции, каждый заблокированный объект необходимо освобождать с помощью метода РазблокироватьДанныеДляРедактирования().

Решение 2: Обработка исключения при записи объекта

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

Посмотрим на пример реализации:


Для Каждого ТекСсылка Из МассивСсылок Цикл
    Попытка
        ДокОбъект = ТекСсылка.ПолучитьОбъект();
        Если ДокОбъект = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        ДокОбъект.Комментарий = "Обработано";
        ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
    Исключение
        ТекстОшибки = ОписаниеОшибки();
        Если СтрНайти(ТекстОшибки, "несоответствия версии") > 0 Тогда
            Сообщить("Документ " + Строка(ТекСсылка) + " был изменен другим процессом. Запись отменена.");
        Иначе
            Сообщить("Ошибка при обработке документа " + Строка(ТекСсылка) + ": " + ТекстОшибки);
        КонецЕсли;
    КонецПопытки;
КонецЦикла;
← На главную