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